试答系列:“西瓜书”-周志华《机器学习》习题试答
系列目录
[第01章:绪论]
[第02章:模型评估与选择]
[第03章:线性模型]
[第04章:决策树]
[第05章:神经网络]
[第06章:支持向量机]
第07章:贝叶斯分类器
第08章:集成学习
第09章:聚类
第10章:降维与度量学习
第11章:特征选择与稀疏学习
第12章:计算学习理论(暂缺)
第13章:半监督学习
第14章:几率图模型
(后续章节更新中...)html
答:首先,咱们进行一些约定和分析:
约定和分析:python
其中对于无标记样本的求和项中存在\(\ln[\sum(\cdot)]\)的形式,若直接求取\(\max_{\{\alpha,\mu,\Sigma\}}LL\)会比较困难,一般采用\(EM\)算法来求取。
EM算法求解:git
该结果与 (13.5) 式等同(尽管看起来不同)。面试
2.最大化\(E(LL)\)。
首先介绍一下高斯函数关于均值和协方差的梯度结果:算法
最后一式应用了关系:\(\nabla_A|A|=|A|(A^{-1})^T\)
下面正式最大化似然函数的指望:\(\max_{\{\alpha,\mu,\Sigma\}}E(LL)\)编程
此即 (13.6) 式,其中\(I(\cdot)\)为指示函数。网络
此即 (13.7) 式。
对于\(\{\alpha_i\}\),还要考虑约束\(\sum_i \alpha_i=1\),利用拉格朗日方法来求解该约束问题:app
由约束\(\sum_i \alpha_i=1\)可得\(\lambda=-m\),因此:dom
此即 (13.8) 式。机器学习
答:彻底与基于混合高斯模型的生成式算法相似,下面试推导一番。
这里一样假设样本类别数等于朴素贝叶斯模型的类别数,并且第i个样本类别对应于第i个模型类别,一样再也不区分\(y\)和\(\Theta\)。
另外,假设样本中每一个特征取值为离散值。
其中\(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\)。
其中\(x_{jf}\)表示样本\(\boldsymbol{x}_j\)的第\(f\)个特征取值。
似然函数的指望:
---M步---
极大化指望似然函数,获得:
其中\(l_i\)表示标记样本中属于第i类的数目,\(l_{i,f,k}\)表示标记样本中属于第\(i\)类,并且第\(f\)个特征取值为\(k\)的样本数目。
其中\(\theta=\{\theta_1,\theta_2,\cdots,\theta_k\}\)是模型参数\(p(x|\theta_i)\)是第\(i\)个混合成分的几率密度,混合系数\(\alpha_i\geq0,\sum_i^k\alpha_i=1\).假定每一个混合成分对应一个类别,但每一个类别可包含多个混合成分.试推导相应的生成式半监督学习算法。
答:
1. 一些认识
对于生成式方法的半监督学习,试借用贝叶斯网中的表示方法来表达各变量间的依赖关系,用以帮助理解:
根据上图可写出联合几率密度:
其中\(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,其他取值为零。
咱们能够将这个条件几率表达为:
其中\(C_y\)表示类别\(y\)所包含的混合成分的序号的集合。
2. 对于“每一个混合成分对应于一个类别”的状况下,求解参数\(\{\alpha_i, \theta_i\}\)
将这种对应关系视为固定不变,求解参数。若是同时也想学习这种对应关系,能够尝试不一样组合方式,取其最佳组合。
与前面混合高斯模型和朴素贝叶斯模型中的方法彻底相似,写出似然函数,而后应用EM算法:
注意,因为似然函数的\(D_l\)和\(D_u\)项都有\(\ln[\sum(\cdot)]\)的形式,所以,E步须要针对两项都要求取隐变量\(\Theta\)的后验几率:
---E步--
---M步--
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}\),此时,它也将是可学习的参数。
此时,似然函数为:
在应用EM算法求解参数时,论文中介绍了二者处理方法:
EM-I:对于有标记和无标记样本,隐变量都只是\(\Theta\).
EM-II:对于有标记,隐变量为\(\Theta\),对于无标记样本,隐变量为\(\Theta,y\).
两种方法计算获得的\(\alpha_i,\theta_i\)彻底同样,可是\(\beta_{y|i}\)不同:
其中,\(\gamma_{ji}\)的含义与前相同,而\(\gamma_{jiy}=p(\Theta=i,y|x_j)\)
答:详细编程代码附后。
答:其实教材正文中有提到:在拟合SVM的时候,对于伪正和伪负的样本采用不一样的\(C_u\)权重值,\(C^+_u:C^-_u=u_- : u_+\),这里\(u_+,u_-\)分别表示伪正和伪负的未标记样本数。
答:没太理解题意,指派和调整过程没感受太大的计算开销啊。对于调整过程,能够按题13.4中所述:“在寻找知足条件,须要交换伪标记的样本\(x_i,x_j\)时,分别对伪正例样本和伪负例样本按\(\xi\)值进行排序,取其最大者分别做为待交换样本\(x_i,x_j\),而后考察它们是否知足交换条件。只须要考虑这两个\(\xi\)值最大的样本便可,由于,若是它们都不知足条件的话,那么其他样本对也都没法知足条件。”
答:如教材正文所述:“......接收到新样本时,或是将其加入原数据集对图进行重构并从新进行标记传播,或是需引入额外的预测机制,例如将\(D_l\)和经标记传播后获得标记的\(D_u\)合并做为训练集,另外训练一个学习器例如支持向量机来对新样本进行预测。”
对于前一种方法---从新进行标记传播,若新样本较少,对于结果影响应该很小。能够基于前面的分析,计算新样本与其他有标记和无标记样本的“亲和力(好比高斯函数值)”,将该亲和力做为权重来肯定新样本的标记:\(\hat{y}_{new}=sign(\sum W_{x_{new}\,\,,x_i}\cdot y_i/\sum W_{x_{new}\,\,,x_i})\)
答:根据题干的描述,自训练与TSVM算法很相似,都是先在有标记样本上训练,而后指派伪标记,而后从新训练。
不一样点在于:TSVM算法中,未标记样本的权重有个从小变大的过程,从新指派标记时每次经过交换标记的方式只调整一对标记。
自训练方法有何缺陷呢? 貌似也不太看得出,尝试在13.4题代码的基础上进行修改,实现自训练算法,在人为生成的两簇没法明显线性分离的数据集上进行试验,观察运行结果:
上图实现了自训练算法,基学习器为SVM。与TSVM不一样,这里未标记样本与有标记样本权重相同,每次从新拟合后,对全部样本从新指派伪标记,而再也不是一次只交换一对。
上图在前面自训练的基础上引入了Cu由小变大的机制,相似于TSVM。
做为对比,上图是TSVM算法的运行结果。
观察上面的运行结果,从运行效果上看,暂时没看出来自训练方法有什么明显的缺陷。
答:我能想到的一个思路是:为了确保不一样视图间的独立性,先计算每一对属性之间的相关性,而后将彼此相关性较高的属性聚为同一个视图下的属性,这个过程有点像聚类,只不过这里不是对样本聚类,而是对属性聚类。
答:
# -*- 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))
修改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')