PCA(Principal Components Analysis)主成分分析是一個簡單的機器學習算法,利用正交變換把由線性相關變量表示的觀測數據轉換爲少數幾個由線性無關比變量表示的數據,實現降維的同時儘可能減小精度的損失,線性無關的變量稱爲主成分。大體以下:算法
首先對給定數據集(數據是向量)進行規範化,使得數據集的平均值爲0,方差爲1(規範化是爲了使數據散佈在原點附近,而不是遠離原點的某塊區域,便於後面的計算)。以後對每一個數據進行正交變換,把數據投影到幾個少許的相互正交的方向(這些方向構成了數據空間的一個子空間)上。數據在每一個方向上都有對應的座標,而用這些方向和對應的座標(座標×方向的累加)就能近似表示原來高維的數據,所以這些方向的求解就是PCA的關鍵。機器學習
若是再由這些座標經過這些方向映射回原來的數據,精度損失是同等方向數量的方向集合(或者叫同維度的子空間吧)中最小的,而數據集在各個方向上的座標的方差之和是同等方向數量的方向集合中最大的,也正對應着方差越大表示這個方向上保存數據的信息量越大(方差越小全部數據越集中在一個點上信息量固然越小)。數據集在這些方向的上的座標的方差,從大到小排序,就把這每個方向稱爲第一主成分、第二主成分……學習
接下來證實什麼樣的方向是主成分,即什麼樣的方向集合能保存原數據集更多的信息,並進一步說明數據集在主成分上的座標的方差是最大的。下面把數據映射爲各個方向上的座標稱爲編碼(即降維),再反映射回來稱爲解碼。優化
定義矩陣$X\in R^{m\times n}$爲數據集,由$m$個$n$維行向量數據$\{x_1,x_2,...,x_m\}$組成。求得數據均值與方差:編碼
$\displaystyle\overline{x}=\sum\limits_{i=1}^{m}\frac{x_i}{m}$spa
$\displaystyle s_i=\frac{1}{m-1}\sum\limits_{j=1}^{m}\left(x_{ij}-{\overline{x}}_i\right)^2,i=1,2,\ldots,n$code
規範化樣本:component
$\displaystyle x_{ij}^\ast=\frac{x_{ij}-\overline{x_j}}{\sqrt{s_j}},i=1,2,\ldots,m,j=1,2,\ldots,n$orm
得到規範化後的數據集$X^*$。PCA將把$X^*$編碼成矩陣$Y^*\in R^{m\times k},k<n$,經過右乘列向量單位正交的方向矩陣$D\in R^{n\times k}$獲得編碼後的矩陣$Y^*$:blog
$Y^* = X^*D$
再解碼回來則是再乘上$D^T$:
$\hat{X^*} = Y^*D^{T}$
爲了最小化精度的損失,優化$D$最小化$\hat{X^*}$與$X$之差的Frobenius範數:
$ \begin{aligned} &D = \displaystyle\text{arg}\min\limits_D{||\hat{X^*} - X^*||^2_F}\\ &D = \text{arg}\min\limits_D{|| X^*DD^T - X^*||^2_F}\\ &D = \text{arg}\min\limits_D{\text{Tr}[(X^*DD^T - X^*)^T(X^*DD^T - X^*)]}\\ &D = \text{arg}\min\limits_D{\text{Tr}[DD^TX^{*T}X^*DD^T-DD^TX^{*T}X^*-X^{*T}X^*DD^T+X^{*T}X^*]}\\ \end{aligned} $
去掉與$D$不相關的$X^{*T}X^*$項,再由跡的性質將矩陣乘法循環右移:
$ \begin{aligned} &D = \text{arg}\min\limits_D{\text{Tr}[X^*DD^TDD^TX^{*T}-X^*DD^TX^{*T}-D^TX^{*T}X^*D]}\\ &D = \text{arg}\min\limits_D{\text{Tr}[X^*DD^TX^{*T}-X^*DD^TX^{*T}-D^TX^{*T}X^*D]}\\ &D = \text{arg}\max\limits_D{\text{Tr}[D^TX^{*T}X^*D]}\\ \end{aligned} $
容易發現當$D$的列向量取$X^{*T}X^*$的前$k$大特徵值對應的特徵向量時,有這個跡的值最大,等於前$k$大特徵值之和。此時壓縮的精度損失最少。咱們又容易發現$X^{*T}X^*$實際上就是$X$行數據每一個元素之間的協方差矩陣(沒有除以$m-1$),再經過計算能夠發現,映射以後的$Y^*$行向量的每一個元素(也就是數據在每一個方向上的座標)的方差就是協方差矩陣的最大特徵值,正印證了座標方差越大信息量越大保存的信息越多。
另外,這個映射矩陣$D$的列向量在人臉識別中就是所謂的特徵臉。
實驗代碼:
import matplotlib.pyplot as plt import pylab #本來在jupyter裏才能顯示的圖片,能夠用窗口顯示 import numpy as np def PCA_img(img,n): t = np.mean(img,0) #以行向量爲壓縮單元,求行平均 s = np.std(img,0) #求行標準差 img=(img-t)/s #每行減去平均值進行規範化 v,vecters = np.linalg.eig(np.dot(img.T,img)/(len(img)-1)) #生成規範化矩陣後,轉置乘自身再除以(行數-1),即爲行向量各個元素的協方差矩陣,求協方差矩陣的特徵值與特徵向量 indv = v.argsort() #將特徵值排序,從小到大 vecters = vecters[:,indv] #將特徵向量按特徵值大小排序 vecters = vecters[:,-n:] #選擇前n大特徵值對應的特徵向量,做爲映射矩陣。在人臉識別中就是特徵臉 img = np.dot(np.dot(img,vecters),vecters.T) #規範化矩陣先乘映射矩陣再乘映射矩陣的轉置,也就是先壓縮再解壓縮 img = np.multiply(img,s)+t #把減掉的均值加回去變爲原矩陣 return img img=plt.imread("aaa.jpg") #讀取圖片 img.flags["WRITEABLE"] = True img = img.astype(float) n = 50 img[:,:,0] = PCA_img(img[:,:,0],n) img[:,:,1] = PCA_img(img[:,:,1],n) img[:,:,2] = PCA_img(img[:,:,2],n) fig = plt.figure() ax = fig.add_subplot(111) ax.imshow(img/255) print(img) pylab.show()
原圖是1200×1920的圖:
壓縮爲1200×50後再解壓回去:
sklearn中計算PCA比手動算快得多,它裏面實現了比較高效的隨機算法,每次算出來的值不同,可是相差不大。代碼以下:
import sklearn.decomposition as dc pca = dc.PCA(n_components = 10) #規定PCA要取前k大特徵值對應的特徵向量的個數,即要映射到幾維,PCA默認壓縮行,好比100*100壓縮到100*10 pca.fit(A) #將要用於壓縮的矩陣傳入,用來「學習」壓縮矩陣 D = pca.components_ #獲取「學習」到的編碼矩陣(壓縮矩陣)D。原矩陣A通過AD^T會獲得壓縮後的矩陣(這裏的D是上面推算的D^T) C = pca.transform(B) #傳入新的矩陣B,用「學習」到的編碼矩陣進行壓縮,即BD^T。在人臉特徵提取中,同一我的的不一樣人臉照片,用同一個壓縮矩陣壓縮後的座標向量一般類似,所以能夠用於人臉識別的降維