西瓜书习题试答-第13章-半监督学习

试答系列:“西瓜书”-周志华《机器学习》习题试答
系列目录
[第01章:绪论]
[第02章:模型评估与选择]
[第03章:线性模型]
[第04章:决策树]
[第05章:神经网络]
[第06章:支持向量机]
第07章:贝叶斯分类器
第08章:集成学习
第09章:聚类
第10章:降维与度量学习
第11章:特征选择与稀疏学习
第12章:计算学习理论(暂缺)
第13章:半监督学习
第14章:概率图模型
(后续章节更新中...)


目录


13.1 试推导出式(13.5)~(13.8).

:首先,我们进行一些约定和分析:
约定和分析

  1. 原教材中用\(y\)代表类别标记,用\(\Theta\)表示(样本)所隶属的高斯成分,同时假定第\(i\)个类别对应于第\(i\)个高斯成分。这里我们将两者视为同一个东西,统一用\(y\)来表示,不再区分。
  2. 将(13.1)式改写为

\[\begin{aligned} p(x)&=\sum_i p(y=i)p(x|y=i)\\ &=\sum_i \alpha_i \Bbb{N}(x|\mu_i,\Sigma_i) \end{aligned}\]

  1. 目标函数\(LL\)中,对于有标记样本项为似然项,对于无标记样本项则为边际似然项,原(13.4)实际上可以表达为:

\[\begin{aligned} LL&=\sum_{(x_j,y_j)\in D_l}\ln p(x_j,y_j)+\sum_{x_j\in D_u}\ln p(x_j)\\ &=\sum_{(x_j,y_j)\in D_l}\ln[\alpha_{y_j}\Bbb{N}(x_j|\mu_{y_j},\Sigma_{y_j})] +\sum_{x_j\in D_u}\ln\sum_i\alpha_i\Bbb{N}(x_j|\mu_i,\Sigma_i)\\ \end{aligned}\]

其中对于无标记样本的求和项中存在\(\ln[\sum(\cdot)]\)的形式,若直接求取\(\max_{\{\alpha,\mu,\Sigma\}}LL\)会比较困难,通常采用\(EM\)算法来求取。
EM算法求解:

  • E步:计算未标记样本所属类别标记的后验概率:

\[\begin{aligned} \gamma_{ji}&=p(y=i|x_j)\\ &=\frac{p(y=i)p(x_j|y=i)}{p(x_j)}\\ &=\frac{\alpha_i\Bbb{N}(x_j|\mu_i,\Sigma_i)}{\sum_k\alpha_k\Bbb{N}(x_j|\mu_k,\Sigma_k)} \end{aligned}\]

该结果与 (13.5) 式等同(尽管看起来不一样)。

  • M步:计算目标函数LL的期望,并最大化:
    1.计算\(LL\)的期望。

\[\begin{aligned} E(LL)&=\sum_{(x_j,y_j)\in D_l}\ln p(x_j,y_j)+\sum_{x_j\in D_u}\sum_i\gamma_{ji}\ln p(x_j,y_j=i)\\ &=\sum_{(x_j,y_j)\in D_l}\ln[\alpha_{y_j}\Bbb{N}(x_j|\mu_{y_j},\Sigma_{y_j})] +\sum_{x_j\in D_u}\sum_i\gamma_{ji}\ln[\alpha_i\Bbb{N}(x_j|\mu_i,\Sigma_i)]\\ \end{aligned} \]

2.最大化\(E(LL)\)
首先介绍一下高斯函数关于均值和协方差的梯度结果:

\[\begin{aligned} &\Bbb{N}(x_j|\mu_i,\Sigma_i)=\frac{1}{(2\pi)^{n/2}|\Sigma_i|^{1/2}}exp[-\frac{1}{2}(x_j-\mu_i)^T\Sigma_i^{-1}(x_j-\mu_i)]\\ &\nabla_{\mu_i}\ln\Bbb{N}(x_j|\mu_i,\Sigma_i)=\Sigma_i^{-1}(x_j-\mu_i)\\ &\nabla_{\Sigma_i^{-1}}\ln\Bbb{N}(x_j|\mu_i,\Sigma_i)=\frac{1}{2}[\Sigma_i-(x_j-\mu_i)(x_j-\mu_i)^T] \end{aligned}\]

最后一式应用了关系:\(\nabla_A|A|=|A|(A^{-1})^T\)
下面正式最大化似然函数的期望:\(\max_{\{\alpha,\mu,\Sigma\}}E(LL)\)

