一、理論篇:
為書寫方便,加粗的字母表示向量。
如果想像力夠好,完全可以想象出兩個矩陣相乘的幾何意義:將右邊矩陣中每一列列向量變換到左邊矩陣中每一行行向量為基所表示的空間去。
比如我們常說的二維向量,
僅僅有這個條件,是不夠來表示一個向量的,還默認了兩個基向量(1,0)和(0,1),一個基向量可代表一個空間,在這里分別指x軸和y軸兩個一維空間(在一維的空間里,(1,0),可以不寫0的,直接寫(1),數(shù)軸嘛)。二維向量正式A的表示方式應該是,拆開看分別是:第一行(1,0)xA,第二行(0,1)xA(當然你也可以理解為基向量的線性組合2x(1,0)'+3x(0,1)',目前在討論變換的幾何意義,總覺得用線性組合的方式不好想象。),于是第一行可想象為空間里的一個“箭頭”A,被左乘了一下,(1,0)xA,變換到(1,0)的空間里去,變成了(2),(在x軸這個一維空間里,(1,0)或(2,3)里的0或3是沒有必要寫的),同樣的道理,第二行y軸也是。
也可以不用(1,0),(0,1)基表示,可以換成其他基,不再累贅。
總結:
1.選擇不同的基,可以對同一組數(shù)據(jù)給出不同的表示。
2.可以完全擴成一個空間的基對這一組數(shù)據(jù)的所有表示加起來,包含了這組數(shù)據(jù)的全部信息。
如果我們舍棄y軸空間,這就是降維。
當然,不能亂舍棄,降維的目的是為了舍棄那些沒有多少用的數(shù)據(jù),來保證減少數(shù)據(jù)量的同時,不降低數(shù)據(jù)的信息。
比如,
第一列和第三列線性相關,只要知道第一列,就等于知道了第三列,這個時候就可以舍棄第三行,變成
再回到幾何意義,我們可以把二維數(shù)據(jù)通過上述分析分別變換(投射)到兩個一維的基里,而基可以任意選。假設有一大串二維數(shù)據(jù)X(幾何意義就是坐標里的一系列點),我們選一個一維基,把這些點進行投射到它的空間,如果選的足夠好,這一個基就包含了大部分的數(shù)據(jù)信息,這樣,只要選這一個基來表示所有數(shù)據(jù)就好了。
總結:
3.不一定要選全部的基去表示這組數(shù)據(jù),用基表示后,信息多,就可以用,信息少,就可以舍棄。
而關鍵是,怎么選基?直觀上看有兩點:
(1)希望投影后的投影值盡可能分散,而這種分散程度,可用方差來表示。
(2)投影后,它們之間不存在線性關系,可用協(xié)方差來表示。
我們的目標就變成了:選擇一組低緯基,讓高維投在低維上,最離散、最不相關。正交時相關性為0,所以一般選正交基
拿上面的二維數(shù)據(jù)X舉例子,我們要選一組基P,令X投射到P后(這個動作用公式來表示就是Y=P.X),得到的矩陣Y的方差最大,協(xié)方差最小。而把矩陣方差和協(xié)方差聯(lián)系起來的,正好是協(xié)方差矩陣。
假設X:
而C=:
對角線表示方差,非對角線表示協(xié)方差。
其中方差(均值化零):
協(xié)方差(均值化零):
同樣Y也有協(xié)方差矩陣。我們要做的,就是要找到P,使得Y協(xié)方差矩陣對角線(方差)最大,非對角線為0(協(xié)方差,不相關),滿足這樣的要求,不剛好是對角矩陣嗎?于是:
這是什么?這就是求矩陣C的對角矩陣呀!!!
總結:
4.怎么選低維基?能讓這組數(shù)據(jù)的協(xié)方差矩陣相似對角化。
有了P,問題就迎刃而解了。P的前k行組成的矩陣乘以數(shù)據(jù)矩陣X,便達到了降維的目的。
最后總結一下PCA的算法步驟:
設有m條n維的數(shù)據(jù)。
1.將原始數(shù)據(jù)按列組成n行m列矩陣X
2.將X的每一行進行零均值化
3.求X的協(xié)方差矩陣C
4.求C的特征值和特征向量
5.將特征向量按對應的特征值大小從上到下排列成矩陣,取前k行組成矩陣P
6.Y=PX即是降到k維后的數(shù)據(jù)
二、實現(xiàn)篇:
原本是想用matlab,畢竟對它比較熟。后來僥幸發(fā)現(xiàn)工作中自動化用的python也有不錯的數(shù)據(jù)處理功能。電腦上沒有matlab,但有現(xiàn)成的python環(huán)境,懶的人一般都會選擇用現(xiàn)有的東西。
Python有著非常強大的科學計算庫:
numpy~~基礎計算庫,多維數(shù)組處理
scipy~~基于numpy,用于數(shù)值計算等等,默認調(diào)用intel mkl(高度優(yōu)化的數(shù)學庫)
pandas~~強大的數(shù)據(jù)框,基于numpy
matplotlib~~繪圖庫,基于numpy,scipy
sklearn~~機器學習庫,有各種機器學習算法
可以直接下載Anaconda,包含了大部分庫。
把我自己的一張圖片降維后,取前n=0,50,100,150,200,250,300,350,400行分別得到的圖片如下:
(圖片是300*400)
附上程序(程序都是自己寫的,有點亂):
import numpy as np
from PIL import Image
import os,glob
import scipy.linalg as linA
#將矩陣零均值化
def zeroMean(dataMat):
meanVal=np.mean(dataMat,axis=0)
newData=dataMat-meanVal
return newData,meanVal
#將矩陣進行PCA降維,dataMat為需要降維的矩陣,n為選取前n個最大的特征值對應的特征向量
def pca(dataMat,n):
newData,meanVal=zeroMean(dataMat)#2.將X的每一行進行零均值化
covMat=np.cov(newData,rowvar=0) #3.求X的協(xié)方差矩陣C
#4.求C的特征值和特征向量
eigVals,eigVects=np.linalg.eig(np.mat(covMat))
eigValIndice=np.argsort(eigVals)
#5.將特征向量按對應的特征值大小從上到下排列成矩陣,取前n行組成矩陣P
n_eigValIndice=eigValIndice[-1:-(n+1):-1]
n_eigVect=eigVects[:,n_eigValIndice]
#6.Y=PX即是降到k維后的數(shù)據(jù)
lowDDataMat=newData*n_eigVect
reconMat=(lowDDataMat*n_eigVect.T)+meanVal#降維后的Y再加上之前零均值化所減去的平均值
return lowDDataMat,reconMat
#讀取圖片并轉(zhuǎn)換為矩陣。1.將原始數(shù)據(jù)按列組成n行m列矩陣X
def ImageToMatrix(pictureName):
#讀取圖片
img = Image.open(pictureName)
#顯示圖片
#img.show()
width,height = img.size
img = img.convert("L")
data = img.getdata()
data = np.matrix(data,dtype='float')/255.0
#new_data = np.reshape(data,(width,height))
new_data = np.reshape(data,(height,width))
return new_data
#將矩陣轉(zhuǎn)換為圖片
def MatrixToImage(data):
data = data*255
new_img = Image.fromarray(data.astype(np.uint8))
return new_img
if __name__ == '__main__':
picture_path = os.getcwd()+'\\picture\\'
picture_save_path= picture_path+'save\\'
pictureName=picture_path+'Man.bmp'
dataMat=ImageToMatrix(pictureName)
n=0
for n in range(0,400,50):
lowDDataMat,reconMat=pca(dataMat,n)
new_img=MatrixToImage(reconMat)
#new_img.show()
new_img.save(picture_save_path+str(n)+'.bmp')
參考:
http://www.360doc.com/content/13/1124/02/9482_331688889.shtml