PCA 主成分分析

前一篇提到的人臉識別中,我們在使用SVM支援向量機做人臉分類之前使用到PCA提取人臉資料中的主要成分,降低計算的維度,那麼具體PCA是如何提取的呢?下文了解一下。

PCA is a method to project data in a higher dimensional space into lower dimensional space by maximizing the variance of each dimension ——wiki

PCA is mostly used as a tool in exploratory data analysis and for making predictive models。 It is often used to visualize genetic distance and relatedness between populations。 PCA is either done by singular value decomposition of a design matrix or by doing the following 2 steps:

1。 calculating the data covariance( or correlation) matrix of the original data

2。 performing eigenvalue decomposition(特徵值分解) on the covariance matrix(協方差矩陣)。

——wiki

主成分分析,是一種統計方法,透過正交變換將一組可能存在相關性的變數轉換為一組線性不相關的變數,轉換後的這組變數叫主成分。對於這些成分,有些就顯得沒有那麼必要。那麼我們可以從這些成分中挑取比較有用的,而對於我們模型的訓練作用不大的可以去除掉。這個過程其實也是一個降維的過程,對高維度的資料集進行降維,減少一些資訊含量,保留主要的那些維度。主要的那些維度是指在這些維度上具有更大的差異,而對於那些差異化不大的特徵,有時候反而會影響我們的模型學習,而成為一種噪音,也會影響我們training時候的速度。所以在機器學習的過程中,去掉那些無關緊要的變數會使得我們的學習模型效率提高。

因此我們要找到那些差異化比較大的特徵,也就是說在表示它的特徵的這一系列數中,它的variance(方差:是在機率論和統計方差衡量隨機變數或一組資料時離散程度的度量)是比較大的,比較離散的,而對於那些不明顯的特徵,用資料來衡量他們的這種特徵程度的話,他們更容易聚集在一起,而不那麼離散,也就是variance的值很小。下面使用程式碼畫圖來形象地瞭解一下。

%matplotlib inline import numpy as npimport matplotlib。pyplot as pltimport seaborn as sns; sns。set()

rng=np。random。RandomState(1)X=np。dot(rng。rand(2,2),rng。rand(2,200))。Tplt。scatter(X[:,0],X[:,1])plt。axis(‘equal’)

先畫出散點圖,觀察一下我們的samples:

PCA 主成分分析

Numpy.dot

(a,b,out=None): do product of two arrays。

就是兩個矩陣的乘積,A矩陣的第一行乘以B矩陣的第一列所得的元素的和作為新矩陣C的第一列中第一行的數,A矩陣第二行乘以B矩陣第一列所得元素的和作為C矩陣的第一列中第二行的數,以此類推。

Numpy.random.RandomState.rand

(2,200) random values in a given shape, 產生一個2x200的矩陣。矩陣中的每一個element(元)都是隨機產生的介於0到1之間的一個數。

X=np.dot(rng.rand(2,2),rng.rand(2,200)).T

這個程式碼的意思是先隨機產生兩個點,然後再用這兩個點進行一個列變換(左乘行變換,右乘列變換)產生與左邊矩陣具有相同行,與右乘的矩陣右相同列的shape為2x200的新矩陣,可以知道這個新的矩陣中的每個點的x或y座標都不會大於2(1x1+1x1=2)。尾部的。T進行了矩陣的轉置,使得矩陣從2x200的shape變成200x2。

使用PCA來解出上圖中散點的主成分

,圖中每個點需要兩個引數(x,y座標)才能表達準確,那麼對於該圖上的點來說,number of components=2,有兩個成分。

from sklearn。decomposition import PCApca=PCA(n_components=2)pca。fit(X)

看起來只有三行程式碼,但是相關的計算幾乎全在裡面了。

Sklearn。decomposition。PCA(n_components=None, *, copy=True, whiten=False, svd_solver=‘auto’, tol=0。0, iterated_power=‘auto’, random_state=None)

PCA: principal component analysis(PCA)

上述程式碼中的主成分分析使用的是SVD(singular value decomposition,奇異值分解)方法。

Linear dimensionality reduction using Singular Value Decomposition of the data to project it to a lower dimensional space。

PCA skips less significant component。 Obviously, we can use SVD to find PCA by truncating the less important basis vectors in the original SVD matrix。

PCA方法尋找主成分的過程,透過採用SVD找到所有的基礎向量之後,再把不怎麼重要的基礎向量裁斷掉。所以這裡需要先使用SVD找到矩陣X的基礎向量。

從Sklearn。decomposition。PCA()的原始碼中抽出

計算基礎向量

部分:

scipy。linalg。svd(a, full_matrices=True, compute_uv=True, overwrite_a=False, check_finite=True, lapack_driver=‘gesdd’)

