機器學習實戰_降維(二)

咱們將會展現兩種主要的降維方法:投影(projection)和流形學習(Manifold Learning),同時咱們還會介紹三種流行的降維技術:主成分分析(PCA),核主成分分析(Kernel PCA)和局部線性嵌入(LLE)。算法

主成分分析(PCA)

主成分分析(Principal Component Analysis)是目前爲止最流行的降維算法。首先它找到接近數據集分佈的超平面,而後將全部的數據都投影到這個超平面上。數組

保留(最大)方差:dom

在將訓練集投影到較低維超平面以前,您首先須要選擇正確的超平面。例如圖左側是一個簡單的二維數據集,以及三個不一樣的軸(即一維超平面)。圖右邊是將數據集投影到每一個軸上的結果。正如你所看到的,投影到實線上保留了最大方差,而在點線上的投影只保留了很是小的方差,投影到虛線上保留的方差則處於上述二者之間。機器學習

clipboard.png

選擇保持最大方差的軸看起來是合理的,由於它極可能比其餘投影損失更少的信息。證實這種選擇的另外一種方法是,選擇這個軸使得將原始數據集投影到該軸上的均方距離最小。這是就 PCA 背後的思想,至關簡單函數

主成分(Principle Componets):
PCA 尋找訓練集中可得到最大方差的軸。在上圖中,它是一條實線。它還發現了一個與第一個軸正交的第二個軸,選擇它能夠得到最大的殘差。在這個 2D 例子中,沒有選擇:就只有這條點線。但若是在一個更高維的數據集中,PCA 也能夠找到與前兩個軸正交的第三個軸,以及與數據集中維數相同的第四個軸,第五個軸等。 定義第i個軸的單位矢量被稱爲第i個主成分(PC)。在圖中,第一個 PC 是c1,第二個 PC 是c2。在投影圖中,前兩個 PC 用平面中的正交箭頭表示,第三個 PC 與上述 PC 造成的平面正交(指向上或下)性能

概述: 主成分的方向不穩定:若是您稍微打亂一下訓練集並再次運行 PCA,則某些新 PC 可能會指向與原始 PC 方向相反。可是,它們一般仍位於同一軸線上。在某些狀況下,一對 PC 甚至可能會旋轉或交換,但它們定義的平面一般保持不變。

那麼如何找到訓練集的主成分呢?幸運的是,有一種稱爲奇異值分解(SVD)的標準矩陣分解技術,能夠將訓練集矩陣X分解爲三個矩陣$U·Σ·V^T$的點積,其中$V^T$$包含咱們想要的全部主成分,以下所示。學習

clipboard.png

下面的 Python 代碼使用了 Numpy 提供的svd()函數得到訓練集的全部主成分,而後提取前兩個 PC:spa

X_centered=X-X.mean(axis=0)    # 中心化
U,s,V=np.linalg.svd(X_centered)
c1=V.T[:,0]
c2=V.T[:,1]
警告:PCA 假定數據集以原點爲中心。正如咱們將看到的,Scikit-Learn 的PCA類負責爲您的數據集中心化處理。可是,若是您本身實現 PCA(如前面的示例所示),或者若是您使用其餘庫,不要忘記首先要先對數據作中心化處理。

投影到d維空間:
一旦肯定了全部的主成分,你就能夠經過將數據集投影到由前d個主成分構成的超平面上,從而將數據集的維數降至d維。選擇這個超平面能夠確保投影將保留儘量多的方差。3d

爲了將訓練集投影到超平面上,能夠簡單地經過計算訓練集矩陣X和Wd的點積,Wd定義爲包含前d個主成分的矩陣(即由V^T的前d列組成的矩陣)code

將訓練集投影到d維空間的公式:
$$ X_{d-proj} = X \cdot W_d $$

下面的 Python 代碼將訓練集投影到由前兩個主成分定義的超平面上:

W2=V.T[:,:2]    # 降爲2維
X2D=X_centered.dot(W2)

