PCA原理及python實戰

背景

在許多領域的研究與應用中,通常需要對含有多個變量的數據進行觀測,收集大量數據後進行分析尋找規律。多變量大數據集無疑會爲研究和應用提供豐富的信息,但是也在一定程度上增加了數據採集的工作量。更重要的是在很多情形下,許多變量之間可能存在相關性,從而增加了問題分析的複雜性。如果分別對每個指標進行分析,分析往往是孤立的,不能完全利用數據中的信息,因此盲目減少指標會損失很多有用的信息,從而產生錯誤的結論。

因此需要找到一種合理的方法,在減少需要分析的指標同時,儘量減少原指標包含信息的損失,以達到對所收集數據進行全面分析的目的。由於各變量之間存在一定的相關關係,因此可以考慮將關係緊密的變量變成儘可能少的新變量,使這些新變量是兩兩不相關的,那麼就可以用較少的綜合指標分別代表存在於各個變量中的各類信息。主成分分析與因子分析就屬於這類降維算法。

這裏要說明的是,PCA與之前說的LDA不同,PCA是無監督學習。

 

PCA引入

PCA(Principal Component Analysis),即主成分分析方法,是一種使用最廣泛的數據降維算法。PCA的主要思想是將n維特徵映射到k維上,這k維是全新的正交特徵也被稱爲主成分,是在原有n維特徵的基礎上重新構造出來的k維特徵。PCA的工作就是從原始的空間中順序地找一組相互正交的座標軸,新的座標軸的選擇與數據本身是密切相關的。其中,第一個新座標軸選擇是原始數據中方差最大的方向,第二個新座標軸選取是與第一個座標軸正交的平面中使得方差最大的,第三個軸是與第1,2個軸正交的平面中方差最大的。依次類推,可以得到n個這樣的座標軸。通過這種方式獲得的新的座標軸,我們發現,大部分方差都包含在前面k個座標軸中,後面的座標軸所含的方差幾乎爲0。於是,我們可以忽略餘下的座標軸,只保留前面k個含有絕大部分方差的座標軸。事實上,這相當於只保留包含絕大部分方差的維度特徵,而忽略包含方差幾乎爲0的特徵維度,實現對數據特徵的降維處理。

爲了更好的理解,這裏引入B站一個UP主的說法:一箇中心兩個基本點:

一箇中心--原始特徵空間的重構,從線形相關-線形無關

兩個基本點:最大投影方差和最小重構舉例

爲了更好的引入,我們先說一些預備知識

預備知識

鑑於公示編輯太麻煩只能手書了

矩陣:

這裏H叫做中心矩陣

性質我們還是手書說一下

 

向量:

對於一個向量a,只需要關心它的方向和大小(長短),即終點座標-起點座標,不需要關心位置。

假設有兩個向量a,b

a=(a1,a2)

b= (b1,b2)

根據高中知識可以得到這兩個向量的內積公式

a∙ b =a1b1+a2b2=‖ a ‖ ‖ b ‖cosa

如果b是單位向量,那麼a∙ b =a1b1+a2b2=‖ a ‖ cosa

SVD

特徵值分解和奇異值分解在機器學習中都是很常見的矩陣分解算法。兩者有着很緊密的關係,特徵值分解和奇異值分解的目的都是一樣,就是提取出一個矩陣最重要的特徵。

如果一個向量v是矩陣A的特徵向量,將一定可以表示成下面的形式:

A\upsilon =\lambda \upsilon       λ是特徵向量v對應的特徵值,一個矩陣的一組特徵向量是一組正交向量。

那麼爲什麼一個向量和一個數相乘的效果與一個矩陣和一個向量相乘的效果是一樣的呢?

矩陣A與向量v相乘,本質上是對向量v進行了一次線性變換(旋轉或拉伸),而該變換的效果爲常數λ乘以向量v。當我們求特徵值與特徵向量的時候,就是爲了求矩陣A能使哪些向量(特徵向量)只發生伸縮變換,而變換的程度可以用特徵值λ表示。

特徵值與特徵向量的幾何意義

一個矩陣其實就是一個線性變換,因爲一個矩陣乘以一個向量後得到的向量,其實就相當於將這個向量進行了線性變換。比如說下面的這個矩陣:

M=\left [ \begin{matrix} 3 & 0\\ 0& 1 \end{matrix} \right ]

它其實對應的線性變換是圖2的形式:

 

因爲這個矩陣M乘以一個向量(x,y)的結果是:

上面的矩陣是對稱的,所以這個變換是一個對x、y軸的方向一個拉伸變換(每一個對角線上的元素將會對一個維度進行拉伸變換,當值大於1時是拉伸,當值小於1時是縮短),如圖2所示。當矩陣不是對稱的時候,假如說矩陣是下面的樣子:

M=\left [ \begin{matrix} 1 & 1\\ 0& 1 \end{matrix} \right ]

它所描述的變換是下面的樣子:

這其實是在平面上對一個軸進行的拉伸變換,如圖3藍色的箭頭所示,藍色的箭頭是一個最主要的變換方向(變換的方向可能不止一個)。如果想要描述好一個變換,那我們就需要描述好這個變換主要的變化方向。

 

特徵值分解

對於矩陣A,有一組特徵向量v,將這組向量進行正交化單位化,就能得到一組正交單位向量。特徵值分解,就是將矩陣A分解爲如下式:

A=Q\Sigma Q^{-1}

其中,Q是矩陣A的特徵向量組成的矩陣,\Sigma則是一個對角陣,對角線上的元素就是特徵值。

我們來分析一下特徵值分解的式子,分解得到的Σ矩陣是一個對角矩陣,裏面的特徵值是由大到小排列的,這些特徵值所對應的特徵向量就是描述這個矩陣變換方向(從主要的變化到次要的變化排列)。

當矩陣是高維的情況下,那麼這個矩陣就是高維空間下的一個線性變換,這個線性變換可能沒法通過圖片來表示,但是可以想象,這個變換也同樣有很多的變化方向,我們通過特徵值分解得到的前N個特徵向量,就對應了這個矩陣最主要的N個變化方向。我們利用這前N個變化方向,就可以近似這個矩陣變換。也就是之前說的:提取這個矩陣最重要的特徵。

總結:特徵值分解可以得到特徵值與特徵向量,特徵值表示的是這個特徵到底有多麼重要,而特徵向量表示這個特徵是什麼,可以將每一個特徵向量理解爲一個線性的子空間,我們可以利用這些線性的子空間幹很多事情。不過,特徵值分解也有很多的侷限,比如說變換的矩陣必須是方陣。

 

特徵值分解的例子

這裏我們用一個簡單的方陣來說明特徵值分解的步驟。我們的方陣A定義爲:

首先,由方陣A的特徵方程,求出特徵值。

特徵值爲\lambda=2和1(2是重數)

然後,把每個特徵值λ帶入線性方程組(A-\lambda E)=0,求出特徵向量。

當λ=2時,解線性方程組 (A-2 E)=0

解得x1=0 , x2=0

特徵向量爲:

當λ=1時,解線性方程組(A- E)=0

特徵向量爲:

最後,方陣A的特徵值分解爲:

我們前面講了很多特徵值、特徵向量和特徵值分解,而且基於我們以前學習的線性代數知識,利用特徵值分解提取特徵矩陣是一個容易理解且便於實現的方法。但是爲什麼還存在奇異值分解呢?特徵值分解最大的問題是隻能針對方陣,即n*n的矩陣。而在實際的應用中,我們分解的大部分都不是方陣。

爲了解決這個問題,就引入了奇異值分解

奇異值分解是一個能適用於任意矩陣的一種分解的方法,對於任意矩陣A總是存在一個奇異值分解:A=Q\Sigma V^{T}

假設A是一個m*n的矩陣,那麼得到的U是一個m*m的方陣,U裏面的正交向量被稱爲左奇異向量。Σ是一個m*n的矩陣,Σ除了對角線其它元素都爲0,對角線上的元素稱爲奇異值。V^{T}是v的轉置矩陣,是一個n*n的矩陣,它裏面的正交向量被稱爲右奇異值向量。而且一般來講,我們會將Σ上的值按從大到小的順序排列。上面矩陣的維度變化可以參照圖4所示。

如何求得左奇異向量、右奇異向量和奇異值呢?

首先,我們用矩陣A的轉置乘以A,得到一個方陣,用這樣的方陣進行特徵分解,得到的特徵值和特徵向量滿足下面的等式:

