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

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


目录


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

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

  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算法求解:git

  • 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\),利用拉格朗日方法来求解该约束问题:app

\[\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\),因此:dom

\[\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,第二、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')