用scikit-learn研究區域性線性嵌入(LLE)

劉建平Pinard發表於2017-01-11

    在區域性線性嵌入(LLE)原理總結中,我們對流形學習中的區域性線性嵌入(LLE)演算法做了原理總結。這裡我們就對scikit-learn中流形學習的一些演算法做一個介紹,並著重對其中LLE演算法的使用方法做一個實踐上的總結。

1. scikit-learn流形學習庫概述

    在scikit-learn中,流形學習庫在sklearn.manifold包中。裡面實現的流形學習演算法有:

    1)多維尺度變換MDS演算法:這個對應的類是MDS。MDS演算法希望在降維時在高維裡樣本之間的歐式距離關係在低維可以得到保留。由於降維時它需要考慮了樣本的全域性歐式距離關係,因此降維計算量很大,現在一般較少使用了。

    2)等距對映ISOMAP演算法:這個對應的類是Isomap。 ISOMAP演算法使用了樣本間的測地距離來代替歐式距離,此外基本和MDS演算法相同。由於降維時它仍然需要考慮了樣本的全域性測地距離關係,因此降維計算量很大。

    3)區域性線性嵌入LLE演算法:這個對應的類是LocallyLinearEmbedding。這個就是我們LLE原理篇裡面的演算法、除了包含我們原理篇裡講到的標準的LLE實現以外,它還支援改進版的LLE演算法,包括MLLE,HLLE和LTSA。這三個演算法我們在原理篇的第五節有介紹。後面我們會詳細講這個類的引數使用。

    4)拉普拉斯特徵對映LE演算法:這個對應的類是SpectralEmbedding。這個演算法使用了圖論的方法,用樣本構成的無向圖對應的拉普拉斯矩陣作特徵分解來降維。具體方法和我們在譜聚類(spectral clustering)原理總結裡面講到的基本相同。

    5)t-distributed Stochastic Neighbor Embedding(t-SNE)演算法:這個對應的類是TSNE。這個是一個比較新的降維方法。t-SNE希望樣本間的在高維對應的高斯核函式相似度在低維可以得到保留,即低維和高維有儘量一樣的相似度矩陣。

    這些演算法基本原理很類似,都基於流形降維後保持樣本之間的某一個特定的關係而產生。下面我們重點講述LLE演算法的使用,即LocallyLinearEmbedding的使用。

2. LLE演算法類庫使用介紹

    LLE演算法類LocallyLinearEmbedding使用起來並不複雜,一般來說,需要調參的引數只有樣本近鄰的個數。下面我們對LocallyLinearEmbedding的主要引數做一個介紹。

    1)n_neighbors:即我們搜尋樣本的近鄰的個數,預設是5。 n_neighbors個數越大,則建立樣本區域性關係的時間會越大,也就意味著演算法的複雜度會增加。當然n_neighbors個數越大,則降維後樣本的區域性關係會保持的更好。在下一節我們可以通過具體的例子看出這一點。一般來說,如果演算法執行時間可以接受,我們可以儘量選擇一個比較大一些的n_neighbors。

    2)n_components:即我們降維到的維數。如果我們降維的目的是視覺化,則一般可以選擇2-5維。

    3) reg :正則化係數,在n_neighbors大於n_components時,即近鄰數大於降維的維數時,由於我們的樣本權重矩陣不是滿秩的,LLE通過正則化來解決這個問題。預設是0.001。一般不用管這個引數。當近鄰數遠遠的大於降維到的維數時可以考慮適當增大這個引數。

    4)eigen_solver:特徵分解的方法。有‘arpack’和‘dense’兩者演算法選擇。當然也可以選擇'auto'讓scikit-learn自己選擇一個合適的演算法。‘arpack’和‘dense’的主要區別是‘dense’一般適合於非稀疏的矩陣分解。而‘arpack’雖然可以適應稀疏和非稀疏的矩陣分解,但在稀疏矩陣分解時會有更好演算法速度。當然由於它使用一些隨機思想,所以它的解可能不穩定,一般需要多選幾組隨機種子來嘗試。

    5)method: 即LLE的具體演算法。LocallyLinearEmbedding支援4種LLE演算法,分別是'standard'對應我們標準的LLE演算法,'hessian'對應原理篇講到的HLLE演算法,'modified'對應原理篇講到的MLLE演算法,‘ltsa’對應原理篇講到的LTSA演算法。預設是'standard'。一般來說HLLE/MLLE/LTSA演算法在同樣的近鄰數n_neighbors情況下,執行時間會比標準的LLE長,當然降維的效果會稍微好一些。如果你對降維後的資料區域性效果很在意,那麼可以考慮使用HLLE/MLLE/LTSA或者增大n_neighbors,否則標準的LLE就可以了。需要注意的是使用MLLE要求n_neighbors > n_components,而使用HLLE要求n_neighbors > n_components * (n_components + 3) / 2

    6)neighbors_algorithm:這個是k近鄰的搜尋方法,和KNN演算法的使用的搜尋方法一樣。演算法一共有三種,第一種是蠻力實現,第二種是KD樹實現,第三種是球樹實現。這三種方法在K近鄰法(KNN)原理小結中都有講述,如果不熟悉可以去複習下。對於這個引數,一共有4種可選輸入,‘brute’對應第一種蠻力實現,‘kd_tree’對應第二種KD樹實現,‘ball_tree’對應第三種的球樹實現, ‘auto’則會在上面三種演算法中做權衡,選擇一個擬合最好的最優演算法。需要注意的是,如果輸入樣本特徵是稀疏的時候,無論我們選擇哪種演算法,最後scikit-learn都會去用蠻力實現‘brute’。個人的經驗,如果樣本少特徵也少,使用預設的 ‘auto’就夠了。 如果資料量很大或者特徵也很多,用"auto"建樹時間會很長,效率不高,建議選擇KD樹實現‘kd_tree’,此時如果發現‘kd_tree’速度比較慢或者已經知道樣本分佈不是很均勻時,可以嘗試用‘ball_tree’。而如果輸入樣本是稀疏的,無論你選擇哪個演算法最後實際執行的都是‘brute’。