使用 Scikit-Learn
Scikit-Learn 的 PCA 類使用 SVD 分解來實現,就像咱們以前作的那樣。如下代碼應用 PCA 將數據集的維度降至兩維(請注意,它會自動處理數據的中心化):

from sklearn.decomposition import PCA

pca=PCA(n_components=2)
X2D=pca.fit_transform(X)

將 PCA 轉化器應用於數據集後,可使用components_訪問每個主成分(注意,它返回以 PC 做爲水平向量的矩陣,所以,若是咱們想要得到第一個主成分則能夠寫成pca.components_.T[:,0])。

方差解釋率(Explained Variance Ratio)
另外一個很是有用的信息是每一個主成分的方差解釋率,可經過explained_variance_ratio_變量得到。它表示位於每一個主成分軸上的數據集方差的比例。例如,讓咱們看一下圖 8-2 中表示的三維數據集前兩個份量的方差解釋率:

>>> print(pca.explained_variance_ratio_)
array([0.84248607, 0.14631839])

這代表,84.2% 的數據集方差位於第一軸,14.6% 的方差位於第二軸。第三軸的這一比例不到1.2%,所以能夠認爲它可能沒有包含什麼信息

選擇正確的維度
一般咱們傾向於選擇加起來到方差解釋率可以達到足夠佔比(例如 95%)的維度的數量,而不是任意選擇要下降到的維度數量。固然,除非您正在爲數據可視化而下降維度 -- 在這種狀況下,您一般但願將維度下降到 2 或 3。

下面的代碼在不降維的狀況下進行 PCA,而後計算出保留訓練集方差 95% 所需的最小維數:

pca=PCA()
pac.fit(X)
cumsum=np.cumsum(pca.explained_variance_ratio_)
d=np.argmax(cumsum>=0.95)+1

你能夠設置n_components = d並再次運行 PCA。可是,有一個更好的選擇:不指定你想要保留的主成分個數,而是將n_components設置爲 0.0 到 1.0 之間的浮點數,代表您但願保留的方差比率:

pca=PCA(n_components=0.95)
X_reduced=pca.fit_transform(X)

另外一種選擇是畫出方差解釋率關於維數的函數(簡單地繪製cumsum)。曲線中一般會有一個肘部,方差解釋率中止快速增加。您能夠將其視爲數據集的真正的維度。在這種狀況下,您能夠看到將維度下降到大約100個維度不會失去太多的可解釋方差。

clipboard.png

PCA 壓縮
顯然,在降維以後,訓練集佔用的空間要少得多。例如,嘗試將 PCA 應用於 MNIST 數據集,同時保留 95% 的方差。你應該發現每一個實例只有 150 多個特徵,而不是原來的 784 個特徵。所以,儘管大部分方差都保留下來,但數據集如今還不到其原始大小的 20%!這是一個合理的壓縮比率,您能夠看到這能夠如何極大地加快分類算法(如 SVM 分類器)的速度。

經過應用 PCA 投影的逆變換,也能夠將縮小的數據集解壓縮回 784 維。固然這並不會返回給你最原始的數據,由於投影丟失了一些信息(在5%的方差內),但它可能很是接近原始數據。原始數據和重構數據之間的均方距離(壓縮而後解壓縮)被稱爲重構偏差(reconstruction error)。例如,下面的代碼將 MNIST 數據集壓縮到 154 維,然後使用inverse_transform()方法將其解壓縮回 784 維。圖 8-9 顯示了原始訓練集(左側)的幾位數字在壓縮並解壓縮後(右側)的對應數字。您能夠看到有輕微的圖像質量下降,但數字仍然大部分無缺無損。

pca=PCA(n_components=154)
X_mnist_reduced=pca.fit_transform(X_mnist)
X_mnist_recovered=pca.inverse_transform(X_mnist_reduced)

clipboard.png

PCA逆變換公式,回退到原來的數據維度

$$ X_{recovered} = X_{d-proj} \cdot W_d^T $$

