降維算法-PCA主成分分析

一、PCA算法介紹
主成分分析(Principal Components Analysis),簡稱PCA,是一種數據降維技術,用於數據預處理。通常咱們獲取的原始數據維度都很高,好比1000個特徵,在這1000個特徵中可能包含了不少無用的信息或者噪聲,真正有用的特徵才100個,那麼咱們能夠運用PCA算法將1000個特徵降到100個特徵。這樣不只能夠去除無用的噪聲,還能減小很大的計算量。php

PCA算法是如何實現的?python

簡單來講,就是將數據從原始的空間中轉換到新的特徵空間中,例如原始的空間是三維的(x,y,z),x、y、z分別是原始空間的三個基,咱們能夠經過某種方法,用新的座標系(a,b,c)來表示原始的數據,那麼a、b、c就是新的基,它們組成新的特徵空間。在新的特徵空間中,可能全部的數據在c上的投影都接近於0,便可以忽略,那麼咱們就能夠直接用(a,b)來表示數據,這樣數據就從三維的(x,y,z)降到了二維的(a,b)。算法

問題是如何求新的基(a,b,c)?函數

通常步驟是這樣的:先對原始數據零均值化,而後求協方差矩陣,接着對協方差矩陣求特徵向量和特徵值,這些特徵向量組成了新的特徵空間。具體的細節,推薦Andrew Ng的網頁教程:Ufldl 主成分分析 ,寫得很詳細。spa

(1)零均值化

假如原始數據集爲矩陣dataMat,dataMat中每一行表明一個樣本,每一列表明同一個特徵。零均值化就是求每一列的平均值,而後該列上的全部數都減去這個均值。也就是說,這裏零均值化是對每個特徵而言的,零均值化都,每一個特徵的均值變成0。實現代碼以下:
1 def zeroMean(dataMat):      
2     meanVal=np.mean(dataMat,axis=0)     #按列求均值,即求各個特徵的均值
3     newData=dataMat-meanVal
4     return newData,meanVal
函數中用numpy中的mean方法來求均值,axis=0表示按列求均值。

該函數返回兩個變量,newData是零均值化後的數據,meanVal是每一個特徵的均值,是給後面重構數據用的。.net

(2)求協方差矩陣

1    newData,meanVal=zeroMean(dataMat)
2    covMat=np.cov(newData,rowvar=0)

numpy中的cov函數用於求協方差矩陣,參數rowvar很重要!若rowvar=0,說明傳入的數據一行表明一個樣本,若非0,說明傳入的數據一列表明一個樣本。由於newData每一行表明一個樣本,因此將rowvar設置爲0。
covMat即所求的協方差矩陣。code

(3)求特徵值、特徵矩陣
調用numpy中的線性代數模塊linalg中的eig函數,能夠直接由covMat求得特徵值和特徵向量:blog

1 eigVals,eigVects=np.linalg.eig(np.mat(covMat))

eigVals存放特徵值,行向量。
eigVects存放特徵向量,每一列帶別一個特徵向量。
特徵值和特徵向量是一一對應的排序

(4)保留主要的成分[即保留值比較大的前n個特徵]
第三步獲得了特徵值向量eigVals,假設裏面有m個特徵值,咱們能夠對其排序,排在前面的n個特徵值所對應的特徵向量就是咱們要保留的,它們組成了新的特徵空間的一組基n_eigVect。將零均值化後的數據乘以n_eigVect就能夠獲得降維後的數據。代碼以下:教程

1     eigValIndice=np.argsort(eigVals)            #對特徵值從小到大排序
2     n_eigValIndice=eigValIndice[-1:-(n+1):-1]   #最大的n個特徵值的下標
3     n_eigVect=eigVects[:,n_eigValIndice]        #最大的n個特徵值對應的特徵向量
4     lowDDataMat=newData*n_eigVect               #低維特徵空間的數據
5     reconMat=(lowDDataMat*n_eigVect.T)+meanVal  #重構數據
6     return lowDDataMat,reconMat