3. LLE用於降維視覺化實踐

    下面我們用一個具體的例子來使用scikit-learn進行LLE降維並視覺化。

    完整程式碼參見我的github: https://github.com/ljpzzz/machinelearning/blob/master/classic-machine-learning/lle.ipynb

    首先我們載入需要的類庫:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline
from sklearn import manifold, datasets
from sklearn.utils import check_random_state

    我們接著生成隨機資料,由於LLE必須要基於流形不能閉合,因此我們生成了一個缺一個口的三維球體。生成資料並視覺化的程式碼如下:

n_samples = 500
random_state = check_random_state(0)
p = random_state.rand(n_samples) * (2 * np.pi - 0.55)
t = random_state.rand(n_samples) * np.pi

# 讓球體不閉合,符合流形定義
indices = ((t < (np.pi - (np.pi / 8))) & (t > ((np.pi / 8))))
colors = p[indices]
x, y, z = np.sin(t[indices]) * np.cos(p[indices]), \
    np.sin(t[indices]) * np.sin(p[indices]), \
    np.cos(t[indices])

fig = plt.figure()
ax = Axes3D(fig, elev=30, azim=-20)
ax.scatter(x, y, z, c=p[indices], marker='o', cmap=plt.cm.rainbow)

    我們可以看到原始的資料是這樣的:

    現在我們簡單的嘗試用LLE將其從三維降為2維並視覺化,近鄰數設為30,用標準的LLE演算法。

train_data = np.array([x, y, z]).T
trans_data = manifold.LocallyLinearEmbedding(n_neighbors =30, n_components = 2,
                                method='standard').fit_transform(train_data)
plt.scatter(trans_data[:, 0], trans_data[:, 1], marker='o', c=colors)

    降維到2維後的效果圖如下:

    可以看出從三維降到了2維後,我們大概還是可以看出這是一個球體。

    現在我們看看用不同的近鄰數時,LLE演算法降維的效果圖,程式碼如下:

for index, k in enumerate((10,20,30,40)):
    plt.subplot(2,2,index+1)
    trans_data = manifold.LocallyLinearEmbedding(n_neighbors = k, n_components = 2,
                                method='standard').fit_transform(train_data)
    plt.scatter(trans_data[:, 0], trans_data[:, 1], marker='o', c=colors)
    plt.text(.99, .01, ('LLE: k=%d' % (k)),
                 transform=plt.gca().transAxes, size=10,
                 horizontalalignment='right')
plt.show()

    效果圖如下:

    現在我們看看還是這些k近鄰數,用HLLE的效果。

for index, k in enumerate((10,20,30,40)):
    plt.subplot(2,2,index+1)
    trans_data = manifold.LocallyLinearEmbedding(n_neighbors = k, n_components = 2,
                                method='hessian').fit_transform(train_data)
    plt.scatter(trans_data[:, 0], trans_data[:, 1], marker='o', c=colors)
    plt.text(.99, .01, ('HLLE: k=%d' % (k)),
                 transform=plt.gca().transAxes, size=10,
                 horizontalalignment='right')
plt.show()

    輸出如下:

    可見在同樣的近鄰數的時候,HLLE降維後的資料分佈特徵效果要比LLE更好。

    我們接著看看MLLE和LTSA的效果。由於程式碼類似,這裡就只給出效果圖。

    首先是MLLE的效果圖:

    接著是LTSA的效果圖:

    從上面的一系列圖也可以看出,同樣的k-近鄰數情況下, MLLE,HLLE和LTSA降維的視覺化效果更好。同樣的演算法,k-近鄰數越大則降維視覺化效果越好。當然,沒有免費的午餐,較好的降維視覺化效果意味著更多的演算法執行時間。

 

(歡迎轉載,轉載請註明出處。歡迎溝通交流: liujianping-ok@163.com) 

相關文章