增量 PCA(Incremental PCA)
先前 PCA 實現的一個問題是它須要在內存中處理整個訓練集以便 SVD 算法運行。幸運的是,咱們已經開發了增量 PCA(IPCA)算法:您能夠將訓練集分批,並一次只對一個批量使用 IPCA 算法。這對大型訓練集很是有用,而且能夠在線應用 PCA(即在新實例到達時即時運行)。

下面的代碼將 MNIST 數據集分紅 100 個小批量(使用 NumPy 的array_split()函數),並將它們提供給 Scikit-Learn 的IncrementalPCA類,以將 MNIST 數據集的維度下降到 154 維(就像之前同樣)。請注意,您必須對每一個最小批次調用partial_fit()方法,而不是對整個訓練集使用fit()方法

from sklearn.decomposition import IncrementalPCA

n_batches=100
inc_pca=IncrementalPCA(n_components=154)
for X_batch in np.array_spplit(X_mnist,n_batches): #分100批次數據
    inc_pca.partial_fit(X_batch)    # 必須批次調用partial_fit()方法
X_mnist_reduced=inc_pca.transform(X_mnist)

或者,您可使用 NumPy 的memmap類,它容許您操做存儲在磁盤上二進制文件中的大型數組,就好像它徹底在內存中;該類僅在須要時加載內存中所需的數據。因爲增量 PCA 類在任什麼時候間內僅使用數組的一小部分,所以內存使用量仍受到控制。這能夠調用一般的fit()方法,以下面的代碼所示:

X_mm=np.memmap(filename,dtype='float32',mode='readonly',shape=(m,n))
batch_size=m//n_batches
inc_pca=IncrementalPCA(n_components=154,batch_size=batch_size)
inc_pca.fit(X_mm)

隨機 PCA(Randomized PCA)
Scikit-Learn 提供了另外一種執行 PCA 的選擇,稱爲隨機 PCA。這是一種隨機算法,能夠快速找到前d個主成分的近似值。它的計算複雜度是O(m × d^2) + O(d^3),而不是O(m × n^2) + O(n^3),因此當d遠小於n時,它比以前的算法快得多。

rnd_pca=PCA(n_components=154,svd_solver='randomized')
X_reduced=rnd_pca.fit_transform(X_mnist)

核 PCA(Kernel PCA)

例如,下面的代碼使用 Scikit-Learn 的KernelPCA類來執行帶有 RBF 核的 kPCA

from sklearn.decomposition import KernelPCA

rbf_pca=KernelPCA(n_components=2,kernel='rbf',gamma=0.04)
X_reduced=rbf_pca.fit_transform(X)

clipboard.png

選擇一種核並調整超參數
因爲 kPCA 是無監督學習算法,所以沒有明顯的性能指標能夠幫助您選擇最佳的核方法和超參數值。可是,降維一般是監督學習任務(例如分類)的準備步驟,所以您能夠簡單地使用網格搜索來選擇可讓該任務達到最佳表現的核方法和超參數。例如,下面的代碼建立了一個兩步的流水線,首先使用 kPCA 將維度降至兩維,而後應用 Logistic 迴歸進行分類。而後它使用Grid SearchCV爲 kPCA 找到最佳的核和gamma值,以便在最後得到最佳的分類準確性:

from sklearn.model_selection import GridSearchCV from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline
clf = Pipeline([
        ("kpca", KernelPCA(n_components=2)),
        ("log_reg", LogisticRegression())
])
param_grid = [{
        "kpca__gamma": np.linspace(0.03, 0.05, 10),
        "kpca__kernel": ["rbf", "sigmoid"]
    }]
grid_search = GridSearchCV(clf, param_grid, cv=3)
grid_search.fit(X, y)

你能夠經過調用best_params_變量來查看使模型效果最好的核和超參數:

>>> print(grid_search.best_params_)
{'kpca__gamma': 0.043333333333333335, 'kpca__kernel': 'rbf'}

