【火爐煉AI】機器學習053-資料降維絕招-PCA和核PCA

煉丹老頑童發表於2019-03-03

【火爐煉AI】機器學習053-資料降維絕招-PCA和核PCA

(本文所使用的Python庫和版本號: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

主成分分析(Principal Component Analysis, PCA)可以說是資料降維的絕招,不僅在人口統計學,數量地理學,分子動力學模擬,數學建模等領域有著重要的應用,而且在機器學習領域,PCA也是非常常用的一種資料降維方法。

首先來理解什麼叫資料降維:假設有一個專案,要從外觀上來判斷某個人是男人還是女人,我們可以提取各種各樣的特徵,比如身高,一般而言,男人的身高要比女人高,但也有特例,比如頭髮長度,有無鬍鬚,臉型,衣服顏色,身材,體重,膚色,BMI指標。。。。等等,如果有需要,你可以為這個專案提取幾百甚至幾千個特徵,假如你一口氣提取了200個特徵,每一個特徵都反映了一個人的某一個方面的資訊,那麼,用機器學習的方法對這200個特徵進行建模,並用訓練集來訓練,則會非常耗時,而且得到的模型結構龐大,故而我們需要減少特徵數量,但是我們不希望減少特徵數量的同時把重要的資訊給弄丟了。將特徵數量從幾百上千降低到幾十的過程就是資料降維。

在上面這個專案中,有很多特徵之間是由關聯的,比如BMI指標就是身高/體重的平方,那麼很明顯,BMI指標一個特徵就包括了身高和體重兩個特徵,BMI和這兩個指標之間具有非常強的相關性。同理,200個特徵之間也可能有很多變數之間具有相關性,所以這些相關特徵之間所反映的資訊是一樣的,PCA的作用就是對所有的這200個特徵,去除相關性非常強的特徵,建立儘可能少的新特徵,使得這些新特徵之間是兩兩不相關,並且這些新特徵在反應專案的資訊方面會盡可能保持原有的資訊。

關於PCA的理論推導和更深層次的數學邏輯,請參考博文PCA演算法

1. 用PCA對資料集降維

為了看到降維效果,此處我們用隨機數自動生成一個資料集,前兩個特徵向量都是隨機數,後三個特徵向量是前兩個特徵計算組合而來。如下為程式碼

# 假如某個專案有5個特徵,這五個特徵分別為:
f1=np.random.normal(size=250)
f2=np.random.normal(size=250)
# 後面的三個特徵是前面兩個特徵演變而來,即與前面兩特徵具有很強的相關性
f3=2*f1+3*f2
f4=4*f1-f2
f5=f3+2*f4
複製程式碼

很多時候,我們要檢視資料集中各特徵向量之間的相關性,如下

# 將這些特徵組合成資料集
dataset_X=np.c_[f1,f2,f3,f4,f5]
# 計算各特徵列之間的相關係數
df=pd.DataFrame(dataset_X,columns=[`f1`,`f2`,`f3`,`f4`,`f5`])
print(df.corr())
複製程式碼

————————————-輸———出——————————–

     f1        f2        f3        f4        f5
複製程式碼

f1 1.000000 -0.002496 0.528931 0.966354 0.994370
f2 -0.002496 1.000000 0.847342 -0.259627 0.103485
f3 0.528931 0.847342 1.000000 0.292844 0.615884
f4 0.966354 -0.259627 0.292844 1.000000 0.933656
f5 0.994370 0.103485 0.615884 0.933656 1.000000

——————————————–完————————————-

可以明顯看出,資料集中有很多特徵之間具有比較強的相關性,比如f1-f5,f1-f4等。

所以我們可以用PCA來進行降維:

# 可以看出f1-f5,f1-f4,f2-f3等之間具有強相關性,故而可以用PCA降維
from sklearn import decomposition
pca=decomposition.PCA()
pca.fit(dataset_X) # 用PCA降維
# 列印降維後的新特徵
variances=pca.explained_variance_
print(variances) # 可以理解成該特徵的重要性,後面三個數字非常小,即特徵不重要
複製程式碼

————————————-輸———出——————————–

[1.15552796e+02 1.14453854e+01 3.08872295e-31 8.39043564e-32
1.18268234e-32]

——————————————–完————————————-

# 故而可以為重要性設定一個閾值,小於該閾值的認為該特徵不重要,可刪除
thresh=0.8
useful_features=variances>thresh
print(useful_features) # 標記為True的表示重要特徵,要保留,False則刪除
複製程式碼

————————————-輸———出——————————–

[ True True False False False]

——————————————–完————————————-

一旦我們通過PCA進行了降維,就需要將原來的高維資料集轉換為低維資料集,然後用低維資料集來建模

useful_features_num=np.sum(useful_features) # 計算True的個數

# 進行PCA降維之後的新資料集為:
pca.n_components=useful_features_num # 即設定PCA的新特徵數量為n_components
new_dataset_X=pca.fit_transform(dataset_X)
print(`before PCA, dataset shape: `, dataset_X.shape)
print(`after PCA, dataset shape: `, new_dataset_X.shape)
複製程式碼

————————————-輸———出——————————–

before PCA, dataset shape: (250, 5)
after PCA, dataset shape: (250, 2)

——————————————–完————————————-

PCA的優點和缺點:

優點:1,對資料進行降維處理,我們可以對新求出的“主元”向量的重要性進行排序,根據需要取前面最重要的部分,將後面的維數省去,可以達到降維從而簡化模型或是對資料進行壓縮的效果。同時最大程度的保持了原有資料的資訊。

2,完全無引數限制,處理結果只與資料相關,而與使用者無關:在PCA的計算過程中完全不需要人為的設定引數或是根據任何經驗模型對計算進行干預,最後的結果只與資料相關,與使用者是獨立的。

缺點:1,PCA以線性方式工作,如果資料集不是以線性方式組織的,那麼PCA效果就很差,此時,我們可以根據先驗知識對資料預先進行非線性轉換,將非線性資料集轉換到相信空間中,這種分析方式叫做Kernel-PCA,也叫核PCA,它解決了PCA只能處理線性資料集的缺點,又結合一些先驗知識的約束,是目前比較流行的方法。

2,有的資料集的分佈並不滿足高斯分佈,在非高斯分佈的情況下,PCA得到的主要特徵可能並不是最優的,在尋找主要特徵時不能將方差作為衡量重要性的標準,此時要根據資料的分佈情況選擇合適的描述完全分佈的變數,根據一定的概率分佈公式來計算兩個特徵資料分佈的相關性,這種分析方式叫獨立主元分解(ICA)。

可以參考博文: 主成分分析(Principal components analysis)-最小平方誤差解釋

2. 用核PCA對資料集降維

前面提到,PCA雖好,但也不是放之四海而皆準,對於非線性方式組織的資料集,PCA方法可能效果較差,此時需要用核PCA方法來解決。

我們來看一個典型的非線性方式組織的資料集:

# 準備資料集
from sklearn.datasets import make_circles
dataset_X,dataset_y=make_circles(n_samples=500,factor=0.2,noise=0.04)
複製程式碼

該資料集的二維分佈圖為:

【火爐煉AI】機器學習053-資料降維絕招-PCA和核PCA

那麼如果我們用普通PCA對這個資料集進行降維,會得到什麼樣的結果了?

# 如果用普通的PCA來降維
from sklearn.decomposition import PCA
pca = PCA()
dataset_X_pca = pca.fit_transform(dataset_X)
print(dataset_X_pca.shape)
visual_2D_dataset(dataset_X_pca,dataset_y,`PCA transformed dataset`)
# 從圖中幾乎看不出PCA降維前後有啥區別
複製程式碼
【火爐煉AI】機器學習053-資料降維絕招-PCA和核PCA

如果用核PCA來降維,能夠將資料集變成線性可分,如下:

# 用核PCA方法來降維
from sklearn.decomposition import KernelPCA
kernel_pca = KernelPCA(kernel="rbf", fit_inverse_transform=True, gamma=10)
X_kernel_pca = kernel_pca.fit_transform(dataset_X)
print(X_kernel_pca.shape) # 2維特徵變成了465維,降維?增維?
visual_2D_dataset(X_kernel_pca[:,:2],dataset_y,`Kernel PCA transformed dataset`)
複製程式碼
【火爐煉AI】機器學習053-資料降維絕招-PCA和核PCA

很明顯,得到的新資料集便是線性可分。

話說,怎麼經過核PCA降維之後,原先的2個特徵怎麼增加到465個特徵?降維還是增維?呵呵。

那麼從這個核PCA得到的新資料集能夠返回到原來的資料集了?

# 如何從Kernel PCA得到的資料集反向計算出原始的資料集
dataset_X_inverse = kernel_pca.inverse_transform(X_kernel_pca)
print(dataset_X_inverse.shape)
visual_2D_dataset(dataset_X_inverse,dataset_y,`inversed dataset_X from KernelPCA`)
複製程式碼
【火爐煉AI】機器學習053-資料降維絕招-PCA和核PCA

可以看出,恢復之後的資料集的分佈情況和原始資料集的分佈情況相同。

########################小**********結###############################

1,對資料集進行降維可以很好地解決特徵數太多導致的訓練太耗時,模型結構臃腫的問題,降維方法有很多種,其中的PCA降維方式可以解決具有線性方式的資料集,而核PCA方法可以對具有非線性方式組織的資料集進行降維。

#################################################################

注:本部分程式碼已經全部上傳到(我的github)上,歡迎下載。

參考資料:

1, Python機器學習經典例項,Prateek Joshi著,陶俊傑,陳小莉譯

相關文章