\[\begin{aligned} &\bf\nabla_{\mu_i}E(LL)\\ &=\sum_{(x_j,y_j)\in D_l}I(y_j=i)\Sigma_{i}^{-1}(x_j-\mu_i)+\sum_{x_j\in D_u}\gamma_{ji}\Sigma_{i}^{-1}(x_j-\mu_i)]\\ &=0\\ &\Rightarrow \mu_i=\frac{1}{l_i+\sum_{D_u}\gamma_{ji}}[\sum_{(x_j,y_j)\in D_l}I(y_j=i)x_j+\sum_{x_j\in D_u}\gamma_{ji}\,x_j] \end{aligned}\]

此即 (13.6) 式,其中\(I(\cdot)\)为指示函数。

\[\begin{aligned} &\bf\nabla_{\Sigma_i^{-1}}E(LL)\\ &=\sum_{(x_j,y_j)\in D_l}I(y_j=i)\frac{1}{2}[\Sigma_i-(x_j-\mu_i)(x_j-\mu_i)^T]\\ &\quad+\sum_{x_j\in D_u}\gamma_{ji}\frac{1}{2}[\Sigma_i-(x_j-\mu_i)(x_j-\mu_i)^T\\ &=0\\ &\Rightarrow \Sigma_i=\frac{1}{l_i+\sum_{D_u}\gamma_{ji}}[\sum_{(x_j,y_j)\in D_l}I(y_j=i)(x_j-\mu_i)(x_j-\mu_i)^T\\ &\qquad\qquad+\sum_{x_j\in D_u}\gamma_{ji}\,(x_j-\mu_i)(x_j-\mu_i)^T] \end{aligned}\]

此即 (13.7) 式。
对于\(\{\alpha_i\}\),还要考虑约束\(\sum_i \alpha_i=1\),利用拉格朗日方法来求解该约束问题:

\[\max_{\{\alpha_i\}}L=\max_{\{\alpha_i\}}[E(LL)+\lambda(\sum_i\alpha_i-1)]$$$$\begin{aligned} &\bf\frac{\partial{L}}{\partial{\alpha_i}}\\ &=\sum_{(x_j,y_j)\in D_l}I(y_j=i)\frac{1}{\alpha_i}+\sum_{x_j\in D_u}\gamma_{ji}\frac{1}{\alpha_i}+\lambda\\ &=0\\ &\Rightarrow \alpha_i=-\frac{l_i+\sum_{D_u}\gamma_{ji}}{\lambda} \end{aligned}\]

由约束\(\sum_i \alpha_i=1\)可得\(\lambda=-m\),所以:

\[\alpha_i=\frac{l_i+\sum_{D_u}\gamma_{ji}}{m} \]

此即 (13.8) 式。

13.2 试基于朴素贝叶斯模型推导出生成式半监督学习算法.

:完全与基于混合高斯模型的生成式算法类似,下面试推导一番。
这里同样假设样本类别数等于朴素贝叶斯模型的类别数,而且第i个样本类别对应于第i个模型类别,同样不再区分\(y\)\(\Theta\)
另外,假设样本中每个特征取值为离散值。

  • 朴素贝叶斯模型

\[\begin{aligned} p(\boldsymbol{x})&=\sum_{i=1}^N p(y=i)p(\boldsymbol{x}|y=i)\\ &=\sum_{i=1}^N p(y=i)\prod_{f=1}^n p(x_f|y=i)\\ &=\sum_{i=1}^N \alpha_i \prod_{f=1}^n \beta_{i,f,x_j} \end{aligned}\]

其中\(i\)为类别索引号,共有N个类别,\(f\)为特征索引号,共有n个不同特征;\(\alpha_i=p(y=i)\)\(\beta_{i,f,k}=p(x_f=k|y=i)\),满足约束\(\sum_i\alpha_i=1,\sum_k\beta_{ifk}=1\)

  • 似然函数

\[\begin{aligned} LL&=\sum_{(\boldsymbol{x}_j,y_j)\in D_l}\ln p(\boldsymbol{x}_j,y_j)+\sum_{\boldsymbol{x}_j\in D_u}\ln p(\boldsymbol{x}_j)\\ &=\sum_{(\boldsymbol{x}_j,y_j)\in D_l}\ln\left[\alpha_{y_j}\prod_{f}\beta_{y_j,f,x_{jf}}\right] +\sum_{\boldsymbol{x}_j\in D_u}\ln\left[ \sum_i\alpha_i\prod_{f}\beta_{i,f,x_{jf}}\right]\\ \end{aligned}\]

其中\(x_{jf}\)表示样本\(\boldsymbol{x}_j\)的第\(f\)个特征取值。

  • EM算法求解参数
    ---E步---
    对于无标记样本:

\[\begin{aligned} \gamma_{ji}&=p(y=i|x_j)\\ &=\frac{\alpha_i\prod_f\beta_{i,f,x_{jf}}}{\sum_i\alpha_i\prod_f\beta_{i,f,x_{jf}}} \end{aligned}\]

似然函数的期望:

\[E(LL)=\sum_{(\boldsymbol{x}_j,y_j)\in D_l}\ln\left[\alpha_{y_j}\prod_{f}\beta_{y_j,f,x_{jf}}\right] +\sum_{\boldsymbol{x}_j\in D_u}\sum_i\gamma_{ji}\ln\left[ \alpha_i\prod_{f}\beta_{i,f,x_{jf}}\right]\]

---M步---
极大化期望似然函数,得到:

\[\begin{aligned} \alpha_i&=\frac{l_i+\sum_{D_u}\gamma_{ji}}{m}\\ \beta_{i,f,k}&=\frac{l_{i,f,k}+\sum_{D_u}\gamma_{ji}1(x_{jf=k})}{l_i+\sum_{D_u}\gamma_{ji}} \end{aligned}\]

其中\(l_i\)表示标记样本中属于第i类的数目,\(l_{i,f,k}\)表示标记样本中属于第\(i\)类,而且第\(f\)个特征取值为\(k\)的样本数目。

13.3 假设数据由混合专家(mixture of experts)模型生成,即数据是基于k个成分混合而得的概率密度生成:

\[p(x|\theta)=\sum_{i=1}^k\alpha_i \,p(x_ |\theta_i)\tag{13.22} \]

其中\(\theta=\{\theta_1,\theta_2,\cdots,\theta_k\}\)是模型参数\(p(x|\theta_i)\)是第\(i\)个混合成分的概率密度,混合系数\(\alpha_i\geq0,\sum_i^k\alpha_i=1\).假定每个混合成分对应一个类别,但每个类别可包含多个混合成分.试推导相应的生成式半监督学习算法。

1. 一些认识
对于生成式方法的半监督学习,试借用贝叶斯网中的表示方法来表达各变量间的依赖关系,用以帮助理解:
在这里插入图片描述
根据上图可写出联合概率密度:

\[p(\Theta=i,y,x)=\alpha_i\,p(y|\Theta=i)\,p(x|\theta_i) \]

其中\(p(y|\Theta=i)\)是一个概率表,对于教材正文中的高斯混合模型,假定第\(i\)个类别对应于第\(i\)个高斯混合成分,那么,概率表将为一个单位矩阵,形如:

\(p(y|\Theta)\) y=1 y=2
\(\Theta=1\) 1 0
\(\Theta=2\) 0 1
对于本题中的“每个混合成分对应一个类别,但每个类别可包含多个混合成分”,此时的概率表将为形如:
\(p(y|\Theta)\) y=1 y=2
-- -- --
\(\Theta=1\) 1 0
\(\Theta=2\) 1 0
\(\Theta=3\) 0 1
\(\Theta=4\) 0 1

按论文【Miller and Uyar, 1997】中的说法,以上情况下的概率表被称之为“硬划分(hard-partition)”,亦即是说,\(p(y|\Theta)\,,y=1,2,\cdots\)中只有一个取值为1,其余取值为零。
我们可以将这个条件概率表达为:

\[p(y|\Theta=i)=1(i\in C_y) \]

其中\(C_y\)表示类别\(y\)所包含的混合成分的序号的集合。

2. 对于“每个混合成分对应于一个类别”的情况下,求解参数\(\{\alpha_i, \theta_i\}\)
将这种对应关系视为固定不变,求解参数。如果同时也想学习这种对应关系,可以尝试不同组合方式,取其最佳组合。
与前面混合高斯模型和朴素贝叶斯模型中的方法完全类似,写出似然函数,然后应用EM算法:

\[\begin{aligned} LL&=\sum_{(\boldsymbol{x}_j,y_j)\in D_l}\ln p(\boldsymbol{x}_j,y_j)+\sum_{\boldsymbol{x}_j\in D_u}\ln p(\boldsymbol{x}_j)\\ &=\sum_{(\boldsymbol{x}_j,y_j)\in D_l}\ln\left[\sum_i\alpha_i\cdot1(i\in C_{y_j})p(x_j|\theta_i)\right]\\ &\qquad+\sum_{\boldsymbol{x}_j\in D_u}\ln\left[\sum_i\alpha_i\cdot p(x_j|\theta_i)\right]\\ \end{aligned}\]

注意,由于似然函数的\(D_l\)\(D_u\)项都有\(\ln[\sum(\cdot)]\)的形式,因此,E步需要针对两项都要求取隐变量\(\Theta\)的后验概率:
---E步--

\[\begin{aligned} &For X_u:\quad\gamma_{ji}=p(\Theta=i|x_j)=\frac{\alpha_i p(x_j|\theta_i)}{\sum_i \alpha_i p(x_j|\theta_i)}\\ &For X_l:\quad\gamma_{ji}=p(\Theta=i|x_j,y_j)=\frac{\alpha_i p(x_j|\theta_i)}{\sum_{i\in C_{y_j}} \alpha_i p(x_j|\theta_i)}\cdot 1(i\in C_{y_j}) \end{aligned} \]

---M步--

\[\begin{aligned} &\nabla_{\theta_i}E(LL)=0\\ &\qquad\Rightarrow \theta_i=?\\ &\nabla_{\alpha_i}[E(LL)+\lambda(\sum_i \alpha_i-1)]=0\\ &\qquad\Rightarrow\alpha_i=\frac{1}{m}[\sum_{x_j\in D_u}\gamma_{ji}+\sum_{(x_j,y_j)\in D_l}\gamma_{ji}1(i\in C_{y_i})] \end{aligned}\]

3. 对于“软划分”的情况下,求解参数\(\{\alpha_i, \theta_i\}\)
论文【Miller and Uyar, 1997】中谈到更一般的情况是\(p(y|\Theta=i)\)这个概率表中各个元素不为零的情况,论文中称之为The generalized mixture model (GM模型),将概率表中的参数值表示为\(p(y|\Theta=i)=\beta_{y|i}\),此时,它也将是可学习的参数。
此时,似然函数为:

\[LL=\sum_{(\boldsymbol{x}_j,y_j)\in D_l}\ln\left[\sum_i\alpha_i\cdot\beta_{y_j|i}\cdotp(x_j|\theta_i)\right]+\sum_{\boldsymbol{x}_j\in D_u}\ln\left[\sum_i\alpha_i\cdot p(x_j|\theta_i)\right]\]

在应用EM算法求解参数时,论文中介绍了两者处理方法:
EM-I:对于有标记和无标记样本,隐变量都只是\(\Theta\).
EM-II:对于有标记,隐变量为\(\Theta\),对于无标记样本,隐变量为\(\Theta,y\).
两种方法计算得到的\(\alpha_i,\theta_i\)完全一样,但是\(\beta_{y|i}\)不一样:

\[\begin{aligned} EM-I:&\quad\beta_{y|i}=\frac{\sum_{(x_j,y_j)\in D_l}\gamma_{ji}\cdot 1(y_j=y)}{\sum_{(x_j,y_j)\in D_l}\gamma_{ji}}\\ EM-II:&\quad\beta_{y|i}=\frac{\sum_{(x_j,y_j)\in D_l}\gamma_{ji}\cdot 1(y_j=y)+\sum_{x_j\in D_u}\gamma_{jiy}}{\sum_{(x_j,y_j)\in D_l}\gamma_{ji}+\sum_{x_j\in D_u}\gamma_{ji}} \end{aligned} \]

其中,\(\gamma_{ji}\)的含义与前相同,而\(\gamma_{jiy}=p(\Theta=i,y|x_j)\)

13.4 从网上下载或自己编程实现TSVM算法,选择两个UCI数据集,将其30%的样例用作测试样本,10%的样本用作有标记样本,60%的样本用作无标记样本,分别训练出无标记样本的TSVM以及仅利用有标记样本的SVM,并比较其性能。

:详细编程代码附后。

  1. TSVM算法实现
    TSVM算法基于教材图13.4所示,其中SVM功能基于sklearn.svm.SVC实现。
    在寻找满足条件,需要交换伪标记的样本\(x_i,x_j\)时,分别对伪正例样本和伪负例样本按\(\xi\)值进行排序,取其最大者分别作为待交换样本\(x_i,x_j\),然后考察它们是否满足交换条件。只需要考虑这两个\(\xi\)值最大的样本即可,因为,如果它们都不满足条件的话,那么其余样本对也都无法满足条件。
  2. 运行结果
    首先为了观察TSVM算法的运行效果,人为生成了一些简单数据集,并用动画形式展示了TSVM算法运行过程:
    在这里插入图片描述
    上图中的生成数据集为明显可分线性划分的两簇数据,对于这样的数据集,即使少量的有标记样本即可进行“正确”的划分,第一次指派伪标记结果即为“正确”结果,在整个TSVM算法运行过程中,伪标记不再发生变化,不会进行伪标记交换操作。
    在这里插入图片描述
    上图生成的数据集的两簇数据存在交叠,无法明显线性划分,对于这样的数据集,引入带伪标记的样本后得到的分类器与之前仅有标记样本训练的分类器会有较大差异,将会导致样本伪标记不断交换。

    然后在两个UCI数据集上进行试验,运行结果如下:
    莺尾花数据集
    sklearn.datasets.load_iris()
    仅用有标记样本进行训练模型的预测精度为: 0.71111111111
    利用无标记样本的半监督模型的预测精度为: 0.7333333333
    预测精度提高了3.12%
    手写数字数据集
    sklearn.datasets.load_digits()
    仅用有标记样本进行训练模型的预测精度为: 0.768518518519
    利用无标记样本的半监督模型的预测精度为: 0.77037037037
    预测精度提高了0.24%

13.5 对未标记样本进行标记指派与调整的过程中有可能出现类别不平衡问题,试给出考虑该问题后的改进TSVM算法。

:其实教材正文中有提到:在拟合SVM的时候,对于伪正和伪负的样本采用不同的\(C_u\)权重值,\(C^+_u:C^-_u=u_- : u_+\),这里\(u_+,u_-\)分别表示伪正和伪负的未标记样本数。

13.6 TSVM对未标记样本进行标记指派与调整的过程涉及很大的计算开销,试设计一个高效的改进算法。

:没太理解题意,指派和调整过程没感觉太大的计算开销啊。对于调整过程,可以按题13.4中所述:“在寻找满足条件,需要交换伪标记的样本\(x_i,x_j\)时,分别对伪正例样本和伪负例样本按\(\xi\)值进行排序,取其最大者分别作为待交换样本\(x_i,x_j\),然后考察它们是否满足交换条件。只需要考虑这两个\(\xi\)值最大的样本即可,因为,如果它们都不满足条件的话,那么其余样本对也都无法满足条件。

13.7 试设计一个能对新样本进行分类的图半监督学习方法。

:如教材正文所述:“......接收到新样本时,或是将其加入原数据集对图进行重构并重新进行标记传播,或是需引入额外的预测机制,例如将\(D_l\)和经标记传播后得到标记的\(D_u\)合并作为训练集,另外训练一个学习器例如支持向量机来对新样本进行预测。

对于前一种方法---重新进行标记传播,若新样本较少,对于结果影响应该很小。可以基于前面的分析,计算新样本与其余有标记和无标记样本的“亲和力(比如高斯函数值)”,将该亲和力作为权重来确定新样本的标记:\(\hat{y}_{new}=sign(\sum W_{x_{new}\,\,,x_i}\cdot y_i/\sum W_{x_{new}\,\,,x_i})\)

13.8 自训练是一种比较原始的半监督学习方法:它先在有标记样本上学习,然后用学得分类器对未标记样本进行判别以获得其伪标记,再在有标记与伪标记样本的合集上重新训练,如此反复。试析该方法有何缺陷。

:根据题干的描述,自训练与TSVM算法很类似,都是先在有标记样本上训练,然后指派伪标记,然后重新训练。
不同点在于:TSVM算法中,未标记样本的权重有个从小变大的过程,重新指派标记时每次通过交换标记的方式只调整一对标记。
自训练方法有何缺陷呢? 貌似也不太看得出,尝试在13.4题代码的基础上进行修改,实现自训练算法,在人为生成的两簇无法明显线性分离的数据集上进行试验,观察运行结果:
在这里插入图片描述

上图实现了自训练算法,基学习器为SVM。与TSVM不同,这里未标记样本与有标记样本权重相同,每次重新拟合后,对所有样本重新指派伪标记,而不再是一次只交换一对。
在这里插入图片描述
上图在前面自训练的基础上引入了Cu由小变大的机制,类似于TSVM。
在这里插入图片描述
作为对比,上图是TSVM算法的运行结果。
观察上面的运行结果,从运行效果上看,暂时没看出来自训练方法有什么明显的缺陷。

13.9 给定一个数据集,假设其属性集包含两个视图,但事先并不知道哪些属性属于哪个视图,试设计一个算法将这两个视图分离出来。

:我能想到的一个思路是:为了确保不同视图间的独立性,先计算每一对属性之间的相关性,然后将彼此相关性较高的属性聚为同一个视图下的属性,这个过程有点像聚类,只不过这里不是对样本聚类,而是对属性聚类。

13.10 试为图13.7算法的第10行写出违约检测算法(用于检测是否有约束未被满足)。

\[[(x_i\in {\Bbb{M}} ) ∧\exists\{x_j|(x_j\in \Bbb{M})∧(x_j\in C_s)∧(s\neq r)\}]\\ or \,\,[(x_i\in {\Bbb{C}} ) ∧\exists\{x_j|(x_j\in \Bbb{C})∧(x_j\in C_r)\}]\]


附:编程代码

ex13.4 代码(python)

# -*- coding: utf-8 -*-
"""
Created on Sat Jul 25 23:45:32 2020

@author: Administrator

ex13.4
"""

from sklearn.svm import SVC
from sklearn import datasets
from sklearn.model_selection import train_test_split as split
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.animation as animation

#==================================
#         编写实现TSVM算法的函数
#==================================
def TSVM(Dl,Du,Cl,Cu,gifname=None):
    '''
    # 该函数实现TSVM算法,其中支持向量机算法基于sklearn中的SVC类的线性支持向量机实现
    # 
    # 输入参数:
    #     Dl:有标记样本数据,格式为元祖形式:(Xl,yl),其中yl取值为{1,-1}
    #     Du:未标记样本数据,仅Xu
    #     Cl,Cu:初始折中参数,Cu<<Cl
    #     gifname:若要将TSVM算法过程保存为gif动画,则传入文件名,默认不保存动画
    # 输出:
    #     clf:基于sklearn的支持向量机SVM分类器
    '''
    
    Xl,yl=Dl
    Xu=Du
    X_mix=np.r_[Xl,Xu]
    clf=SVC(C=Cl,kernel='linear').fit(Xl,yl)  #基于有标记样本的初始SVM分类器
    yu=clf.predict(Xu)                        #向未标记样本指派伪标记
    #acts用于后续绘制动画所用,其中储存了相应的事件动作的关键参数,
    #比如:重新拟合后的权重w和偏置b、重新分派伪标记后的伪标记yu等
    acts=[{'w':clf.coef_.reshape(-1),'b':clf.intercept_,'text':'初始模型(仅有标记样本)'}]
    acts.append({'assign':yu.copy(),'text':'分派伪标记'})
    while Cu<Cl:
        #样本权重,传入clf.fit()函数可实现对于不同样本不同的权重值
        sample_weight=[1.0]*len(yl)+[Cu/Cl]*len(yu)
        clf.fit(X_mix,np.r_[yl,yu],sample_weight=sample_weight)
        acts.append({'w':clf.coef_.reshape(-1),'b':clf.intercept_,'text':'调整Cu为%.3E后重新拟合'%Cu})
        while True:
            f=clf.decision_function(Xu)  #计算f(x)=wx+b的结果
            xi=np.fmax(1-f*yu,0)         #计算ξ值,基于y(wx+b)≥1-ξ,ξ≥0
            y1_index=np.where(yu==1)[0]  #伪标记为+1的索引号
            y0_index=np.where(yu==-1)[0] #伪标记为-1的索引号
            max1=max(xi[yu==1])          #伪标记为+1的样本中的最大ξ值
            max0=max(xi[yu==-1])         #伪标记为-1的样本中的最大ξ值
            #只需分别考虑伪正负样本中最大ξ值的两个样本即可,
            #因为若这两个最大ξ值的样本不满足条件(ξi>0,ξj>0,ξi+ξj>2),
            #那么其他样本对也必然无法满足了。
            if (max1>0)&(max0>0)&(max1+max0>2):
                print('交换伪标记:ξ_+1=%.3f,ξ_-1=%.3f'%(max1,max0))
                i=y1_index[np.argmax(xi[yu==1])]   #伪标记为+1的样本中的最大ξ值对应的样本索引号
                j=y0_index[np.argmax(xi[yu==-1])]  #伪标记为-1的样本中的最大ξ值对应的样本索引号
                yu[i]*=-1
                yu[j]*=-1
                acts.append({'exchanging':[i,j],'text':'交换伪标记中...'})
                acts.append({'assign':yu.copy(),'text':'完成交换'})
                clf.fit(X_mix,np.r_[yl,yu],sample_weight=sample_weight)
                acts.append({'w':clf.coef_.reshape(-1),'b':clf.intercept_,'text':'交换标记后重新拟合'})
            else:
                break
        Cu=min(2*Cu,Cl)
    acts.append({'text':'TSVM算法执行完毕!'})
    
    if gifname!=None:
        #设置绘图中显示中文
        plt.rcParams['font.sans-serif']=['SimHei']
        plt.rcParams['axes.unicode_minus'] = False
        
        fig=plt.figure()
        
        #绘制有标记样本,用颜色区分正负样本
        plt.scatter(Xl[yl==1,0],Xl[yl==1,1],s=40,c='r',marker='+',edgecolors='r')
        plt.scatter(Xl[yl==-1,0],Xl[yl==-1,1],s=40,c='g',marker='_',edgecolors='g')
        #绘制有标记样本,全部显示为黑色小点
        plt.scatter(Xu[:,0],Xu[:,1],s=10,c='k')
        #设置坐标轴上下限
        x1min,x1max=min(X_mix[:,0]),max(X_mix[:,0])
        x2min,x2max=min(X_mix[:,1]),max(X_mix[:,1])
        plt.xlim([x1min-(x1max-x1min)*0.2,x1max+(x1max-x1min)*0.2])
        plt.ylim([x2min-(x2max-x2min)*0.2,x2max+(x2max-x2min)*0.2])
        
        #分类器决策线
        decision_line,=plt.plot([],[],c='k')
        #无标记样本之+1样本指派结果,颜色与有标记样本对应颜色相同
        unlabel_points1,=plt.plot([],[],marker='.',linestyle='',c='r')
        #无标记样本之-1样本指派结果,颜色与有标记样本对应颜色相同
        unlabel_points0,=plt.plot([],[],marker='.',linestyle='',c='g')
        #绘制交换标记操作的牵引线
        exchange,=plt.plot([0,0],[0,0],linestyle='',c='m',linewidth=2)  #初始,任意位置,设置为不显示
        #显示当前操作状态的文字说明
        state=plt.text((x1min+x1max)/2,x2max+(x2max-x2min)*0.1,'',fontsize=12)
        
        def update(num):
            # 更新动画帧的函数,num为帧数
            act=acts[num]
            if 'w' in act:
                decision_line.set_data([x1min,x1max],
                                       [-(x1min*act['w'][0]+act['b'])/act['w'][1],
                                    -(x1max*act['w'][0]+act['b'])/act['w'][1]])
                exchange.set_linestyle('')
                state.set_text(act['text'])
            elif 'exchanging' in act:
                i,j=act['exchanging']
                exchange.set_linestyle('--')
                exchange.set_data([Xu[i][0],Xu[j][0]],[Xu[i][1],Xu[j][1]])
                state.set_text(act['text'])
            elif 'assign' in act:
                yu=act['assign']
                unlabel_points1.set_data(Xu[yu==1,0],Xu[yu==1,1])
                unlabel_points0.set_data(Xu[yu==-1,0],Xu[yu==-1,1])
                state.set_text(act['text'])
            else:
                state.set_text(act['text'])
            
            return [decision_line,unlabel_points1,unlabel_points0,exchange,state]
        
        #动画生成函数animation.FuncAnimation(),其中参数intervel为每帧的持续时间,单位为ms
        ani=animation.FuncAnimation(fig,update,frames=range(len(acts)),interval=1000)
        #关于保存gif动画,有些电脑可能直接可以正常运行,
        #有些电脑则会报错,这是因为电脑系统中缺少了某些组件,
        #可以根据提示以及参考网络上一些案例进行安装,
        #比如,我参考了下列网址中的方法成功地解决了问题:
        #https://blog.csdn.net/weixin_41957054/article/details/107280246
        #https://blog.csdn.net/qq_21905401/article/details/103023074
        ani.save(str(gifname)+'.gif',writer='imagemagick')
        plt.show()
    return clf

#==========================================
#     生成简单二维数据集,
#     明显线性可分离的两类数据,
#     试验并观察TSVM算法过程
#     计算过程通过gif动画的形式进行演示
#==========================================
#X1和X2为生成的两类数据
X1=np.random.random([50,2])
X2=np.random.random([50,2])+[2,0]
X1=X1[np.argsort(X1[:,1])]
X2=X2[np.argsort(X2[:,1])]
#在X1和X2中各取五个样本组成有标记样本
Xl=np.r_[X1[:5],X2[-5:]]
yl=np.array([1]*5+[-1]*5)
#其余的样本作为无标记样本
Xu=np.r_[X1[5:],X2[:-5]]
TSVM((Xl,yl),Xu,1,0.0001,'demo1')

#==========================================
#     同样生成简单二维数据集,
#     但是两类数据有重合,无法明显线性划分
#==========================================
#X1和X2为生成的两类数据
X1=np.random.random([50,2])
X2=np.random.random([50,2])+[1,0]
X1=X1[np.argsort(X1[:,1])]
X2=X2[np.argsort(X2[:,1])]
#在X1和X2中各取五个样本组成有标记样本
Xl=np.r_[X1[:5],X2[-5:]]
yl=np.array([1]*5+[-1]*5)
#其余的样本作为无标记样本
Xu=np.r_[X1[5:],X2[:-5]]
TSVM((Xl,yl),Xu,1,0.0001,'demo2')

#==========================================
#    在莺尾花数据集上进行试验
#==========================================
print('-------在莺尾花数据集上进行试验-------')
iris=datasets.load_iris()
X=iris['data']
y=(iris['target']==1)*2-1  #将第1类设为y=+1,第2、3类设为y=-1

#划分数据集:X_test:Xu:Xl样本比例为:3:6:1
X_train,X_test,y_train,y_test=split(X,y,test_size=0.3,random_state=12)
Xu,Xl,yu,yl=split(X_train,y_train,test_size=1/7,random_state=12)

#单独有标记样本进行训练和预测
Cl=1
clf0=SVC(C=Cl,kernel='linear').fit(Xl,yl)
y_pre0=clf0.predict(X_test)
acc0=(y_pre0==y_test).mean()
print('仅用有标记样本进行训练的模型的预测精度为:',acc0)

#利用无标记样本的半监督模型
Cl,Cu=1,0.0001
clf1=TSVM((Xl,yl),Xu,Cl,Cu)
y_pre1=clf1.predict(X_test)
acc1=(y_pre1==y_test).mean()
print('利用无标记样本的半监督模型的预测精度为:',acc1)
print('预测精度提高了%.2f%%'%((acc1-acc0)/acc0*100))

#==========================================
#    在手写数据集上进行试验
#==========================================
print('-------在手写数据集上进行试验-------')
digits=datasets.load_digits()
X=digits['data']
y=(digits['target']<5)*2-1  #将0~4设为y=+1,第5~9设为y=-1

#划分数据集:X_test:Xu:Xl样本比例为:3:6:1
X_train,X_test,y_train,y_test=split(X,y,test_size=0.3,random_state=12)
Xu,Xl,yu,yl=split(X_train,y_train,test_size=1/7,random_state=12)

#单独有标记样本进行训练和预测
Cl=1
clf0=SVC(C=Cl,kernel='linear').fit(Xl,yl)
y_pre0=clf0.predict(X_test)
acc0=(y_pre0==y_test).mean()
print('仅用有标记样本进行训练的模型的预测精度为:',acc0)

#利用无标记样本的半监督模型
Cl,Cu=1,0.0001
clf1=TSVM((Xl,yl),Xu,Cl,Cu)
y_pre1=clf1.predict(X_test)
acc1=(y_pre1==y_test).mean()
print('利用无标记样本的半监督模型的预测精度为:',acc1)
print('预测精度提高了%.2f%%'%((acc1-acc0)/acc0*100))

ex13.8 代码(python)

修改TSVM函数中部分代码实现自训练算法,可以只修改循环体“while Cu<Cl:”部分代码为:

def self_train(Dl,Du,Cl,Cu,gifname=None):
    '''
    ***********************
    前面部分与TSVM函数相同
    '''
    while Cu<=Cl:
        #样本权重,传入clf.fit()函数可实现对于不同样本不同的权重值
        sample_weight=[1.0]*len(yl)+[Cu/Cl]*len(yu)
        
        clf.fit(X_mix,np.r_[yl,yu],sample_weight=sample_weight)
        yu=clf.predict(Xu)
        acts.append({'w':clf.coef_.reshape(-1),'b':clf.intercept_,'text':'加入带伪标记的无标记样本重新拟合'})
        acts.append({'assign':yu.copy(),'text':'重新分派伪标记'})
        while True:
            clf.fit(X_mix,np.r_[yl,yu],sample_weight=sample_weight)
            yu1=clf.predict(Xu)
            if (yu==yu1).all():
                break
            else:
                yu=yu1
                acts.append({'w':clf.coef_.reshape(-1),'b':clf.intercept_,'text':'重新拟合'})
                acts.append({'assign':yu.copy(),'text':'重新指派'})
        Cu=min(2*Cu,Cl+0.01)
        if Cu<=Cl:
            acts.append({'assign':yu.copy(),'text':'调整Cu为%.3e'%Cu})
    acts.append({'text':'TSVM算法执行完毕!'})
    '''
    ***********************
    后面也与TSVM函数相同,不做变化
    '''

然后人为生成的两簇无法明显线性分离的数据集进行试验,

#==========================================
#     生成简单二维数据集,
#     两类数据有重合,无法明显线性划分
#==========================================
#X1和X2为生成的两类数据
X1=np.random.random([50,2])
X2=np.random.random([50,2])+[1,0]
X1=X1[np.argsort(X1[:,1])]
X2=X2[np.argsort(X2[:,1])]
#在X1和X2中各取五个样本组成有标记样本
Xl=np.r_[X1[:5],X2[-5:]]
yl=np.array([1]*5+[-1]*5)
#其余的样本作为无标记样本
Xu=np.r_[X1[5:],X2[:-5]]
self_train((Xl,yl),Xu,1,1,'13.8_self_train_Cu=1')
self_train((Xl,yl),Xu,1,1E-4,'13.8_self_train_Cu=1E_4')
TSVM((Xl,yl),Xu,1,1E-4,'13.8_TSVM_Cu=1E_4')
posted @ 2020-09-21 01:12  之始  阅读(3230)  评论(0编辑  收藏  举报