代碼中有幾點要說明一下,首先argsort對特徵值是從小到大排序的,那麼最大的n個特徵值就排在後面,因此eigValIndice[-1:-(n+1):-1]就取出這個n個特徵值對應的下標。【python裏面,list[a:b:c]表明從下標a開始到b,步長爲c。】

reconMat是重構的數據,乘以n_eigVect的轉置矩陣,再加上均值meanVal。

OK,這四步下來就能夠從高維的數據dataMat獲得低維的數據lowDDataMat,另外,程序也返回了重構數據reconMat,有些時候reconMat課便於數據分析。

貼一下總的代碼:

 1 #零均值化
 2 def zeroMean(dataMat):      
 3     meanVal=np.mean(dataMat,axis=0)     #按列求均值,即求各個特徵的均值
 4     newData=dataMat-meanVal
 5     return newData,meanVal
 6  
 7 def pca(dataMat,n):
 8     newData,meanVal=zeroMean(dataMat)
 9     covMat=np.cov(newData,rowvar=0)    #求協方差矩陣,return ndarray;若rowvar非0,一列表明一個樣本,爲0,一行表明一個樣本
10     
11     eigVals,eigVects=np.linalg.eig(np.mat(covMat))#求特徵值和特徵向量,特徵向量是按列放的,即一列表明一個特徵向量
12     eigValIndice=np.argsort(eigVals)            #對特徵值從小到大排序
13     n_eigValIndice=eigValIndice[-1:-(n+1):-1]   #最大的n個特徵值的下標
14     n_eigVect=eigVects[:,n_eigValIndice]        #最大的n個特徵值對應的特徵向量
15     lowDDataMat=newData*n_eigVect               #低維特徵空間的數據
16     reconMat=(lowDDataMat*n_eigVect.T)+meanVal  #重構數據
17     return lowDDataMat,reconMat

三、選擇主成分個數
文章寫到這裏尚未完,應用PCA的時候,對於一個1000維的數據,咱們怎麼知道要降到幾維的數據纔是合理的?即n要取多少,才能保留最多信息同時去除最多的噪聲?通常,咱們是經過方差百分比來肯定n的,這一點在Ufldl教程中說得很清楚,而且有一條簡單的公式,下面是該公式的截圖:

 

根據這條公式,能夠寫個函數,函數傳入的參數是百分比percentage和特徵值向量,而後根據percentage肯定n,代碼以下:

 1 def percentage2n(eigVals,percentage):
 2     sortArray=np.sort(eigVals)   #升序
 3     sortArray=sortArray[-1::-1]  #逆轉,即降序
 4     arraySum=sum(sortArray)
 5     tmpSum=0
 6     num=0
 7     for i in sortArray:
 8         tmpSum+=i
 9         num+=1
10         if tmpSum>=arraySum*percentage:
11             return num

那麼pca函數也能夠重寫成百分比版本,默認百分比99%。

 1 def pca(dataMat,percentage=0.99):
 2     newData,meanVal=zeroMean(dataMat)
 3     covMat=np.cov(newData,rowvar=0)    #求協方差矩陣,return ndarray;若rowvar非0,一列表明一個樣本,爲0,一行表明一個樣本
 4     eigVals,eigVects=np.linalg.eig(np.mat(covMat))#求特徵值和特徵向量,特徵向量是按列放的,即一列表明一個特徵向量
 5     n=percentage2n(eigVals,percentage)                 #要達到percent的方差百分比,須要前n個特徵向量
 6     eigValIndice=np.argsort(eigVals)            #對特徵值從小到大排序
 7     n_eigValIndice=eigValIndice[-1:-(n+1):-1]   #最大的n個特徵值的下標
 8     n_eigVect=eigVects[:,n_eigValIndice]        #最大的n個特徵值對應的特徵向量
 9     lowDDataMat=newData*n_eigVect               #低維特徵空間的數據
10     reconMat=(lowDDataMat*n_eigVect.T)+meanVal  #重構數據
11     return lowDDataMat,reconMat

 

相關文章
相關標籤/搜索