Singular Value Decomposition。 奇異值分解。

A

:(M,N) array_like, matrix to decompose,一個要分解的(m,n)矩陣。

SVD Return(返回值):

U

: unitary matrix having left singular vectors(左奇異矩陣) as columns。 Of shape (M,M) or (M,K), depending on full_matrices。

S

: ndarray, the singular values(奇異值), sorted in non-increasing order。 Of shape(K), with K=min(M,N)。

Wh

: Unitary matrix having right singular vectors(右奇異矩陣) as rows。 Of shape (N,N) or (k,N)。

任何一個矩陣A都可以拆分成如下的三個矩陣相乘。

PCA 主成分分析

PCA 主成分分析

(讀作sigma)符號通常也用S來代替,下文中這兩者通用哈。

上式中

U

V

分別是AAT和ATA的正交特徵矩陣,

S

是擁有r個元素的對角矩陣,對角線上的元素的值等於AAT或ATA的正特徵值的根,其中AAT和ATA擁有同樣的正特徵值。

S長下面這樣:

PCA 主成分分析

加上維度完整地寫出如下形式。

PCA 主成分分析

PCA 主成分分析

上面拆分過程中,A矩陣一次性可以做到的變換可以分為

U

S

V

T三個步驟完成,其每個步驟的作用如下圖所示:

PCA 主成分分析

V

T: orthogonal(rotation), 正交化(旋轉);

Sigma

: diagonal(scale),對角化(調整比例);

U

:(orthogonal)(rotation),正交化(旋轉)。

其中U和V為正交矩陣,滿足如下條件:

PCA 主成分分析

已經明瞭U,V,S大致的計算方法,下面使用具體的矩陣來計算體驗一下這個過程,找點感覺。

假設A矩陣如下:

PCA 主成分分析

下面將A矩陣分解為U,V,S:

計算U

時,因為U是AAT的正交特徵矩陣,所以下面要先計算AAT,然後待求得特徵值之後,再計算它的正交矩陣:

PCA 主成分分析

計算特徵值:

PCA 主成分分析

得AAT的特徵值為

PCA 主成分分析

代入特徵值,並計算得特徵向量和矩陣為:

PCA 主成分分析

由於V是ATA的正交特徵矩陣,那麼可以使用相同的方法

計算V

先求得ATA的特徵值為

PCA 主成分分析

對應的特徵向量和矩陣為:

PCA 主成分分析

由上面得到的U和V,可以透過下式

求解得S

PCA 主成分分析

把所求得的矩陣代回下式計算

驗證

,最後等於A,沒有問題。

PCA 主成分分析

至於為什麼可以透過這樣的方式計算得到左奇異矩陣,右奇異矩陣和奇異值,相關證明自己找文獻哈。

下面對使用SVD方法找出的PCA結果畫圖檢視一下。

def draw_vector(v1,v0,ax=None): ax=ax or plt。gca() arrowprops=dict(arrowstyle=‘<-’, linewidth=2, shrinkA=0,shrinkB=0,color=‘Black’) ax。annotate(‘’,v1,v0,arrowprops=arrowprops) # draw a FancyArrowPatch arrow between the positions xy and xytext(v1 and v0)。

plt。scatter(X[:,0],X[:,1],alpha=0。3)for length,vector in zip(pca。explained_variance_,pca。components_): v=vector*3*np。sqrt(length) draw_vector(pca。mean_,pca。mean_+v)plt。axis(‘equal’)

PCA 主成分分析

程式碼中相關引數解析:

Pca。

components_

:array,shape(n_components, n_features)

Components_=

V

Principal axes in feature space, representing the direction of maximum variance in the data。

特徵空間中的主軸(主要成分),展示資料中最大方差的方向,(要看你需要,需要兩個主成分,n_components=2時就是前兩大),確定的是主成分的方向。

Pca。

explained_variance_

:array,shape(n_components,)

Explained_variance_=(

S

**2)/(n_samples-1)

The amount of variance explained by each of the selected components。

選擇的成分的方差的大小,確定的是對應的方向上的大小,也就是上圖中兩個黑色箭頭的長短,排序是S中方差從大到小的排序。

Pca。

mean_

:array, shape(n_feature)

Per-feature empirical mean, estimated from the training set。Equal to X。mean,

計算出整個訓練集中點群的中間點位置,即上圖中兩個黑色箭頭的交叉點(

箭頭的起點

)。

因為我們給定的n_components=2,所以上圖中有兩個向量,

V

確定了這兩個向量的方向,在計算時我們使用了sqrt(explained_variance_),對explained_variance開了根號,而explained_variance是由

S

