試答系列:「西瓜書」-周志華《機器學習》習題試答
系列目錄
[第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')