一寸宏、理論篇:
為書寫方便宁炫,加粗的字母表示向量。
如果想像力夠好氮凝,完全可以想象出兩個(gè)矩陣相乘的幾何意義:將右邊矩陣中每一列列向量變換到左邊矩陣中每一行行向量為基所表示的空間去羔巢。
比如我們常說的二維向量,
僅僅有這個(gè)條件罩阵,是不夠來表示一個(gè)向量的竿秆,還默認(rèn)了兩個(gè)基向量(1,0)和(0,1),一個(gè)基向量可代表一個(gè)空間稿壁,在這里分別指x軸和y軸兩個(gè)一維空間(在一維的空間里幽钢,(1,0),可以不寫0的傅是,直接寫(1)搅吁,數(shù)軸嘛)。二維向量正式A的表示方式應(yīng)該是落午,拆開看分別是:第一行(1,0)xA,第二行(0,1)xA(當(dāng)然你也可以理解為基向量的線性組合2x(1,0)'+3x(0,1)'肚豺,目前在討論變換的幾何意義溃斋,總覺得用線性組合的方式不好想象。)吸申,于是第一行可想象為空間里的一個(gè)“箭頭”A梗劫,被左乘了一下,(1,0)xA截碴,變換到(1,0)的空間里去梳侨,變成了(2),(在x軸這個(gè)一維空間里日丹,(1,0)或(2,3)里的0或3是沒有必要寫的)走哺,同樣的道理,第二行y軸也是哲虾。
也可以不用(1,0),(0,1)基表示丙躏,可以換成其他基择示,不再累贅。
總結(jié):
1.選擇不同的基晒旅,可以對同一組數(shù)據(jù)給出不同的表示栅盲。
2.可以完全擴(kuò)成一個(gè)空間的基對這一組數(shù)據(jù)的所有表示加起來,包含了這組數(shù)據(jù)的全部信息废恋。
如果我們舍棄y軸空間谈秫,這就是降維。
當(dāng)然鱼鼓,不能亂舍棄拟烫,降維的目的是為了舍棄那些沒有多少用的數(shù)據(jù),來保證減少數(shù)據(jù)量的同時(shí)蚓哩,不降低數(shù)據(jù)的信息构灸。
比如,
第一列和第三列線性相關(guān)岸梨,只要知道第一列喜颁,就等于知道了第三列,這個(gè)時(shí)候就可以舍棄第三行曹阔,變成
再回到幾何意義半开,我們可以把二維數(shù)據(jù)通過上述分析分別變換(投射)到兩個(gè)一維的基里,而基可以任意選赃份。假設(shè)有一大串二維數(shù)據(jù)X(幾何意義就是坐標(biāo)里的一系列點(diǎn))寂拆,我們選一個(gè)一維基,把這些點(diǎn)進(jìn)行投射到它的空間抓韩,如果選的足夠好纠永,這一個(gè)基就包含了大部分的數(shù)據(jù)信息,這樣谒拴,只要選這一個(gè)基來表示所有數(shù)據(jù)就好了尝江。
總結(jié):
3.不一定要選全部的基去表示這組數(shù)據(jù),用基表示后英上,信息多炭序,就可以用,信息少苍日,就可以舍棄惭聂。
而關(guān)鍵是,怎么選基相恃?直觀上看有兩點(diǎn):
(1)希望投影后的投影值盡可能分散辜纲,而這種分散程度,可用方差來表示。
(2)投影后侨歉,它們之間不存在線性關(guān)系屋摇,可用協(xié)方差來表示。
我們的目標(biāo)就變成了:選擇一組低緯基幽邓,讓高維投在低維上炮温,最離散、最不相關(guān)牵舵。正交時(shí)相關(guān)性為0柒啤,所以一般選正交基
拿上面的二維數(shù)據(jù)X舉例子,我們要選一組基P畸颅,令X投射到P后(這個(gè)動作用公式來表示就是Y=P.X)担巩,得到的矩陣Y的方差最大,協(xié)方差最小没炒。而把矩陣方差和協(xié)方差聯(lián)系起來的涛癌,正好是協(xié)方差矩陣。
假設(shè)X:
而C=:
對角線表示方差送火,非對角線表示協(xié)方差拳话。
其中方差(均值化零):
協(xié)方差(均值化零):
同樣Y也有協(xié)方差矩陣。我們要做的种吸,就是要找到P弃衍,使得Y協(xié)方差矩陣對角線(方差)最大,非對角線為0(協(xié)方差坚俗,不相關(guān))镜盯,滿足這樣的要求,不剛好是對角矩陣嗎猖败?于是:
這是什么速缆?這就是求矩陣C的對角矩陣呀!6魑拧艺糜!
總結(jié):
4.怎么選低維基?能讓這組數(shù)據(jù)的協(xié)方差矩陣相似對角化判呕。
有了P,問題就迎刃而解了送滞。P的前k行組成的矩陣乘以數(shù)據(jù)矩陣X侠草,便達(dá)到了降維的目的。
最后總結(jié)一下PCA的算法步驟:
設(shè)有m條n維的數(shù)據(jù)犁嗅。
1.將原始數(shù)據(jù)按列組成n行m列矩陣X
2.將X的每一行進(jìn)行零均值化
3.求X的協(xié)方差矩陣C
4.求C的特征值和特征向量
5.將特征向量按對應(yīng)的特征值大小從上到下排列成矩陣,取前k行組成矩陣P
6.Y=PX即是降到k維后的數(shù)據(jù)
二边涕、實(shí)現(xiàn)篇:
原本是想用matlab,畢竟對它比較熟。后來僥幸發(fā)現(xiàn)工作中自動化用的python也有不錯的數(shù)據(jù)處理功能功蜓。電腦上沒有matlab园爷,但有現(xiàn)成的python環(huán)境,懶的人一般都會選擇用現(xiàn)有的東西式撼。
Python有著非常強(qiáng)大的科學(xué)計(jì)算庫:
numpy~~基礎(chǔ)計(jì)算庫童社,多維數(shù)組處理
scipy~~基于numpy,用于數(shù)值計(jì)算等等著隆,默認(rèn)調(diào)用intel mkl(高度優(yōu)化的數(shù)學(xué)庫)
pandas~~強(qiáng)大的數(shù)據(jù)框扰楼,基于numpy
matplotlib~~繪圖庫,基于numpy,scipy
sklearn~~機(jī)器學(xué)習(xí)庫,有各種機(jī)器學(xué)習(xí)算法
可以直接下載Anaconda美浦,包含了大部分庫弦赖。
把我自己的一張圖片降維后,取前n=0,50,100,150,200,250,300,350,400行分別得到的圖片如下:
(圖片是300*400)
附上程序(程序都是自己寫的浦辨,有點(diǎn)亂):
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
#將矩陣進(jìn)行PCA降維蹬竖,dataMat為需要降維的矩陣,n為選取前n個(gè)最大的特征值對應(yīng)的特征向量
def pca(dataMat,n):
newData,meanVal=zeroMean(dataMat)#2.將X的每一行進(jìn)行零均值化
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.將特征向量按對應(yīng)的特征值大小從上到下排列成矩陣,取前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