【翻譯】Sklearn 與 TensorFlow 機器學習實用指南 —— 第8章 降維(下)

2021-02-21 機器學習研究組訂閱
主成分分析(PCA)

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

保留(最大)方差

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

圖 8-7 選擇投射到哪一個子空間

選擇保持最大方差的軸看起來是合理的,因為它很可能比其他投影損失更少的信息。證明這種選擇的另一種方法是,選擇這個軸使得將原始數據集投影到該軸上的均方距離最小。這是就 PCA 背後的思想,相當簡單。

主成分(Principle Componets)

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

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

那麼如何找到訓練集的主成分呢?幸運的是,有一種稱為奇異值分解(SVD)的標準矩陣分解技術,可以將訓練集矩陣X分解為三個矩陣U·Σ·V^T的點積,其中V^T包含我們想要的所有主成分,如公式 8-1 所示。

公式 8-1 主成分矩陣

下面的 Python 代碼使用了 Numpy 提供的svd()函數獲得訓練集的所有主成分,然後提取前兩個 PC:

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維。選擇這個超平面可以確保投影將保留儘可能多的方差。例如,在圖 8-2 中,3D 數據集被投影到由前兩個主成分定義的 2D 平面,保留了大部分數據集的方差。因此,2D 投影看起來非常像原始 3D 數據集。

為了將訓練集投影到超平面上,可以簡單地通過計算訓練集矩陣X和Wd的點積,Wd定義為包含前d個主成分的矩陣(即由V^T的前d列組成的矩陣),如公式 8-2 所示。

公式 8-2 將訓練集投影到d維空間

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

W2=V.T[:,:2]X2D=X_centered.dot(W2)

好了你已經知道這個東西了!你現在已經知道如何給任何一個數據集降維而又能儘可能的保留原數據集的方差了。

使用 Scikit-Learn

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

from sklearn.decomposition import PCApca=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;參見圖 8-8)。曲線中通常會有一個肘部,方差解釋率停止快速增長。您可以將其視為數據集的真正的維度。在這種情況下,您可以看到將維度降低到大約100個維度不會失去太多的可解釋方差。

圖 8-8 可解釋方差關於維數的函數


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)

圖 8-9 MNIST 保留 95 方差的壓縮

逆變換的公式如公式 8-3 所示

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

增量 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 IncrementalPCAn_batches=100inc_pca=IncrementalPCA(n_components=154)for X_batch in np.array_spplit(X_mnist,n_batches):    inc_pca.partial_fit(X_batch)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_batchesinc_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)

在第 5 章中,我們討論了核技巧,一種將實例隱式映射到非常高維空間(稱為特徵空間)的數學技術,讓支持向量機可以應用於非線性分類和回歸。回想一下,高維特徵空間中的線性決策邊界對應於原始空間中的複雜非線性決策邊界。

事實證明,同樣的技巧可以應用於 PCA,從而可以執行複雜的非線性投影來降低維度。這就是所謂的核 PCA(kPCA)。它通常能夠很好地保留投影后的簇,有時甚至可以展開分布近似於扭曲流形的數據集。

例如,下面的代碼使用 Scikit-Learn 的KernelPCA類來執行帶有 RBF 核的 kPCA(有關 RBF 核和其他核的更多詳細信息,請參閱第 5 章):

from sklearn.decomposition import KernelPCArbf_pca=KernelPCA(n_components=2,kernel='rbf',gamma=0.04)X_reduced=rbf_pca.fit_transform(X)

圖 8-10 展示了使用線性核(等同於簡單的使用 PCA 類),RBF 核,sigmoid 核(Logistic)將瑞士卷降到 2 維。

圖 8-10 使用不同核的 kPCA 將瑞士卷降到 2 維

選擇一種核並調整超參數

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

from sklearn.model_selection import GridSearchCV from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipelineclf = 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)。一旦你有這個前圖像,你就可以測量其與原始實例的平方距離。然後,您可以選擇最小化重建前圖像錯誤的核和超參數。

圖 8-11 核 PCA 和重建前圖像誤差

您可能想知道如何進行這種重建。一種解決方案是訓練一個監督回歸模型,將預計實例作為訓練集,並將原始實例作為訓練目標。如果您設置了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類來展開瑞士卷。得到的二維數據集如圖 8-12 所示。正如您所看到的,瑞士卷被完全展開,實例之間的距離保存得很好。但是,距離不能在較大範圍內保留的很好:展開的瑞士卷的左側被擠壓,而右側的部分被拉長。儘管如此,LLE 在對流形建模方面做得非常好。

from sklearn.manifold import LocallyLinearEmbeddinglle=LocallyLinearEmbedding(n_components=2,n_neighbors=10)X_reduced=lle.fit_transform(X)

圖 8-12 使用 LLE 展開瑞士卷

這是LLE的工作原理:首先,對於每個訓練,該算法識別其最近的k個鄰居(在前面的代碼中k = 10中),然後嘗試重構為這些鄰居的線性函數。更特殊的,它假設如果 不是的k個最近鄰之一,就找到權重從而使之間的平方距離儘可能的小。因此,LLE 的第一步是方程 8-4 中描述的約束優化問題,其中W是包含所有權重的權重矩陣。第二個約束簡單地對每個訓練實例的權重進行歸一化。

公式 8-4 LLE 第一步:對局部關係進行線性建模

在這步之後,權重矩陣(包含權重對訓練實例的線形關係進行編碼。現在第二步是將訓練實例投影到一個d維空間(d < n)中去,同時儘可能的保留這些局部關係。如果在這個d維空間的圖像,那麼我們想要之間的平方距離儘可能的小。這個想法讓我們提出了公式8-5中的非限制性優化問題。它看起來與第一步非常相似,但我們要做的不是保持實例固定並找到最佳權重,而是恰相反:保持權重不變,並在低維空間中找到實例圖像的最佳位置。請注意,Z是包含所有的矩陣。

公式 8-5 LLE 第二步:保持關係的同時進行降維

Scikit-Learn 的 LLE 實現具有如下的計算複雜度:查找k個最近鄰為O(m log(m) n log(k)),優化權重為O(m n k^3),建立低維表示為O(d m^2)。不幸的是,最後一項m^2使得這個算法在處理大數據集的時候表現較差。


其他降維方法

還有很多其他的降維方法,Scikit-Learn 支持其中的好幾種。這裡是其中最流行的:

多維縮放(MDS)在嘗試保持實例之間距離的同時降低了維度(參見圖 8-13)

Isomap 通過將每個實例連接到最近的鄰居來創建圖形,然後在嘗試保持實例之間的測地距離時降低維度。

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

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

圖 8-13 使用不同的技術將瑞士卷降維至 2D

想要了解更多資訊,請掃描下方二維碼,關注機器學習研究會

                                          

轉自:人工智慧愛好者社區

相關焦點