前言
在上篇《Python 機器學習實戰 —— 監督學習》介紹了 支持向量機、k近鄰、朴素貝葉斯分類 、決策樹、決策樹集成等多種模型,這篇文章將為大家介紹一下無監督學習的使用。
無監督學習顧名思義數據中不包含已知的輸出結果,學習算法中只有輸入數據,算法需要從這些輸入數據中提取相關規律。
無監督學習主要分為兩種類型:數據集變換與聚類算法,數據集的無監督變換是創建數據集的新的表達方式,使其特性更容易理解,最常見的模型有 PCA、NMF、t-SNE 等模型。聚類算法則是將數據划分成不同的組,每組數據中包含有類似的特征,常見的模型有 KMeans、DBSCAN、GMM、Agglomerative 等,下面將對各種模型的特性與應用場景作詳細介紹。
目錄
數據集變換的一個主要常見應用就是降維和壓縮數據,從多維數據中提取其重要的特征,最常用的模型就是 PCA 與 NMF。
另一個應用是流形學習,它試圖把一個低維度流形數據嵌入到一個高維度空間來描述數據集,通過轉換找不到的數據規律,常見的模型有 t-SNE、MDS、LLE、Isomap 等。
一、PCA 主成分分析
1.1 PCA 降維的基本原理
主成分分析 PCA(Principal Component Analysis)是最常用的非監督學習,常用於高維數據的降維,可用於提取數據的主要特征分量。PCA 經常與監督學習合並使用,特別是在數據量較大的情況下,通過 PCA 壓縮數據集,再使用監督學習模型進行分析可提升系統有效性。
PCA 會根據設置把數據點分解成一些分量的加權求和,利用降維的思想,把多指標轉化為少數幾個綜合指標。這個變換把數據變換到一個新的坐標系統中,使得任何數據投影的第一大方差在第一個坐標(稱為第一主成分)上,第二大方差在第二個坐標(第二主成分)上,依次類推。主成分分析經常用於減少數據集的維數,同時保持數據集的對方差貢獻最大的特征。
下面以一個最簡單的例子作為說明,如圖以一個二維特征的數據集為例,PCA 使用旋轉數據集的方法,把旋轉后新數據特征的重要性來選擇特征的子集。左上角的圖是原始的數據點,算法先找到方差最大的方向 Component1,此方向包含最多的信息量。然后並以其垂直方向為 Component 2,如此類推,因為這是二維數據集所以只包含兩個成分,通常主成分個數會與維度數相等。定好成分以后,如圖二把坐標軸旋轉,令第一個成分與 X 軸平行,第二個成分與 Y 軸平行。在旋轉時從數據中減去平均值,使到轉換后的數據都是以 0 為中心。由於 X 軸與 Y 軸是不相關的,所以除了對角線,其他的矩陣都為 0。因此在圖三旋轉后只保存了 Component1 成分,這就把數據集從二維數據集降到一維數據集。最后把軸重新旋轉到原來方向,並把平均值加到數據中,就得到圖四。這就是最簡單的 PCA 降維過程。
1.2 PCA 類簡介
構造函數
1 class PCA(_BasePCA): 2 @_deprecate_positional_args 3 def __init__(self, n_components=None, *, copy=True, whiten=False, 4 svd_solver='auto', tol=0.0, iterated_power='auto', 5 random_state=None): 6 ......
參數說明
- n_components:int, float 類型 or 'mle', 默認值國 None。int 時則是直接指定希望PCA降維后的特征維度數目,此時 n_components是一個大於等於1的整數。也可以使用 float 指定主成分的方差和所占的最小比例閾值,讓PCA類自己去根據樣本特征方差來決定降維到的維度數,此時 n_components是一個[0,1]之間的數。當然,我們還可以將參數設置為"mle", 此時PCA類會用MLE算法根據特征的方差分布情況自己去選擇一定數量的主成分特征來降維。我們也可以用默認值,即不輸入n_components,此時n_components=min(樣本數,特征數)。
- copy: bool 類型,表示是否在運行算法時,將原始數據復制一份。默認為True,則運行PCA算法后,原始數據的值不會有任何改變。因為是在原始數據的副本上進行運算的。
- whiten :bool 類型,判斷是否進行白化,默認值為 False ,即不進行白化。所謂白化,就是對降維后的數據的每個特征進行歸一化,讓方差都為1.對於PCA降維本身來說,一般不需要白化。如果你PCA降維后有后續的數據處理動作。
- svd_solver:str 類型,可選值 {'auto', 'full', 'arpack', 'randomized'} 之一,默認值為 auto 。即指定奇異值分解SVD的方法,由於特征分解是奇異值分解SVD的一個特例,一般的PCA庫都是基於SVD實現的。randomized一般適用於數據量大,數據維度多同時主成分數目比例又較低的PCA降維,它使用了一些加快SVD的隨機算法。 full則是傳統意義上的SVD,使用了scipy庫對應的實現。arpack和randomized的適用場景類似,區別是randomized使用的是scikit-learn自己的SVD實現,而arpack直接使用了scipy庫的sparse SVD實現。默認是auto,即PCA類會自己去在前面講到的三種算法里面去權衡,選擇一個合適的SVD算法來降維。一般來說,使用默認值就夠了。
- tol: float 類型,默認值為 0.0,代表求解方法精度。
- iterated_power: int 類型,默認auto,代表當 svd_solver == ‘randomized’ 時冪方法的迭代次數。
- random_state: int 類型,默認None,隨機數種子,推薦設置一個任意整數,同一個隨機值,模型可以復現。
常用屬性
- components_:返回主要成分,運行 transform 后的主要成分將保存在此屬性當中。
- n_components_:返回所保留的成分個數 n。
- explained_variance_:特征方差值,方差值越大,說明重要性要越高。與 explained_variance_ratio_ 同時使用可更清晰分辨每個特征的占比。
- explained_variance_ratio_:每個特征方差貢獻率,個比例越大,說明重要性要越高,所有總和為1。
- noise_variance_: 根據概率主成分分析模型估計的噪聲協方差。
常用方法
- fit(self, X, y=None): 表示用數據X來訓練PCA模型。
- transform(selft,X):將數據X轉換成降維后的數據。通過與 fit 同用,先調用 fix,當模型訓練好后,再使用 transform 方法來降維。
- fit_transform(self, X, y=None): 用X來訓練PCA模型,同時返回降維后的數據。相當於結合了 fit 與 transform 兩個方法。
- inverse_transform(self, X):將降維后的數據轉換成為原始數據。
1.3 應用實例
1.3.1 使用 PCA 降噪
嘗試先使用 PCA 降噪,提取 60 個主要成分,再使用 KNeighborsClassifier 模型進行訓練,對比一下測試結果。使用 PCA 降噪后,准確率從 30% 上升到 40%,可見通過有效提取主要成分有可能得到更好的數據模式。一般可以通過 explained_variance_ratio_ 屬性,來判斷所需要成分的數量,成分數量過少不能反映數量集的主要特征,成分數量過多,丟失了 PCA 降燥的意義,一般會把 explained_variance 保持到 90% 以上。
1 def knn_test(): 2 #測試數據集 3 person=datasets.fetch_lfw_people(min_faces_per_person=20) 4 X_train,X_test,y_train,y_test=train_test_split(person.data,person.target,random_state=1) 5 #使用 KNeighborsClassifier 模型進行測試 6 knn = KNeighborsClassifier() 7 knn.fit(X_train, y_train) 8 #輸出knn准確率 9 print('KNN:\n train data:{0}\n test data:{1}' 10 .format(knn.score(X_train, y_train), 11 knn.score(X_test, y_test))) 12 13 def pca_test(): 14 #測試數據集 15 person=datasets.fetch_lfw_people(min_faces_per_person=20) 16 X_train,X_test,y_train,y_test=train_test_split(person.data,person.target,random_state=1) 17 #建立PCA模型,使用60個主要成分 18 pca=PCA(60 ,whiten=True,random_state=2).fit(X_train) 19 #將數據X轉換成降維后的數據 20 X_train_pca=pca.transform(X_train) 21 X_test_pca=pca.transform(X_test) 22 #建立 KNeighborsClassifier 模型 23 knn=KNeighborsClassifier() 24 #把過濾后60個特征的數據放入KNN模型進行訓練 25 knn.fit(X_train_pca,y_train) 26 #輸出准確率 27 print('\nPCA->KNN:\n train data:{0}\n test data:{1}' 28 .format(knn.score(X_train_pca, y_train), 29 knn.score(X_test_pca, y_test))) 30 #觀察累計方差貢獻率 31 plt.plot(np.cumsum(pca.explained_variance_ratio_)) 32 plt.xlabel('number of components') 33 plt.ylabel('cumulative explained variance') 34 plt.show() 35 36 if __name__ == '__main__': 37 knn_test() 38 pca_test()
運行結果
1.3.2 提取面部特征
PCA 通過 fit_transform 方法完成降維后,可通過 inverse_transform 方法將降維后的數據轉換成為原始數據。對比一下圖一與圖二,可以辨別二種不同的情況,通過使用此方法對圖片進行壓縮,原來 3023 個特征已經被壓縮到 175 個,圖片的特征已比較清晰。特征成分保存在 PCA 的 components_ 屬性中,圖三顯示了前 15 個特征成分。
1 def pca_test(): 2 #測試數據集 3 person=datasets.fetch_lfw_people(min_faces_per_person=20) 4 5 #建立PCA模型,使用 95% 的主要成分 6 pca=PCA(0.95 ,whiten=True,random_state=2) 7 #將數據X轉換成降維后的數據 8 componmets=pca.fit_transform(person.data) 9 #顯示原圖 10 index1,index2,index3=0,0,0 11 fix, axes1 = plt.subplots(3, 5, figsize=(60, 40)) 12 for ax in axes1.ravel(): 13 a=person.data[index1,:] 14 ax.imshow(person.data[index1,:].reshape(62,47),cmap='viridis') 15 ax.set_title('original '+str(index1),fontsize=100) 16 index1+=1 17 #提取特征成分后還原數據 18 fix, axes2 = plt.subplots(3, 5, figsize=(60, 40)) 19 for ax in axes2.ravel(): 20 image = pca.inverse_transform(componmets[index2]) 21 ax.imshow(image.reshape(62, 47), cmap='viridis') 22 ax.set_title('inverse '+str(index2),fontsize=100) 23 index2 += 1 24 #顯示特征成分 25 fix, axes3 = plt.subplots(3, 5, figsize=(60, 40)) 26 for ax in axes3.ravel(): 27 ax.imshow(pca.components_[index3].reshape(62, 47), cmap='viridis') 28 ax.set_title('component ' + str(index3), fontsize=100) 29 index3 += 1 30 plt.show()
運行結果
原圖
提取成分后還原圖
特征成分圖
二、NMF 非負矩陣分解
2.1 NMF 基本原理
非負矩陣分解 NMF(non-negatie matrix factorization)是另一種用於數據集變換的無監督學習,與 PCA 相似其目在於降維以及提取有用特征。NMF 試圖將每個數據點分解成一些分量的加權求和,然而與 PCA 不同的是,PCA 使用的是正交分量,把數據點解釋成為數據方差。而 NMF 中將要分解的系數均為非負值,也就是說所有被分解的系數均大於等於0,因此被分解后的每個特征均為非負值。
NMF 使用了隨機初始化,不同的隨機種子可能產生不同的結果。對於兩個分量的 NMF 如左圖,所有的數據點都可以分解成兩個正數組合。特征數越多,分量個數越多,只要有足夠多的分量,特征就可以完成地分解。如果僅使用一個分量如右圖,NMF 就會創建一個指向平均值的分量。
2.2 NMF 類簡介
構造函數
1 class NMF(TransformerMixin, BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, n_components=None, *, init='warn', solver='cd', 4 beta_loss='frobenius', tol=1e-4, max_iter=200, 5 random_state=None, alpha=0., l1_ratio=0., verbose=0, 6 shuffle=False, regularization='both'):
參數說明
- n_components:int, 默認值為 None。int 時則是直接指定 NMF 降維后的特征維度數目,此時 n_components是一個大於等於 1 的整數。當使用默認值,即不輸入n_components,此時n_components=min(樣本數,特征數)。
- init: str 類型,可選擇 {'random', 'nndsvd', 'nndsvda', 'nndsvdar', 'custom'} 之一,默認為 None。選擇W,H迭代初值的算法, 當為 None 時,即自動選擇值,不使用選擇初值的算法。'random':非負隨機矩陣,縮放為:sqrt(X.mean() / n_components); ‘nndsvd’`:非負雙奇異值分解(NNDSVD),初始化(對於稀疏性更好); 'nndsvda':NNDSVD與零填充了X的平均值(當不需要稀疏性時,效果更好); ' nndsvdar ': NNDSVD帶充滿小隨機值的零(通常替代NNDSVDa 因為當不需要稀疏性時); 'custom':使用自定義矩陣W和H。
- solver: str 類型,默認為 ‘cd' 坐標軸下降法 , 可選 {’cd','mu'} 。 cd 是坐標軸下降法cd,mu 是乘性更新算法。
- beta_loss:float 類型 或 {'frobenius', 'kullback-leibler', 'itakura-saito'} 之一,默認是 ’frobenius’。 衡量V與W H之間的損失值。
- tol: float 類型,默認值為 1e-4,代表求解方法精度。
- max_iter:默認值為 200 ,指定了模型優化的最大迭代次數。
- random_state:int 類型,默認None,隨機數種子,推薦設置一個任意整數,同一個隨機值,模型可以復現。
- alpha:float 類型,是正則項系數,初始值為 0.0,數值越大,則對復雜模型的懲罰力度越大。
- l1_ratio : float類型, 默認值為 0.0 彈性凈混合參數。當 l1_ratio=0 對應於L2懲罰,當 l1_ratio=1對應 L1 懲罰,當0<l1_ratio<1,懲罰是L1和L2的組合。
- verbose: int類型,默認值為0,代表詳細程度
- shuffle: bool 類型,默認值為 False, 如果為true,則隨機化 CD 求解器中的坐標順序。
- regularization: str 類型,{'both', 'components', 'transformation', None} 之一 , 默認值 為 'both'。選擇正則化是否影響 components(H),transformation(W),both 則兩者都有,None 則兩者都沒有。
2.3 應用實例
2.3.1 NMF 提取特征
用回 PCA 的相同例子進行測試,用 150 個主要成分建立 NMF 模型,提取特征成分后還原數據,把還原后數據和特征成分顯示出來。與 PCA 進行對比,可見 NMF 模型的特征與 PCA 提取的特征有明顯差別,NMF 更具有人臉特征的原型。
1 def nmf_test(): 2 person=datasets.fetch_lfw_people(min_faces_per_person=20) 3 #建立NMF模型,使用 150 個主要成分 4 nmf=NMF(150,random_state=2) 5 #將數據X轉換成降維后的數據 6 componmets=nmf.fit_transform(person.data) 7 index1,index2=0,0 8 #提取特征成分后還原數據 9 fix, axes1 = plt.subplots(3, 5, figsize=(60, 40)) 10 for ax in axes1.ravel(): 11 image = nmf.inverse_transform(componmets[index1]) 12 ax.imshow(image.reshape(62, 47), cmap='viridis') 13 ax.set_title('inverse '+str(index1),fontsize=100) 14 index1 += 1 15 #顯示特征成分 16 fix, axes2 = plt.subplots(3, 5, figsize=(60, 40)) 17 for ax in axes2.ravel(): 18 ax.imshow(nmf.components_[index2].reshape(62, 47), cmap='viridis') 19 ax.set_title('component ' + str(index2), fontsize=100) 20 index2 += 1 21 plt.show()
運行結果
提取成分后還原圖
特征成分圖
2.3.2 還原面部特征
從上面例子看到的特征成分圖可見 NMF 更具有人臉特征的原型。從 component 6 可以看出人臉是向右側,而 component 10 是向左側。現在嘗試根據特征成分圖排序還原數據,根據 component 6 和 component 10 進行倒序顯示。
從運行結果可以看到,正如所料,component 6 向右側,component 10 是向左側,通過 NMF 提取的特征更容易被人理解。
1 def nmf_test1(n): 2 person=datasets.fetch_lfw_people(min_faces_per_person=20) 3 #建立NMF模型,使用 150 個主要成分 4 nmf=NMF(150,random_state=2) 5 #將數據X轉換成降維后的數據 6 componmets=nmf.fit_transform(person.data) 7 #按照所提取特征成分 components[n]倒序排列 8 index=np.argsort(componmets[:,n])[::-1] 9 #提取特征成分后還原數據 10 fix, axes1 = plt.subplots(3, 5, figsize=(60, 40)) 11 for ax,index1 in zip(axes1.ravel(),index): 12 a = person.data[index1, :] 13 ax.imshow(person.data[index1, :].reshape(62, 47), cmap='viridis') 14 ax.set_title('original ' + str(index1), fontsize=100) 15 plt.show() 16 17 if __name__ == '__main__': 18 nmf_test1(6) 19 nmf_test1(10)
運行結果
component 6
component 10
三、ML 流形學習
PCA、NMF 雖然是比較常用的降維算法,然而對非線性的數據集處理效果不太好,為了彌補這一缺陷 sklearn 提供了另一套方案 —— 流形學習 ML (manifold learning)。流形學習是一種無監督評估器,它試圖將一個低維度流形嵌入到一個高維度空間來描述數據集。
下面介紹幾個常用的 ML 流形學習模型:MDS 多維標度法、LLE 局部線性嵌入法、Isomap 保距映射法、t-SNE 分布鄰域嵌入算法。
3.1 MDS 多維標度法
多維標度法 MDS (multidimensional scaling)是最常用的流形學習模型之一,它可以把三維數據通過計算距離矩陣,投影到二維空間,還原最優的二維嵌入結果。它的基本特征是根據每個數據點與數據集中其他點的的距離來進行計算的。
構造函數
1 class MDS(BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, n_components=2, *, metric=True, n_init=4, 4 max_iter=300, verbose=0, eps=1e-3, n_jobs=None, 5 random_state=None, dissimilarity="euclidean"): 6 self.n_components = n_components 7 self.dissimilarity = dissimilarity 8 self.metric = metric 9 self.n_init = n_init 10 self.max_iter = max_iter 11 self.eps = eps 12 self.verbose = verbose 13 self.n_jobs = n_jobs 14 self.random_state = random_state
參數說明
- n_components:int, 默認值為2。int 時則是直接指定 NMF 降維后的特征維度數目,此時 n_components是一個大於等於 1 的整數。當使用默認值,即不輸入n_components,此時n_components=min(樣本數,特征數)。
- dissimilarity:可選值:{'euclidean', 'precomputed'}, 默認值為 'euclidean' 。用於定義模型的非相似性,euclidean:根據點與點之間的歐幾里得距離進行計算; precomputed: 根據距離矩陣方式進行試算。
- metric:bool 類型,默認值為True,是否使用距離量度 MDS,False 使用非距離度量 SMACOF
- n_init:int 類型,默認值為 4,初始化 SMACOF 算法方式的運行次數。把應用的最小結果作為運行結果輸出。
- max_iter:int 類型,默認值為 300 ,指定了模型優化的最大迭代次數。
- eps:float 類型,默認值為 1-e3,收斂闕值。
- verbose: int類型,默認值為0,代表詳細程度。
- n_jobs:CPU 並行數,默認為None,代表1。若設置為 -1 的時候,則用所有 CPU 的內核運行程序。
- random_state:int 類型,默認None,隨機數種子,推薦設置一個任意整數,同一個隨機值,模型可以復現。
應用實例
下面例子使用 sklearn 自帶的測試數據集,此數據原來是三維空間中的立體數據,經過 MDS 二維評估轉換后,投影會轉換成可見的 S 形數據。
1 def mds_test(): 2 # 測試數據 3 n_points = 1000 4 X, color = datasets._samples_generator.make_s_curve(n_points, random_state=0) 5 # 顯示測試數據的3D圖 6 ax1 = plt.axes(projection='3d') 7 ax1.scatter3D(X[:, 0], X[:, 1], X[:, 2], c=color) 8 plt.show() 9 # 初始化 MDS 模型 10 mds=MDS(n_components=2,random_state=1) 11 model=mds.fit_transform(X) 12 # 顯示 MDS 評估結果 13 ax2=plt.axes() 14 plt.scatter(model[:,0],model[:,1]) 15 plt.show()
運行結果
3.2 LLE 局部線性嵌入法
使用 MDS 算法構建嵌入時,數據保留的是每對數據點之間的距離,然而在局部線性嵌入法模型 LLE (locally linear embedding) 的時候,數據不會保留所有的距離,而是保留鄰節點間的距離,此值可通過參數n_neighbors 設定,默認為5 。
構造函數
1 class LocallyLinearEmbedding(TransformerMixin, 2 _UnstableArchMixin, BaseEstimator): 3 @_deprecate_positional_args 4 def __init__(self, *, n_neighbors=5, n_components=2, reg=1E-3, 5 eigen_solver='auto', tol=1E-6, max_iter=100, 6 method='standard', hessian_tol=1E-4, modified_tol=1E-12, 7 neighbors_algorithm='auto', random_state=None, n_jobs=None): 8 ......
參數說明
- n_neighbors: int, 默認為 5 表示默認鄰居的數量。
- n_components:int, 默認值為2。int 時則是直接指定降維后的特征維度數目,此時 n_components是一個大於等於 1 的整數。當使用默認值,即不輸入n_components,此時n_components=min(樣本數,特征數)。
- reg:float 類型,默認值 1e-3,正則化系數,當 n_neighbors大於n_components時,即近鄰數大於降維的維數時,由於我們的樣本權重矩陣不是滿的,LLE通過正則化來解決這個問題。
- eigen_solver:str 類型, {'auto', 'arpack', 'dense'} 之一, 默認值 'auto' ,特征分解的方法。‘dense’:適合於非稀疏的矩陣分解; ‘arpack’: 雖然可以適應稀疏和非稀疏的矩陣分解,但在稀疏矩陣分解時會有更好算法速度。
- tol:float 類型,默認值為 1e-6,對當 eigen_solver 為 arpack 時的公差,代表求解方法精度。在 eigen_solver 為 dense 時無效。
- max_iter:int 類型,默認值為 100 ,指定了模型優化的最大迭代次數。
- method:str 類型 {'standard', 'hessian', 'modified', 'ltsa'} 之一, 默認值為 'standard' ,指定 LLE 的具體算法。一般來說 'hessian', 'modified', 'ltsa' 算法在同樣的近鄰數 n_neighbors 情況下,運行時間會比standard 的 LLE 長,當然降維的效果會稍微好一些。如果你對降維后的數據局部效果很在意,那么可以考慮使用 'hessian', 'modified', 'ltsa' 或者增大n_neighbors,否則標准的 standard 就可以了。需要注意的是使用 modified 要求n_neighbors > n_components,而使用 hessian 要求n_neighbors > n_components * (n_components + 3) / 2
- hessian_tol:float 類型,默認值為 1e-4,當 method 為 hessian 時的公差精度,其他 method 無效
- modified_tol: float 類型,默認值為 1e-12,當 method 為 modified 時的公差精度,其他 method 無效
- neighbors_algorithm:str 類型,{'auto', 'brute', 'kd_tree', 'ball_tree'} 之一, 默認值為 'auto' ,代表 k近鄰的實現方法,和KNN算法的使用的搜索方法類似。‘brute’ 對應第一種蠻力實現,‘kd_tree’對應第二種KD樹實現,‘ball_tree’對應第三種的球樹實現, ‘auto’則會在上面三種算法中做權衡,選擇一個擬合最好的最優算法。需要注意的是,如果輸入樣本特征是稀疏的時候,無論我們選擇哪種算法,最后scikit-learn都會去用蠻力實現 ‘brute’。
- n_jobs:CPU 並行數,默認為None,代表1。若設置為 -1 的時候,則用所有 CPU 的內核運行程序。
- random_state:int 類型,默認None,隨機數種子,推薦設置一個任意整數,同一個隨機值,模型可以復現。
使用與 MDS 相同的例子作為測試,把原來三維空間中的立體數據,經過 LLE 二維評估轉換后,投影會轉換成可見的 S 形數據。
1 def lls_test(): 2 # 測試數據 3 n_points = 1000 4 X, color = datasets._samples_generator.make_s_curve(n_points, random_state=0) 5 # 顯示測試數據的3D圖 6 ax1 = plt.axes(projection='3d') 7 ax1.scatter3D(X[:, 0], X[:, 1], X[:, 2], c=color) 8 plt.show() 9 # 初始化 LLE 模型 10 lle=LLE(n_neighbors=299,n_components=2,method='standard',random_state=1) 11 model=lle.fit_transform(X) 12 # 顯示 LLE 評估結果 13 ax2=plt.axes() 14 ax2.scatter(model[:,0],model[:,1]) 15 plt.show()
運行結果
3.3 Isomap 保距映射法
Isomap 模型與 LLE 模型相似,數據不會保留所有的距離,而是保留鄰節點間的距離,此值可通過參數n_neighbors 設定,默認為5 。 但 Isomap 在現實使用中對高維數據源的學習效果比較差,但有比較好的嵌入效果,一般用於數據集的預處理,可以提供分析數據的線索。
構造函數
1 class Isomap(TransformerMixin, BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, *, n_neighbors=5, n_components=2, eigen_solver='auto', 4 tol=0, max_iter=None, path_method='auto', 5 neighbors_algorithm='auto', n_jobs=None, metric='minkowski', 6 p=2, metric_params=None): 7 ......
參數說明
- n_neighbors: int, 默認為 5 表示默認鄰居的數量。
- n_components:int, 默認值為2。int 時則是直接指定降維后的特征維度數目,此時 n_components是一個大於等於 1 的整數。當使用默認值,即不輸入n_components,此時n_components=min(樣本數,特征數)。
- eigen_solver:str 類型, {'auto', 'arpack', 'dense'} 之一, 默認值 'auto' ,特征分解的方法。‘dense’:適合於非稀疏的矩陣分解; ‘arpack’: 雖然可以適應稀疏和非稀疏的矩陣分解,但在稀疏矩陣分解時會有更好算法速度。
- tol:float 類型,默認值為 1e-6,對當 eigen_solver 為 arpack 時的公差,代表求解方法精度。在 eigen_solver 為 dense 時無效。
- max_iter:int 類型,默認值為 None,當 eigen_solver 為 arpack 時,指定了模型優化的最大迭代次數。在 eigen_solver 為 dense 時無效。
- path_method: str 類型,{'auto', 'FW', 'D'} 之一, 默認值為 'auto'。 ‘FW’:使用 Floyd_Warshall 算法 ; ‘D’: 使用 Dijkstra 算法
- neighbors_algorithm:str 類型,{'auto', 'brute', 'kd_tree', 'ball_tree'} 之一, 默認值為 'auto' ,代表 k近鄰的實現方法,和KNN算法的使用的搜索方法類似。‘brute’ 對應第一種蠻力實現,‘kd_tree’對應第二種KD樹實現,‘ball_tree’對應第三種的球樹實現, ‘auto’則會在上面三種算法中做權衡,選擇一個擬合最好的最優算法。需要注意的是,如果輸入樣本特征是稀疏的時候,無論我們選擇哪種算法,最后scikit-learn都會去用蠻力實現 ‘brute’。
- n_jobs:CPU 並行數,默認為None,代表1。若設置為 -1 的時候,則用所有 CPU 的內核運行程序。
- metric: str 類型 或 callable, 默認值為 ‘minkowski’。
- p: int 類型,默認值為2 。
- metric_params: dict類型 , 默認值 為 None,使用 metric 時的參數
測試例子中使用 make_swiss_roll 數據集,使用 2000 個測試數據,把原來三維空間中的立體數據,經過 Isomap 二維評估轉換后,觀察其二維投影。
1 def iosmap_test(): 2 # 測試數據 3 X, color =make_swiss_roll(n_samples=2000, noise=0.1) 4 # 顯示測試數據的3D圖 5 ax1 = plt.axes(projection='3d') 6 ax1.scatter3D(X[:, 0], X[:, 1], X[:, 2], c=color) 7 plt.show() 8 # 初始化 Isomap 模型 9 isomap =Isomap(n_neighbors=299,n_components=2) 10 model=isomap.fit_transform(X) 11 # 顯示 Isomap 評估結果 12 ax2=plt.axes() 13 ax2.scatter(model[:,0],model[:,1]) 14 plt.show()
運行結果
3.4 t-SNE 分布鄰域嵌入算法
t-SNE 分布鄰域嵌入算法的思想是找到數據的一個二維表示方法,盡可能地分析數據點之間的距離,讓原始特征中距離較近的點更加近,距離較遠的點更加遠。通過加大距離差值,更有利於分析特征差異。
構造函數
1 class TSNE(BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, n_components=2, *, perplexity=30.0, 4 early_exaggeration=12.0, learning_rate=200.0, n_iter=1000, 5 n_iter_without_progress=300, min_grad_norm=1e-7, 6 metric="euclidean", init="random", verbose=0, 7 random_state=None, method='barnes_hut', angle=0.5, 8 n_jobs=None, square_distances='legacy'):
參數說明
- n_components:int, 默認值為2。int 時則是直接指定降維后的特征維度數目,此時 n_components是一個大於等於 1 的整數。當使用默認值,即不輸入n_components,此時n_components=min(樣本數,特征數)。
- perplexity:float 類型,默認值為 30.0,較大的數據集通常需要更大的perplexity,一般介於5和50之間的值。
- early_exaggeration: float 類型,默認值為12.0 。控制原始空間中的自然簇在嵌入空間中的緊密程度以及它們之間的空間大小。對於較大的值,自然簇之間的空間將在嵌入空間中更大。同樣,這個參數的選擇不是很關鍵。如果在初始優化時成本函數增加,則早期誇大因子或學習率可能過高。
- learning_rate:float 類型,默認值為 200.0 表示學習率。學習率太高了,數據可能看起來像一個“球”形與它的最近鄰大約相等的距離。如果是學習率太低了,大多數點可能看起來被壓縮在一個密集的區域,幾乎沒有異常值,如果遇到此情形可嘗試提高學習率可能會有所幫助。
- n_iter:int 類型,默認值為1000,優化的最大迭代次數,一般應該在 200 以上。
- n_iter_without_progress:int 類型,默認值為 30,控制迭代停止條件。
- min_grad_norm:float 類型,默認值為 1e-7,如果梯度范數低於此閾值,則優化將被中止。
- metric:str 類型或 callable 默認值為 euclidean,表示計算特征數組中實例之間的距離時使用的度量為歐氏距離的平方。如果度量標准是字符串,則它必須是scipy.spatial.distance.pdist為其度量標准參數所允許的選項之一,或者是成對列出的度量標准.PAIRWISE_DISTANCE_FUNCTIONS。如果度量是“預先計算的”,則X被假定為距離矩陣。或者,如果度量標准是可調用函數,則會在每對實例(行)上調用它,並記錄結果值。可調用應該從X中獲取兩個數組作為輸入,並返回一個表示它們之間距離的值。
- init:str 類型,{'random', 'pca'}之一,或 narray 類型數組 (n_samples,n_components),默認值為 random,嵌入的初始化。PCA 初始化不能與預先計算的距離一起使用,通常比隨機初始化更全局穩定。
- verbose:int 類型,默認值為 0,詳細度等級。
- random_state: :int 類型,默認None,隨機數種子,推薦設置一個任意整數,同一個隨機值,模型可以復現。
- method:str 類型,默認值為 “barnes_hut”,默認情況下,梯度計算算法使用在O(NlogN)時間內運行的 Barnes-Hut 近似值。 method ='exact' 將運行在O(N ^ 2)時間內較慢但精確的算法上。當最近鄰的誤差需要好於3%時,應該使用精確的算法。但是,確切的方法無法擴展到數百萬個示例。
- angle:float 類型,默認值為 0.5 。僅當 method ='barnes_hut' 時才有效,這是Barnes-Hut T-SNE的速度和准確性之間的折衷。 'angle'是從一個點測量的遠端節點的角度大小(在[3]中稱為theta)。如果此大小低於'角度',則將其用作其中包含的所有點的匯總節點。該方法對0.2-0.8范圍內該參數的變化不太敏感。小於0.2的角度會迅速增加計算時間和角度,因此0.8會快速增加誤差。
- n_jobs:int 類型,CPU 並行數,默認為None,代表1。若設置為 -1 的時候,則用所有 CPU 的內核運行程序。
- square_distances:True 或 'legacy‘,是否將距離值平方。’legacy‘ 意味着距離值只有在米制=“歐幾里德” 時才平方。True表示所有度量的距離值都是平方的。
下面嘗試使用 t-SNE 分析手寫數字的數據集,由於 t-SNE 不支持變換新數據,所以它不包含 transform 方法,一般會直接使用 fit_transform 方法代替。用 digits 數據集來測試,使用 Isomap 模型測試時,盡管 0,4,6的數據比較有效地分開 ,然后其他大部分數據依然重疊得比較利害,測試結果末達到預期效果。使用 t-SNE 模型進行測試時,由於模型自身的特點,它可以把測試集的特征區別進一步放大,所以在測試結果中可以把 0~9 數字有效地區別開來。
1 def tsne_test(): 2 # 輸入測試數據 3 digits=load_digits() 4 # Isomap 模型測試 5 isomap=Isomap(n_neighbors=299,n_components=2) 6 model0=isomap.fit_transform(digits.data) 7 # 轉出 Isomap 模型測試結果 8 plt.xlim(model0[:,0].min(),model0[:,0].max()+1) 9 plt.ylim(model0[:,1].min(),model0[:,1].max()+1) 10 for i in range(len(model0)): 11 plt.text(model0[i,0],model0[i,1],str(digits.target[i]), 12 color='red',fontdict={'weight':'bold','size':9}) 13 plt.show() 14 # t-SNE 模型測試 15 tsne=TSNE(random_state=2,learning_rate=800) 16 model1=tsne.fit_transform(digits.data) 17 plt.figure(figsize=(6,6)) 18 #輸出 t-SNE 測試結果 19 plt.xlim(model1[:,0].min(),model1[:,0].max()+1) 20 plt.ylim(model1[:,1].min(),model1[:,1].max()+1) 21 for i in range(len(model1)): 22 plt.text(model1[i,0],model1[i,1],str(digits.target[i]), 23 color='red',fontdict={'weight':'bold','size':8}) 24 plt.show()
運行結果
Isomap 測試結果
t-SNE 測試結果
到此為此,本節介紹了4個常用的 ML 流形學習模型:MDS 多維標度法、LLE 局部線性嵌入法、Isomap 保距映射法、t-SNE 分布鄰域嵌入算法。一般這些流形算法都用於在初期探索分析數據,但如果最終目標是監督學習的話則很少用到。
本章總結
由於篇幅關系,無監督學習中的數據集變換使用就先介紹到這里,關於聚類模型中 KMeans、DBSCAN、GMM 、Agglomerative 等模型將在 《 Python 機器學習實戰 —— 無監督學習(下)》中詳細講述,敬請留意。