另外一種徹底爲非監督的方法,是選擇產生最低重建偏差的核和超參數。可是,重建並不像線性 PCA 那樣容易。這裏是緣由:圖 8-11 顯示了原始瑞士捲 3D 數據集(左上角),而且使用 RBF 核應用 kPCA 後生成的二維數據集(右上角)。因爲核技巧,這在數學上等同於使用特徵映射φ將訓練集映射到無限維特徵空間(右下),而後使用線性 PCA 將變換的訓練集投影到 2D。請注意,若是咱們能夠在縮減空間中對給定實例實現反向線性 PCA 步驟,則重構點將位於特徵空間中,而不是位於原始空間中(例如,如圖中由x表示的那樣)。因爲特徵空間是無限維的,咱們不能找出重建點,所以咱們沒法計算真實的重建偏差。幸運的是,能夠在原始空間中找到一個貼近重建點的點。這被稱爲重建前圖像(reconstruction pre-image)。一旦你有這個前圖像,你就能夠測量其與原始實例的平方距離。而後,您能夠選擇最小化重建前圖像錯誤的核和超參數。

clipboard.png

您可能想知道如何進行這種重建。一種解決方案是訓練一個監督迴歸模型,將預計實例做爲訓練集,並將原始實例做爲訓練目標。若是您設置了fit_inverse_transform = True,Scikit-Learn 將自動執行此操做,代碼以下所示:

rbf_pca = KernelPCA(n_components = 2, kernel="rbf", gamma=0.0433,fit_inverse_transform=True)
X_reduced = rbf_pca.fit_transform(X)
X_preimage = rbf_pca.inverse_transform(X_reduced)
概述:默認條件下,fit_inverse_transform = False而且KernelPCA沒有inverse_tranfrom()方法。這種方法僅僅當fit_inverse_transform = True的狀況下才會建立。

你能夠計算重建前圖像偏差:

>>> from sklearn.metrics import mean_squared_error
>>> mean_squared_error(X, X_preimage) 32.786308795766132

如今你可使用交叉驗證的方格搜索來尋找能夠最小化重建前圖像偏差的核方法和超參數。

LLE

局部線性嵌入(Locally Linear Embedding)是另外一種很是有效的非線性降維(NLDR)方法。這是一種流形學習技術,不依賴於像之前算法那樣的投影。簡而言之,LLE 首先測量每一個訓練實例與其最近鄰(c.n.)之間的線性關係,而後尋找能最好地保留這些局部關係的訓練集的低維表示(稍後會詳細介紹) 。這使得它特別擅長展開扭曲的流形,尤爲是在沒有太多噪音的狀況下。

例如,如下代碼使用 Scikit-Learn 的LocallyLinearEmbedding類來展開瑞士捲。獲得的二維數據集如圖所示。正如您所看到的,瑞士捲被徹底展開,實例之間的距離保存得很好。可是,距離不能在較大範圍內保留的很好:展開的瑞士捲的左側被擠壓,而右側的部分被拉長。儘管如此,LLE 在對流形建模方面作得很是好。

from sklearn.manifold import LocallyLinearEmbedding

lle=LocallyLinearEmbedding(n_components=2,n_neighbors=10)
X_reduced=lle.fit_transform(X)

clipboard.png

其餘降維方法

多維縮放(MDS)在嘗試保持實例之間距離的同時下降了維度

Isomap 經過將每一個實例鏈接到最近的鄰居來建立圖形,而後在嘗試保持實例之間的測地距離時下降維度。

t-分佈隨機鄰域嵌入(t-Distributed Stochastic Neighbor Embedding,t-SNE)能夠用於下降維​​度,同時試圖保持類似的實例臨近並將不類似的實例分開。它主要用於可視化,尤爲是用於可視化高維空間中的實例(例如,能夠將MNIST圖像降維到 2D 可視化)。

線性判別分析(Linear Discriminant Analysis,LDA)其實是一種分類算法,但在訓練過程當中,它會學習類之間最有區別的軸,而後使用這些軸來定義用於投影數據的超平面。LDA 的好處是投影會盡量地保持各個類之間距離,因此在運行另外一種分類算法(如 SVM 分類器)以前,LDA 是很好的降維技術。

clipboard.png

參考資料:【1】《Sklearn與TensorFlow機器學習實用指南》

相關文章
相關標籤/搜索