1. 程式人生 > >PCA演算法的數學原理以及Python實現

PCA演算法的數學原理以及Python實現

部落格中的筆記:

降維當然意味著資訊的丟失,不過鑑於實際資料本身常常存在的相關性,我們可以想辦法在降維的同時將資訊的損失儘量降低

根據相關性來講資訊的損失量降到最低

更正式的說,向量(x,y)實際上表示線性組合:

x(1,0)?+y(0,1)?

不難證明所有二維向量都可以表示為這樣的線性組合。此處(1,0)和(0,1)叫做二維空間中的一組基。

一般的,如果我們有M個N維向量,想將其變換為由R個N維向量表示的新空間中,那麼首先將R個基按行組成矩陣A,然後將向量按列組成矩陣B,那麼兩矩陣的乘積AB就是變換結果,其中AB的第m列為A中第m列變換後的結果

協方差(Covariance)在概率論

統計學中用於衡量兩個變數的總體誤差。而方差是協方差的一種特殊情況,即當兩個變數是相同的情況。

那麼如何選擇這個方向(或者說基)才能儘量保留最多的原始資訊呢?一種直觀的看法是:希望投影后的投影值儘可能分散。

方差

上面的問題(儘可能投影的分散)被形式化表述為:尋找一個一維基,使得所有資料變換為這個基上的座標表示後,方差值最大。

協方差

從直觀上說,讓兩個欄位儘可能表示更多的原始資訊,我們是不希望它們之間存在(線性)相關性的,因為相關性意味著兩個欄位不是完全獨立,必然存在重複表示的資訊。

數學上可以用兩個欄位的協方差表示其相關性,由於已經讓每個欄位均值為0,則:

Cov(a,b)=1m∑i=1maibi

當協方差為0時,表示兩個欄位完全獨立。為了讓協方差為0,我們選擇第二個基時只能在與第一個基正交的方向上選擇。因此最終選擇的兩個方向一定是正交的

至此,我們得到了降維問題的優化目標:將一組N維向量降為K維(K大於0,小於N),其目標是選擇K個單位(模為1)正交基,使得原始資料變換到這組基上後,各欄位兩兩間協方差為0,而欄位的方差則儘可能大(在正交的約束下,取最大的K個方差

協方差矩陣求法:

協方差矩陣

接下來講解一下我對於PCA演算法的理解:

協方差矩陣對角化

現在事情很明白了!我們要找的P不是別的,而是能讓原始協方差矩陣對角化的P。換句話說,優化目標變成了尋找一個矩陣P,滿足PCP?

是一個對角矩陣,並且對角元素按從大到小依次排列,那麼P的前K行就是要尋找的基,用P的前K行組成的矩陣乘以X就使得X從N維降到了K維並滿足上述優化條件

協方差矩陣C是一個是對稱矩陣,線上性代數上,實對稱矩陣有一系列非常好的性質:

1)實對稱矩陣不同特徵值對應的特徵向量必然正交。

2)設特徵向量λ

重數為r,則必然存在r個線性無關的特徵向量對應於λ,因此可以將這r個特徵向量單位正交化。

PCA演算法

總結一下PCA的演算法步驟:

設有m條n維資料。

1)將原始資料按列組成n行m列矩陣X

2)將X的每一行(代表一個屬性欄位)進行零均值化,即減去這一行的均值

3)求出協方差矩陣C=1mXX?

4)求出協方差矩陣的特徵值及對應的特徵向量

5)將特徵向量按對應特徵值大小從上到下按行排列成矩陣,取前k行組成矩陣P

6)Y=PX

即為降維到k維後的資料

PCA本質上是將方差最大的方向作為主要特徵,並且在各個正交方向上將資料“離相關”,也就是讓它們在不同正交方向上沒有相關性。

Python程式碼:

'''
@author: Garvin
'''
from numpy import *
import matplotlib.pyplot as plt

def loadDataSet(fileName, delim='\t'):
    fr = open(fileName)
    dataArr=[]
    for line in fr.readlines():
        stringArr= line.strip().split(delim)
        colArr=[]
        for numString in stringArr:
            colArr.append(float(numString))
        dataArr.append(colArr)
    return mat(dataArr)

def pca(dataMat, topNfeat=9999999):
    meanVals = mean(dataMat, axis=0)#獲取平均值。
    meanRemoved = dataMat - meanVals #remove mean
    covMat = cov(meanRemoved, rowvar=0)#協方差矩陣
    eigVals,eigVects = linalg.eig(mat(covMat))#求特徵值,特徵向量
    eigValInd = argsort(eigVals)     #特徵值排序       #sort, sort goes smallest to largest
    print(eigValInd)
    eigValInd = eigValInd[:-(topNfeat+1):-1]  #cut off unwanted dimensions減掉不想要的特徵值
    print(eigValInd)
    redEigVects = eigVects[:,eigValInd]    #識別最大到最小的特徵向量   #reorganize eig vects largest to smallest
    lowDDataMat = meanRemoved * redEigVects #將資料轉到新的維度中#transform data into new dimensions
    reconMat = (lowDDataMat * redEigVects.T) + meanVals
    return lowDDataMat, reconMat #轉化回來的矩陣,以及最終的降維矩陣

##畫出最合適的方向。
def plotBestFit(dataSet1,dataSet2):      
    dataArr1 = array(dataSet1)
    dataArr2 = array(dataSet2)
    n = shape(dataArr1)[0] 
    n1=shape(dataArr2)[0]
    xcord1 = []; ycord1 = []
    xcord2 = []; ycord2 = []
    xcord3=[];ycord3=[]
    j=0
    for i in range(n):
            xcord1.append(dataArr1[i,0]); ycord1.append(dataArr1[i,1])
            xcord2.append(dataArr2[i,0]); ycord2.append(dataArr2[i,1])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
    ax.scatter(xcord2, ycord2, s=30, c='green')
    plt.xlabel('X1'); plt.ylabel('X2');
    plt.show()    

if __name__=='__main__':
     mata=loadDataSet('testSet.txt')
     #print(mata)
     a,b= pca(mata, 2)
     plotBestFit(a,b)