(A^{T}A)\upsilon =\lambda\upsilon

這裏的\upsilon就是我們要求的右奇異向量。

其次,我們將A和A的轉置做矩陣的乘法,得到一個方陣,用這樣的方陣進行特徵分解,得到的特徵和特徵向量滿足下面的等式:

(AA^{T})u =\lambda u  這裏的 u就是左奇異向量。

上面我們說 A^{T}A的特徵向量組成的矩陣就是我們SVD中的V矩陣,而AA^{T}的特徵向量組成的就是我們SVD中的U矩陣

上式證明中使用了U^{T}U=I,\Sigma ^{T}\Sigma=\Sigma ^{2}

可以看出 A^{T}A的特徵向量組成的矩陣就是我們SVD中的V矩陣,而AA^{T}的特徵向量組成的就是我們SVD中的U矩陣

奇異值求法有兩種:

a) 第一種:

b)第二種:

通過上面*式的證明,我們還可以看出,特徵值矩陣等於奇異值矩陣的平方,也就是說特徵值和奇異值滿足如下關係:

\sigma _{i}=\sqrt{\lambda _{i}},其中\sigma _{i}就是奇異值,奇異值跟特徵值類似,在矩陣Σ中也是從大到小排列。

 

SVD計算舉例

這裏我們用一個簡單的矩陣來說明奇異值分解的步驟。我們的矩陣A定義爲:

 

PCA  python實戰

還是使用鶯尾花數據集

#計算協方差矩陣
mean_vec = np.mean(X_std, axis=0)
cov_mat = (X_std - mean_vec).T.dot((X_std - mean_vec)) / (X_std.shape[0]-1)
print('協方差矩陣 \n%s' %cov_mat)

這裏X_std是做好了數據標準化處理

協方差矩陣 
[[ 1.00671141 -0.11010327  0.87760486  0.82344326]
 [-0.11010327  1.00671141 -0.42333835 -0.358937  ]
 [ 0.87760486 -0.42333835  1.00671141  0.96921855]
 [ 0.82344326 -0.358937    0.96921855  1.00671141]]
#計算特徵向量與特徵值
cov_mat = np.cov(X_std.T)

eig_vals, eig_vecs = np.linalg.eig(cov_mat)

print('特徵向量 \n%s' %eig_vecs)
print('\n特徵值 \n%s' %eig_vals)
特徵向量 
[[ 0.52237162 -0.37231836 -0.72101681  0.26199559]
 [-0.26335492 -0.92555649  0.24203288 -0.12413481]
 [ 0.58125401 -0.02109478  0.14089226 -0.80115427]
 [ 0.56561105 -0.06541577  0.6338014   0.52354627]]

特徵值 
[2.93035378 0.92740362 0.14834223 0.02074601]
#取前兩個特徵值
matrix_w = np.hstack((eig_pairs[0][1].reshape(4,1),
                      eig_pairs[1][1].reshape(4,1)))

print('Matrix W:\n', matrix_w)
Matrix W:
 [[ 0.52237162 -0.37231836]
 [-0.26335492 -0.92555649]
 [ 0.58125401 -0.02109478]
 [ 0.56561105 -0.06541577]]
Y = X_std.dot(matrix_w)

看一下原始數據分佈【取其中兩個維度】和轉換後的數據分佈

預測

X_train,X_test,y_train, y_test = train_test_split(X,y, test_size=0.3, random_state=0)
classifier=LogisticRegression()
classifier.fit(X_train,y_train)
predictions=classifier.predict(X_test)

acc = accuracy_score(y_test, predictions)
print(acc)

X_train_n,X_test_n,y_train_n, y_test_n = train_test_split(Y,y, test_size=0.3, random_state=0)
classifier.fit(X_train_n,y_train_n)
predictions_n=classifier.predict(X_test_n)

acc_n = accuracy_score(y_test_n, predictions_n)
print(acc_n)

acc=0.8888888888888888

acc_n=0.8666666666666667

發現在維度減少一半的情況下,準確率沒有很明顯的下降

完整代碼:https://github.com/Andyszl/Feature_Engineering/blob/master/PCA.py

 

參考:https://zhuanlan.zhihu.com/p/37777074

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章