的平方除以(n_samples-1)得到的,所以這裡的結果和S是線性的關係,從而確定了這兩個向量的大小,由此畫出了上圖中兩個黑色的既有長度,又有大小的箭頭。箭頭越長說明圖上的散點在這個方向上越分散,不同的點在這個方向上更能有較大的區別,而

sigma

值更小的,表現出來箭頭也更短,所以長箭頭表示的方向比短箭頭表示的方向在這組資料中更為主要,圖中兩個黑色箭頭互相垂直。

畫箭頭的function:

Ax。annotate(self, text, xy, *args, **kwargs)

Annotate the point xy with text

text

ax。annotate(‘’,v1,v0,arrowprops=arrowprops)

直線是點xy 到xytext之間,即v1和v0這兩個點連成一條直線,箭頭的樣式定義在arrowprops這個dictionary裡面。

arrowprops=dict(arrowstyle=‘<-’, linewidth=2, shrinkA=0, shrinkB=0, color=‘Black’)

arrowstyle箭頭的型別,arrowstyle變成’->’時,方向就會反過來。Linwidth:線條寬度,shrink:就是要不要縮短箭頭,我們這裡就不需要了,color:箭頭顏色。

下面看看當我們把維度降下來,變成一個component的時候:

此時需要把n_components=1,之前的S裡面有兩個基礎向量,現在把n_components設定為1後變成了一個。在S矩陣裡面,從左上角到右下角,取出第一個sigma。

pca=PCA(n_components=1)pca。fit(X)X_pca=pca。transform(X)print(“original shape: ”,X。shape)print(“transformed shape: ”,X_pca。shape)

PCA 主成分分析

X_new=pca。inverse_transform(X_pca)plt。scatter(X[:,0],X[:,1],alpha=0。3)plt。scatter(X_new[:,0],X_new[:,1],alpha=0。8)plt。axis(‘equal’)

Pca。

fit

(X): fit the model。

Pca。

transform

(X) : Apply dimensionality reduction to X。

給原來的矩陣降維,這裡設定的n_components=1,最終維度為1。

Pca。

inverse_transform

(X_pca): transform data back to its original space。

把降維後的資料還原到原來的空間中,這邊是把二維降維到一維後的資料還原到二維空間中,還原到二維空間後,其中一個維度的資訊被丟失就不能展示出來。

PCA 主成分分析

畫出來的影象如上圖,橘色部分的點,組成一條線條,和之前的較長邊的向量(黑色箭頭)具有相同的傾斜角度,並且藍色的每一個點做垂線垂直於橘色點組成的直線的話,在垂足的地方有與藍色點對應的橘色點,也就是藍色點在這條直線上的投影。這裡面在另外一個方向上,也就是較短的那個向量方向上的資訊我們就丟掉了,inverse_transform之後的結果只有一個component inverse transform的結果,而在另外那個方向上,由於它的variance更小,我們選擇丟掉它。

再來看一下更高維度的:

from sklearn。datasets import load_digitsdigits=load_digits()digits。data。shape

#shape 為: 1797,64,有64個維度。

def plot_digits(data): fig,axes=plt。subplots(4,10,figsize=(10,4), subplot_kw={‘xticks’:[],‘yticks’:[]}, gridspec_kw=dict(hspace=0。1,wspace=0。1)) for i,ax in enumerate(axes。flat): ax。imshow(data[i]。reshape(8,8), cmap=‘binary’,interpolation=‘nearest’, clim=(0,16))plot_digits(digits。data)

PCA 主成分分析

畫出來的影象如上圖,現在

人為給它加一點noise

np。random。seed(42)noisy=np。random。normal(digits。data,4)plot_digits(noisy)

得到下面的影象:

PCA 主成分分析

然後只提取前面50%的variance,

pca=PCA(0。5)。fit(noisy)plt。plot(np。cumsum((pca。explained_variance_ratio_)))

PCA 主成分分析

畫出上圖所需要的相關計算如下:

所有components的方差求和:Total_var=explained_variance_。sum()

每個component的方差所佔比例:Explained_variance_ratio_ = explained_variance_ / total_var

每個component的方差所佔比例求和:Np。cumsum()。

PCA 主成分分析

上圖可以看到對方差佔比總和剛達50%時,components的數量為12個,也就是對方差按從大到小排名後,前面至少12個components的variance加起來才會佔總的variance 50%以上,使用那12個components進行畫圖:

components=pca。transform(noisy)filtered=pca。inverse_transform(components)plot_digits(filtered)

PCA 主成分分析

上圖看起來雖然沒有加noise之前的圖清晰,但是比增加了noise的那張圖要清晰很多,這個提取主成分的過程過濾掉了很多不必要的成分,也就是一個過濾noise的過程,即使丟掉了一些細微的資訊,但是影象的質量比有Noise那個清晰多了。