PCA算法全稱是Principal Component Analysis,即主成分分析算法。它是一種維數(shù)約減(Dimensionality Reduction)算法,即把高維度數(shù)據(jù)在損失最小的情況下轉(zhuǎn)換為低維度數(shù)據(jù)的算法鹃锈。顯然窜醉,PCA可以用來對數(shù)據(jù)進(jìn)行壓縮宪萄,可以在可控的失真范圍內(nèi)提高運算速度。
1.PCA算法原理
我們先從最簡單的情況談起榨惰。假設(shè)需要把一個二維數(shù)據(jù)降維成一維數(shù)據(jù)拜英,要怎么做呢?如下圖所示琅催。
我們可以想辦法找出一個向量居凶,以便讓二維數(shù)據(jù)的點(方形點)到這個向量所在的直線上的平均距離最短,即投射誤差最小藤抡。
這樣就可以在失真最小的情況下排监,把二維數(shù)據(jù)轉(zhuǎn)換為向量所在直線上的一維數(shù)據(jù)。再進(jìn)一步杰捂,假設(shè)需要把三維數(shù)據(jù)降為二維數(shù)據(jù)時,我們需要找出兩個向量
和
棋蚌,以便讓三維數(shù)據(jù)的點在這兩個向量決定的平面上的投射誤差最小嫁佳。
如果從數(shù)學(xué)角度來一般地描述PCA算法就是:當(dāng)需要從n維數(shù)據(jù)降為k維數(shù)據(jù)時,需要找出k個向量谷暮,
蒿往,……,
湿弦,把n維的數(shù)據(jù)投射到這k個向量決定的線性空間里瓤漏,最終使投射誤差最小化的過程。
思考:什么情況下颊埃,進(jìn)行PCA運算時誤差為0蔬充?在上圖中,當(dāng)這些二維數(shù)據(jù)在同一條直線上時班利,進(jìn)行PCA運算后饥漫,誤差為0。
問題來了罗标,怎樣找出投射誤差最小的k個向量呢庸队?要完整的用數(shù)學(xué)公式推導(dǎo)這個方法莫换,涉及較多高級線性代數(shù)的知識双戳,這里不再詳述蹈矮。感興趣的話可以參考后面擴(kuò)展部分的內(nèi)容埠戳。下面我們直接介紹PCA算法求解的一般步驟狞尔。
假設(shè)有一個數(shù)據(jù)集谢揪,用m x n維的矩陣A表示孝情。矩陣中每一行表示一個樣本雹舀,每一列表示一個特征鼓黔,總共有m個樣本央勒,每個樣本有n個特征不见。我們的目標(biāo)是減少特征個數(shù),保留最重要的k個特征崔步。
1.數(shù)據(jù)歸一化和縮放
數(shù)據(jù)歸一化和縮放是一種數(shù)學(xué)技巧稳吮,旨在提高PCA運算時的效率。數(shù)據(jù)歸一化的目標(biāo)是使特征的均值為0井濒。數(shù)據(jù)歸一化公式為:
其中灶似,是指第i個樣本的第j個特征的值,
表示的是第j個特征的均值瑞你。當(dāng)不同的特征值不在同一個數(shù)量級上的時候酪惭,還需要對數(shù)據(jù)進(jìn)行縮放。數(shù)據(jù)歸一化在縮放的公式為:
其中者甲,是指第i個樣本的第j個特征的值春感,
表示的是第j個特征的均值,
表示第j個特征的范圍虏缸,即
鲫懒。
2.計算協(xié)方差矩陣的特征向量
針對預(yù)處理后的矩陣X,先計算其協(xié)方差矩陣(Covariance Matrix):
其中刽辙,表示協(xié)方差矩陣窥岩,用大寫的Sigma表示,是一個n * n維的矩陣宰缤。
接著通過奇異值分解來計算協(xié)方差矩陣的特征向量:
其中颂翼,svd是奇異值分解(Singular Value Decomposition)運算,是高級線性代數(shù)的內(nèi)容慨灭。經(jīng)過奇異值分解后朦乏,有3個返回值,其中矩陣U是一個n * n的矩陣氧骤,如果我們選擇U的列作為向量集歇,那么我們將得到n個列向量,
语淘,……诲宇,
,這些向量就是協(xié)方差矩陣的特征向量惶翻。它表示的物理意義是姑蓝,協(xié)方差矩陣
可以由這些特征向量進(jìn)行線性組合得到。
3.數(shù)據(jù)降維和恢復(fù)
得到特征矩陣后吕粗,就可以對數(shù)據(jù)進(jìn)行降維處理了纺荧。假設(shè)降維前的值是,降維后是
,那么
其中宙暇,,...,
输枯,它選取自矩陣U的前k個向量,
稱為主成分特征矩陣占贫,它是數(shù)據(jù)降維和恢復(fù)的關(guān)鍵中間變量桃熄。看一下數(shù)據(jù)維度型奥,
是n * k的矩陣瞳收,因此
是k * n的矩陣,
是n * 1的向量厢汹,因此
是k * 1的向量螟深。這樣即完成了數(shù)據(jù)的降維操作。
也可以用矩陣運算一次性轉(zhuǎn)換多個向量烫葬,提高效率界弧。假設(shè)X是行向量組成的矩陣,則
其中搭综,X是m * n的矩陣夹纫,降維后的矩陣Z是一個m * k的矩陣。
從物理意義角度來看设凹,就是
在
構(gòu)成的線性空間的投射,并且其投射誤差最小茅姜。要從數(shù)學(xué)上正面這個結(jié)論闪朱,將是一個非常復(fù)雜的過程。對其原理感興趣的話钻洒,可以參考后面擴(kuò)展閱讀部分的內(nèi)容奋姿。
數(shù)據(jù)降維后,怎么恢復(fù)呢素标?從前面的計算公式我們知道称诗,降維后的數(shù)據(jù)計算公式。所以如果要還原數(shù)據(jù)头遭,可以使用下面的公式:
其中寓免,是n * k的矩陣,
是k維列向量计维。這樣算出來的
就是n維列向量袜香。
矩陣化數(shù)據(jù)恢復(fù)運算公式為:
其中,是還原回來的數(shù)據(jù)鲫惶,是一個m * n的矩陣蜈首,每行表示一個訓(xùn)練樣例。Z是一個m * k的矩陣,是降維后的數(shù)據(jù)欢策。
2.PCA算法示例
假設(shè)我們的數(shù)據(jù)集總共有5個記錄吆寨,每個記錄有2個特征,這樣構(gòu)成的矩陣A為:
我們的目標(biāo)是把二維數(shù)據(jù)降為一維數(shù)據(jù)踩寇。為了更好地理解PCA的計算過程啄清,分別使用Numpy和sklearn對同一個數(shù)據(jù)進(jìn)行PCA降維處理。
1.使用Numpy模擬PCA計算過程
import numpy as np
A = np.array([[3,2000],
[2,3000],
[4,5000],
[5,8000],
[1,2000]],dtype='float')
# 數(shù)據(jù)歸一化姑荷,axis=0表示按列歸一化
mean = np.mean(A,axis=0)
norm = A - mean
# 數(shù)據(jù)縮放
score = np.max(norm,axis=0)-np.min(norm,axis=0)
norm = norm / score
norm
由于兩個特征的均值不在同一個數(shù)量級盒延,所以對數(shù)據(jù)進(jìn)行了縮放。輸出如下:
array([[ 0. , -0.33333333],
[-0.25 , -0.16666667],
[ 0.25 , 0.16666667],
[ 0.5 , 0.66666667],
[-0.5 , -0.33333333]])
接著鼠冕,對協(xié)方差矩陣進(jìn)行奇異值分解添寺,求解其特征向量:
U,S,V = np.linalg.svd(np.dot(norm.T,norm))
U
輸出如下:
array([[-0.67710949, -0.73588229],
[-0.73588229, 0.67710949]])
由于需要把二維數(shù)據(jù)降為一維數(shù)據(jù),因此只取特征矩陣的第一列(前k列)來構(gòu)造主成分特征矩陣:
U_reduce = U[:,0].reshape(2,1)
U_reduce
輸出如下:
array([[-0.67710949],
[-0.73588229]])
有了主成分特征矩陣懈费,就可以對數(shù)據(jù)進(jìn)行降維了:
R = np.dot(norm,U_reduce)
R
輸出如下:
array([[ 0.2452941 ],
[ 0.29192442],
[-0.29192442],
[-0.82914294],
[ 0.58384884]])
這樣就把二維的數(shù)據(jù)降為一維的數(shù)據(jù)了计露。如果需要還原數(shù)據(jù),依照PCA數(shù)據(jù)恢復(fù)的計算公式憎乙,可得:
Z = np.dot(R,U_reduce.T)
Z
輸出如下:
array([[-0.16609096, -0.18050758],
[-0.19766479, -0.21482201],
[ 0.19766479, 0.21482201],
[ 0.56142055, 0.6101516 ],
[-0.39532959, -0.42964402]])
由于我們在數(shù)據(jù)預(yù)處理階段對數(shù)據(jù)進(jìn)行了歸一化票罐,并且做了縮放處理,所以需要進(jìn)一步還原才能得到原始數(shù)據(jù)泞边,這一步是數(shù)據(jù)預(yù)處理的逆運算该押。
A1 = np.multiply(Z,scope)+mean
A1
其中,np.multiply是矩陣對應(yīng)元素相乘阵谚,np.dot是矩陣的行乘以矩陣的列蚕礼。輸出如下:
array([[2.33563616e+00, 2.91695452e+03],
[2.20934082e+00, 2.71106794e+03],
[3.79065918e+00, 5.28893206e+03],
[5.24568220e+00, 7.66090960e+03],
[1.41868164e+00, 1.42213588e+03]])
與原始矩陣A相比,恢復(fù)后的數(shù)據(jù)A1還是存在一定程度的失真梢什,這種失真是不可避免的奠蹬。
2.使用sklearn進(jìn)行PCA降維運算
在sklearn工具包里,類sklearn.decomposition.PCA實現(xiàn)了PCA算法嗡午,使用方便囤躁,不需要了解具體的PCA的運算步驟。但需要注意的是荔睹,數(shù)據(jù)的預(yù)處理需要自己完成狸演,其PCA算法本身不進(jìn)行數(shù)據(jù)預(yù)處理(歸一化和縮放)。此處僻他,我們選擇MinMaxScaler類進(jìn)行數(shù)據(jù)預(yù)處理严沥。
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MinMaxScaler
def std_PCA(**argv):
scaler = MinMaxScaler()
pca = PCA(**argv)
pipeline = Pipeline([('scaler', scaler),
('pca', pca)])
return pipeline
pca = std_PCA(n_components=1)
R2 = pca.fit_transform(A)
R2
輸出如下:
array([[-0.2452941 ],
[-0.29192442],
[ 0.29192442],
[ 0.82914294],
[-0.58384884]])
這個輸出值就是矩陣A經(jīng)過預(yù)處理以及PCA降維后的數(shù)值。我們發(fā)現(xiàn)中姜,這里的輸出結(jié)果和上面使用Numpy方式的輸出結(jié)果符號相反消玄,這其實不是錯誤跟伏,只是降維后選擇的坐標(biāo)方向不同而已。
接著把數(shù)據(jù)恢復(fù)回來:
A2 = pca.inverse_transform(R2)
A2
這里的pca是一個Pipeline實例翩瓜,其逆運算inverse_transform()是逐級進(jìn)行的受扳,即先進(jìn)行PCA還原,再執(zhí)行預(yù)處理的逆運算兔跌。即先調(diào)用PCA.inverse_transform()勘高,然后再調(diào)用MinMaxScaler.inverse_transform()。輸出如下:
array([[2.33563616e+00, 2.91695452e+03],
[2.20934082e+00, 2.71106794e+03],
[3.79065918e+00, 5.28893206e+03],
[5.24568220e+00, 7.66090960e+03],
[1.41868164e+00, 1.42213588e+03]])
可以看到坟桅,這里還原回來的數(shù)據(jù)和前面Numpy方式還原回來的數(shù)據(jù)是一致的华望。
3.PCA的物理含義
我們可以把前面例子中的數(shù)據(jù)在一個坐標(biāo)軸上全部畫出來,從而仔細(xì)觀察PCA降維過程的物理含義仅乓。如下圖所示赖舟。
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 8), dpi=144)
plt.title('Physcial meanings of PCA')
ymin = xmin = -1
ymax = xmax = 1
plt.xlim(xmin, xmax)
plt.ylim(ymin, ymax)
ax = plt.gca() # gca 代表當(dāng)前坐標(biāo)軸,即 'get current axis'
ax.spines['right'].set_color('none') # 隱藏坐標(biāo)軸
ax.spines['top'].set_color('none')
plt.scatter(norm[:, 0], norm[:, 1], marker='s', c='b')
plt.scatter(Z[:, 0], Z[:, 1], marker='o', c='r')
plt.arrow(0, 0, U[0][0], U[1][0], color='r', linestyle='-')
plt.arrow(0, 0, U[0][1], U[1][1], color='r', linestyle='--')
plt.annotate(r'$U_{reduce} = u^{(1)}$',
xy=(U[0][0], U[1][0]), xycoords='data',
xytext=(U_reduce[0][0] + 0.2, U_reduce[1][0] - 0.1), fontsize=10,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))
plt.annotate(r'$u^{(2)}$',
xy=(U[0][1], U[1][1]), xycoords='data',
xytext=(U[0][1] + 0.2, U[1][1] - 0.1), fontsize=10,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))
plt.annotate(r'raw data',
xy=(norm[0][0], norm[0][1]), xycoords='data',
xytext=(norm[0][0] + 0.2, norm[0][1] - 0.2), fontsize=10,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))
plt.annotate(r'projected data',
xy=(Z[0][0], Z[0][1]), xycoords='data',
xytext=(Z[0][0] + 0.2, Z[0][1] - 0.1), fontsize=10,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))
plt.show()
圖中正方形的點是原始數(shù)據(jù)經(jīng)過預(yù)處理后(歸一化夸楣、縮放)的數(shù)據(jù)宾抓,圓形的點是從一維恢復(fù)到二維后的數(shù)據(jù)。同時豫喧,我們畫出主成分特征向量石洗,
。根據(jù)上圖紧显,來介紹幾個有意思的結(jié)論:首先讲衫,圓形的點實際上就是方形的點在向量
所在直線上的投影。所謂PCA數(shù)據(jù)恢復(fù)孵班,并不是真正的恢復(fù)涉兽,只是把降維后的坐標(biāo)轉(zhuǎn)換為原坐標(biāo)系中的坐標(biāo)而已。針對我們的例子重父,只是把由向量
決定的一維坐標(biāo)系中的坐標(biāo)轉(zhuǎn)換為原始二維坐標(biāo)系中的坐標(biāo)。其次忽匈,主成分特征向量
房午,
是相互垂直的。再次丹允,方形點和圓形點之間的距離郭厌,就是PCA數(shù)據(jù)降維后的誤差。
3.PCA的數(shù)據(jù)還原率及應(yīng)用
PCA算法可以用來對數(shù)據(jù)進(jìn)行壓縮雕蔽,可以在可控的失真范圍內(nèi)提高運算速度折柠。
1.數(shù)據(jù)還原率
使用PCA對數(shù)據(jù)進(jìn)行壓縮時,涉及失真的度量問題批狐,即壓縮后的數(shù)據(jù)能夠在多大程度上還原出原數(shù)據(jù)扇售,我們稱這一指標(biāo)為數(shù)據(jù)還原率前塔,用百分比表示。假設(shè)我們要求失真度不超過1%承冰,即數(shù)據(jù)還原率達(dá)到99%华弓,怎么來實現(xiàn)這個要求呢?k是主成分分析法中主成分的個數(shù)困乒〖牌粒可以用下面的公式作為約束條件,從而選擇合適的誤差范圍娜搂,最合適的k值:
其中迁霎,分子部分表示平均投射誤差的平方;分母部分表示所有訓(xùn)練樣本到原點距離的平均值百宇。這里的物理意義用術(shù)語可以描述為99%的數(shù)據(jù)真實性被保留下來了考廉。簡單的理解為壓縮后的數(shù)據(jù)還原出原數(shù)據(jù)的準(zhǔn)確度為99%。另外恳谎,常用的比率還有0.05芝此,這個時候數(shù)據(jù)還原率就是95%。在實際應(yīng)用中因痛,可以根據(jù)要解決的問題的場景來決定這個比率婚苹。
假設(shè)我們的還原率要求是99%,那么用下面的算法來選擇參數(shù)k:
(1)讓k=1
(2)運行PCA算法鸵膏,計算出
(3)利用計算投射誤差膊升,并判斷是否滿足要求,如果不滿足要求谭企,則令k=k+1廓译,然后繼續(xù)步驟(2);如果滿足要求债查,k即是我們選擇的參數(shù)非区。
這個算法容易理解,但實際上效率非常低盹廷,因為每做一次循環(huán)都要運行一遍PCA算法征绸。另一個更高效的做法是,利用協(xié)方差矩陣進(jìn)行奇異值分解返回的S矩陣:俄占。其中管怠,S是n * n的對角矩陣,即只有對角線上的元素是非0值缸榄,其他元素都是0渤弛。
從數(shù)學(xué)上可以證明,投射誤差率也可以使用下面的公式計算:
這樣運算效率大大提高了甚带,我們只需要進(jìn)行一次svd運算即可她肯。
2.加快監(jiān)督機(jī)器學(xué)習(xí)算法的運算速度
PCA算法的一個典型應(yīng)用就是用來加快監(jiān)督學(xué)習(xí)的速度佳头。
例如,我們有m個訓(xùn)練數(shù)據(jù)辕宏,
畜晰,……,
瑞筐,其中凄鼻,
是10000維的數(shù)據(jù),想象一下聚假,如果這是個圖片分類的問題块蚌,比如輸入的圖片是100 * 100分辨率的,那么我們就有10000維的輸入數(shù)據(jù)膘格。
使用PCA算法來加快算法運算速度時峭范,我們把輸入數(shù)據(jù)分解出來,
瘪贱,……纱控,
,然后運用PCA算法對輸入數(shù)據(jù)進(jìn)行降維壓縮菜秦,得到降維后的數(shù)據(jù)
甜害,
,……球昨,
尔店,最后得到新的訓(xùn)練樣本為
,
主慰,……嚣州,
。利用新的訓(xùn)練樣本訓(xùn)練出關(guān)于壓縮后的變量z的預(yù)測函數(shù)
共螺。
思考:針對圖片分類問題该肴,使用PCA算法進(jìn)行數(shù)據(jù)降維,與直接把圖片進(jìn)行縮放處理相比藐不,有什么異同點匀哄?
需要注意的是,PCA算法只用來處理訓(xùn)練樣本佳吞,運行PCA算法得到的轉(zhuǎn)換參數(shù)可以直接用來對交叉驗證數(shù)據(jù)集
及測試數(shù)據(jù)集
進(jìn)行轉(zhuǎn)換拱雏。當(dāng)然棉安,還需要相應(yīng)地對數(shù)據(jù)進(jìn)行歸一化處理以及對數(shù)據(jù)進(jìn)行縮放底扳。
4.人臉識別
本節(jié)使用英國劍橋AT&T實驗室的研究人員自拍的一組照片(AT&TLaboratories Cambridge),來開發(fā)一個特定的人臉識別系統(tǒng)贡耽。人臉識別衷模,本質(zhì)上是個分類問題鹊汛,需要把人臉圖片當(dāng)成訓(xùn)練數(shù)據(jù)集,對模型進(jìn)行訓(xùn)練阱冶。訓(xùn)練好的模型刁憋,就可以對新的人臉照片進(jìn)行類別預(yù)測。這就是人臉識別系統(tǒng)的原理木蹬。
1.加載數(shù)據(jù)集
從下面的地址可以下面數(shù)據(jù)集:http://www.cl.cam.ac.uk/research/dtg/attarchive/facesataglance.html至耻,查看數(shù)據(jù)集里所有400張照片的縮略圖。數(shù)據(jù)集總共包含40位人員的照片镊叁,每個人10張照片尘颓。
下載完照片,就可以使用下面的代碼來加載這些照片了:
import time
import logging
from sklearn.datasets import fetch_olivetti_faces
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
data_home='datasets/'
logging.info('Start to load dataset')
faces = fetch_olivetti_faces(data_home=data_home)
logging.info('Done with load dataset')
輸出如下:
2019-06-23 21:45:13,639 Start to load dataset
2019-06-23 21:45:13,666 Done with load dataset
加載的圖片數(shù)據(jù)集保存在faces變量里晦譬,scikit-learn已經(jīng)替我們把每張照片做了初步的處理疤苹,剪裁成64×64大小且人臉居中顯示。這一步至關(guān)重要敛腌,否則我們的模型將被大量的噪聲數(shù)據(jù)卧土,即圖片背景干擾。因為人臉識別的關(guān)鍵是五官紋理和特征像樊,每張照片的背景都不同尤莺,人的發(fā)型也可能經(jīng)常變化,這些特征都應(yīng)該盡量排除在輸入特征之外凶硅。最后缝裁,要成功加載數(shù)據(jù)集,還需要安裝Python的圖片處理工具包Pillow足绅。
成功加載數(shù)據(jù)后捷绑,其data里保存的就是按照scikit-learn要求的訓(xùn)練數(shù)據(jù)集,target里保存的就是類別目標(biāo)索引氢妈。我們通過下面的代碼粹污,將數(shù)據(jù)集的概要信息顯示出來:
import numpy as np
X = faces.data
y = faces.target
targets = np.unique(faces.target)
target_names = np.array(["c%d" % t for t in targets])
n_targets = target_names.shape[0]
n_samples, h, w = faces.images.shape
print('Sample count: {}\nTarget count: {}'.format(n_samples, n_targets))
print('Image size: {}x{}\nDataset shape: {}\n'.format(w, h, X.shape))
輸出如下:
Sample count: 400
Target count: 40
Image size: 64x64
Dataset shape: (400, 4096)
從輸出可知,總共有40位人物的照片首量,圖片總數(shù)是400張壮吩,輸入特征有4096個。為了后續(xù)區(qū)分不同的人物加缘,我們用索引號給目標(biāo)人物命名鸭叙,并保存在變量target_names里。為了更直觀地觀察數(shù)據(jù)拣宏,從每個人物的照片里隨機(jī)選擇一張顯示出來沈贝。先定義一個函數(shù)來顯示照片陣列:
%matplotlib inline
import matplotlib.pyplot as plt
def plot_gallery(images,titles,h,w,n_row=2,n_col=5):
"""顯示圖片陣列"""
plt.figure(figsize=(2*n_col,2*n_row),dpi=144)
plt.subplots_adjust(bottom=0,left=0.01,right=0.99,top=0.90,hspace=0.01)
for i in range(n_row*n_col):
plt.subplot(n_row,n_col,i+1)
plt.imshow(images[i].reshape((h,w)), cmap=plt.cm.gray)
plt.title(titles[i])
plt.axis('off')
輸入?yún)?shù)images是一個二維數(shù)據(jù),每一行都是一個圖片數(shù)據(jù)勋乾。在加載數(shù)據(jù)時宋下,fetch_olivetti_faces()函數(shù)已經(jīng)幫我們做了預(yù)處理嗡善,圖片的每個像素的RGB值都轉(zhuǎn)換成了[0,1]的浮點數(shù)。因此学歧,我們畫出來的照片將是黑白的罩引,而不是彩色的。在圖片識別領(lǐng)域枝笨,一般情況下用黑白照片就可以了袁铐,可以減少計算量,也會讓模型更準(zhǔn)確横浑。
接著分成兩行顯示出這些人物的照片:
n_row = 2
n_col = 6
sample_images = None
sample_titles = []
for i in range(n_targets):
people_images = X[y==i]
people_sample_index = np.random.randint(0, people_images.shape[0], 1)
people_sample_image = people_images[people_sample_index, :]
if sample_images is not None:
sample_images = np.concatenate((sample_images, people_sample_image), axis=0)
else:
sample_images = people_sample_image
sample_titles.append(target_names[i])
plot_gallery(sample_images, sample_titles, h, w, n_row, n_col)
代碼中昭躺,X[y==i]可以選擇出屬于特定人物的所有照片,隨機(jī)選擇出來的照片都放在sample_images數(shù)組對象里伪嫁,最后使用我們之前定義的函數(shù)plot_gallery()把照片畫出來领炫,如下圖所示。
從圖片中可以看到张咳,fetch_olivetti_faces()函數(shù)幫我們剪裁了中間部分帝洪,只留下臉部特征。如果想對比原圖脚猾,可以到主頁http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html下載原圖對比葱峡。
最后,把數(shù)據(jù)集劃分成訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集:
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=4)
2.一次失敗的嘗試
我們使用支持向量機(jī)來實現(xiàn)人臉識別:
from sklearn.svm import SVC
from time import time
start = time()
print('Fitting train datasets ...')
clf = SVC(class_weight='balanced')
clf.fit(X_train,y_train)
print('Done in {0:.2f}s'.format(time()-start))
輸出如下:
Fitting train datasets ...
Done in 0.92s
指定SVC的class_weight參數(shù)龙助,讓SVC模型能根據(jù)訓(xùn)練樣本的數(shù)量來均衡地調(diào)整權(quán)重砰奕,這對不均勻的數(shù)據(jù)集,即目標(biāo)人物的照片數(shù)量相差較大的情況是非常有幫助的提鸟。由于總共只有400張照片军援,數(shù)據(jù)規(guī)模較小,模型很快就運行完了称勋。
接著胸哥,針對測試數(shù)據(jù)集進(jìn)行預(yù)測:
start = time()
print('Predicting test dataset ...')
y_pred = clf.predict(X_test)
print('Done in {0:.2f}s'.format(time()-start))
輸出如下:
Predicting test dataset ...
Done in 0.10s
最后,分別使用confusion_matrix和classification_report來查看模型分類的準(zhǔn)確性赡鲜。
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test,y_pred,labels=range(n_targets))
print('confusion matrix:\n')
np.set_printoptions(threshold=np.nan)
print(cm)
np.set_printoptions()是為了確保完整地輸出cm數(shù)組的內(nèi)容空厌,這是因為這個數(shù)組是40×40的,默認(rèn)情況下不會全部輸出银酬。輸出如下:
confusion matrix:
[[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
confusion matrix理想的輸出嘲更,是矩陣的對角線上有數(shù)字,其他地方都沒有數(shù)字揩瞪。但我們的結(jié)果顯示不是這樣的赋朦。可以明顯看出,很多圖片都被預(yù)測成索引為12的類別了北发。結(jié)果看起來完全不對,這是怎么回事呢喷屋?我們再看一下classification_report的結(jié)果:
from sklearn.metrics import classification_report
print(classification_report(y_test,y_pred,target_names=target_names))
輸出如下:
precision recall f1-score support
c0 0.00 0.00 0.00 1
c1 0.00 0.00 0.00 3
c2 0.00 0.00 0.00 2
c3 0.00 0.00 0.00 1
c4 0.00 0.00 0.00 1
c5 0.00 0.00 0.00 1
c6 0.00 0.00 0.00 4
c7 0.00 0.00 0.00 2
c8 0.00 0.00 0.00 4
c9 0.00 0.00 0.00 2
c10 0.00 0.00 0.00 1
c11 0.00 0.00 0.00 0
c12 0.00 0.00 0.00 4
c13 0.00 0.00 0.00 4
c14 0.00 0.00 0.00 1
c15 0.00 0.00 0.00 1
c16 0.00 0.00 0.00 3
c17 0.00 0.00 0.00 2
c18 0.00 0.00 0.00 2
c19 0.00 0.00 0.00 2
c20 0.00 0.00 0.00 1
c21 0.00 0.00 0.00 2
c22 0.00 0.00 0.00 3
c23 0.00 0.00 0.00 2
c24 0.00 0.00 0.00 3
c25 0.00 0.00 0.00 3
c26 0.00 0.00 0.00 2
c27 0.00 0.00 0.00 2
c28 0.00 0.00 0.00 0
c29 0.00 0.00 0.00 2
c30 0.00 0.00 0.00 2
c31 0.00 0.00 0.00 3
c32 0.00 0.00 0.00 2
c33 0.00 0.00 0.00 2
c34 0.00 0.00 0.00 0
c35 0.00 0.00 0.00 2
c36 0.00 0.00 0.00 3
c37 0.00 0.00 0.00 1
c38 0.00 0.00 0.00 2
c39 0.00 0.00 0.00 2
avg / total 0.00 0.00 0.00 80
40個類別里琳拨,查準(zhǔn)率、召回率屯曹、F1 Score全為0狱庇,不能有更差的預(yù)測結(jié)果了。為什么恶耽?哪里出了差錯密任?
答案是,我們把每個像素都作為一個輸入特征來處理偷俭,這樣的數(shù)據(jù)噪聲太嚴(yán)重了浪讳,模型根本沒有辦法對訓(xùn)練數(shù)據(jù)集進(jìn)行擬合。想想看涌萤,我們總共有4096個特征淹遵,可是數(shù)據(jù)集大小才400個,比特征個數(shù)還少负溪,而且我們還需要把數(shù)據(jù)集分出20%來作為測試數(shù)據(jù)集透揣,這樣訓(xùn)練數(shù)據(jù)集就更小了。這樣的狀況下川抡,模型根本無法進(jìn)行準(zhǔn)確地訓(xùn)練和預(yù)測辐真。
3.使用PCA來處理數(shù)據(jù)集
解決上述問題的一個辦法是使用PCA來給數(shù)據(jù)降維,只選擇前k個最重要的特征崖堤。問題來了侍咱,選擇多少個特征合適呢?即怎么確定k的值密幔?PCA算法可以通過下面的公式來計算失真幅度:
在scikit-learn里放坏,可以從PCA模型的explained_variance_ratio_變量里獲取經(jīng)PCA處理后的數(shù)據(jù)還原率。這是一個數(shù)組老玛,所有元素求和即可知道我們選擇的k值的數(shù)據(jù)還原率淤年,數(shù)值越大說明失真越小,隨著k值的增大蜡豹,數(shù)值會無限接近于1麸粮。
利用這一特性,可以讓k取值10~300之間镜廉,每隔30進(jìn)行一次取樣弄诲。在所有的k值樣本下,計算經(jīng)過PCA算法處理后的數(shù)據(jù)還原率。然后根據(jù)數(shù)據(jù)還原率要求齐遵,來確定合理的k值寂玲。針對我們的情況,選擇失真度小于5%梗摇,即PCA處理后能保留95%的原數(shù)據(jù)信息拓哟。其代碼如下:
from sklearn.decomposition import PCA
print("Exploring explained variance ratio for dataset ...")
candidate_components = range(10,300,30)
explained_ratios = []
start = time()
for c in candidate_components:
pca = PCA(n_components=c)
X_pca = pca.fit_transform(X)
explained_ratios.append(np.sum(pca.explained_variance_ratio_))
print('Done in {0:.2f}s'.format(time()-start))
輸出如下:
Exploring explained variance ratio for dataset ...
Done in 0.75s
根據(jù)不同的k值,構(gòu)建PCA模型伶授,然后調(diào)用fit_transform()函數(shù)來處理數(shù)據(jù)集断序,再把模型處理后數(shù)據(jù)還原率,放入explained_ratios數(shù)組糜烹。接著把這個數(shù)組畫出來:
plt.figure(figsize=(10,6),dpi=144)
plt.grid()
plt.plot(candidate_components,explained_ratios)
plt.xlabel('Number of PCA Components')
plt.ylabel('Explained Variance Ratio')
plt.title('Explained variance ratio for PCA')
plt.yticks(np.arange(0.5,1.05,0.05))
plt.xticks(np.arange(0,300,20))
上圖中橫坐標(biāo)表示k值违诗,縱坐標(biāo)表示數(shù)據(jù)還原率。從圖中可以看出疮蹦,要保留95%以上的數(shù)據(jù)還原率诸迟,k值選擇140即可。根據(jù)上圖愕乎,也可以非常容易地找出不同的數(shù)據(jù)還原率所對應(yīng)的k值亮蒋。為了更直觀地觀察和對比在不同數(shù)據(jù)還原率下的數(shù)據(jù),我們選擇數(shù)據(jù)還原率分別在95%妆毕、90%慎玖、80%、70%笛粘、60%的情況下趁怔,這些數(shù)據(jù)還原率對應(yīng)的k值分別是140、75薪前、37润努、19、8示括,畫出經(jīng)PCA處理后的圖片铺浇。
為了方便,這里直接選擇前面的人物圖片中的前5位作為我們的樣本圖片垛膝。每行畫出5個圖片鳍侣,先畫出原圖,接著再畫出每行在不同數(shù)據(jù)還原率下對應(yīng)的圖片吼拥。
def title_prefix(prefix, title):
return "{}: {}".format(prefix, title)
n_row = 1
n_col = 5
sample_images = sample_images[0:5]
sample_titles = sample_titles[0:5]
plotting_images = sample_images
plotting_titles = [title_prefix('orig', t) for t in sample_titles]
candidate_components = [140, 75, 37, 19, 8]
for c in candidate_components:
print("Fitting and projecting on PCA(n_components={}) ...".format(c))
start = time()
pca = PCA(n_components=c)
pca.fit(X)
X_sample_pca = pca.transform(sample_images)
X_sample_inv = pca.inverse_transform(X_sample_pca)
plotting_images = np.concatenate((plotting_images, X_sample_inv), axis=0)
sample_title_pca = [title_prefix('{}'.format(c), t) for t in sample_titles]
plotting_titles = np.concatenate((plotting_titles, sample_title_pca), axis=0)
print("Done in {0:.2f}s".format(time() - start))
print("Plotting sample image with different number of PCA conpoments ...")
plot_gallery(plotting_images, plotting_titles, h, w,
n_row * (len(candidate_components) + 1), n_col)
代碼里倚聚,我們把所有的圖片收集進(jìn)plotting_images數(shù)組,然后調(diào)用前面定義的plot_gallery()函數(shù)一次性地畫出來凿可。如下圖所示惑折。
圖中第1行顯示的是原圖,第2行顯示的是數(shù)據(jù)還原度在95%處,即k=140的圖片惨驶;第2行顯示的是數(shù)據(jù)還原度在90%處白热,即k=90的圖片;依此類推粗卜“T啵可以直觀地觀察到贯底,原圖和95%數(shù)據(jù)還原率的圖片沒有太大差異番电。另外聚谁,即使在k=8時评疗,圖片依然能比較清楚地反映出人物的臉部特征輪廓测砂。
4.最終結(jié)果
接下來問題就變得簡單了。我們選擇k=140作為PCA參數(shù)百匆,對訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集進(jìn)行特征提取砌些。
n_components = 140
print("Fitting PCA by using training data ...")
start = time()
pca = PCA(n_components=n_components, svd_solver='randomized', whiten=True).fit(X_train)
print("Done in {0:.2f}s".format(time() - start))
print("Projecting input data for PCA ...")
start = time()
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)
print("Done in {0:.2f}s".format(time() - start))
輸出如下:
Fitting PCA by using training data ...
Done in 0.08s
Projecting input data for PCA ...
Done in 0.01s
接著使用GridSearchCV來選擇一個最佳的SVC模型參數(shù),然后使用最佳參數(shù)對模型進(jìn)行訓(xùn)練加匈。
from sklearn.model_selection import GridSearchCV
print("Searching the best parameters for SVC ...")
param_grid = {'C': [1, 5, 10, 50, 100],
'gamma': [0.0001, 0.0005, 0.001, 0.005, 0.01]}
clf = GridSearchCV(SVC(kernel='rbf', class_weight='balanced'), param_grid, verbose=2, n_jobs=4)
clf = clf.fit(X_train_pca, y_train)
print("Best parameters found by grid search:")
print(clf.best_params_)
這一步執(zhí)行時間比較長存璃,因為GridSearchCV使用矩陣式搜索法,對每組參數(shù)組合進(jìn)行一次訓(xùn)練雕拼,然后找出最好的參數(shù)的模型纵东。我們通過設(shè)置n_jobs=4來啟動4個線程并發(fā)執(zhí)行,同時設(shè)置verbose=2來輸出一些過程信息啥寇。最終選擇出來的最佳模型參數(shù)如下:
Best parameters found by grid search:
{'C': 5, 'gamma': 0.001}
接著使用這一模型對測試樣本進(jìn)行預(yù)測偎球,并且使用confusion_matrix輸出預(yù)測準(zhǔn)確性信息。
start = time()
print("Predict test dataset ...")
y_pred = clf.best_estimator_.predict(X_test_pca)
cm = confusion_matrix(y_test, y_pred, labels=range(n_targets))
print("Done in {0:.2f}.\n".format(time()-start))
print("confusion matrix:")
np.set_printoptions(threshold=np.nan)
print(cm)
輸出如下:
Predict test dataset ...
Done in 0.01.
confusion matrix:
[[1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2]]
從輸出的對角線上的數(shù)據(jù)可以看出辑甜,大部分預(yù)測結(jié)果都正確衰絮。我們再使用classification_report輸出分類報告,查看測準(zhǔn)率磷醋,召回率及F1 Score猫牡。
print(classification_report(y_test, y_pred))
輸出如下:
precision recall f1-score support
0 0.50 1.00 0.67 1
1 1.00 0.67 0.80 3
2 1.00 0.50 0.67 2
3 1.00 1.00 1.00 1
4 0.50 1.00 0.67 1
5 1.00 1.00 1.00 1
6 1.00 0.75 0.86 4
7 1.00 1.00 1.00 2
8 1.00 1.00 1.00 4
9 1.00 1.00 1.00 2
10 1.00 1.00 1.00 1
12 1.00 1.00 1.00 4
13 1.00 1.00 1.00 4
14 1.00 1.00 1.00 1
15 1.00 1.00 1.00 1
16 0.75 1.00 0.86 3
17 1.00 1.00 1.00 2
18 1.00 1.00 1.00 2
19 1.00 1.00 1.00 2
20 1.00 1.00 1.00 1
21 1.00 1.00 1.00 2
22 0.75 1.00 0.86 3
23 1.00 1.00 1.00 2
24 1.00 1.00 1.00 3
25 1.00 0.67 0.80 3
26 1.00 1.00 1.00 2
27 1.00 1.00 1.00 2
29 1.00 1.00 1.00 2
30 1.00 1.00 1.00 2
31 1.00 1.00 1.00 3
32 1.00 1.00 1.00 2
33 1.00 1.00 1.00 2
35 1.00 1.00 1.00 2
36 1.00 1.00 1.00 3
37 1.00 1.00 1.00 1
38 1.00 1.00 1.00 2
39 1.00 1.00 1.00 2
avg / total 0.97 0.95 0.95 80
在總共只有400張圖片,每位目標(biāo)人物只有10張圖片的情況下邓线,測準(zhǔn)率和召回率平均達(dá)到了0.95以上淌友,這是一個非常了不起的性能。
5.拓展閱讀
PCA算法的推導(dǎo)涉及大量的線性代數(shù)的知識骇陈。張洋先生的一篇博客《PCA的數(shù)學(xué)原理》亩进,基本上做到了從最基礎(chǔ)的內(nèi)容談起,一步步地推導(dǎo)出PCA算法缩歪,值得一讀归薛。
此外,孟巖先生的幾篇博客中也介紹了矩陣及其相關(guān)運算的物理含義,深入淺出主籍,讀后猶如醍醐灌頂习贫,這些博文分別是《理解矩陣(一)》,《理解矩陣(二)》和《理解矩陣(三)》千元。
最后推薦的是網(wǎng)易公開課的一個視頻課程:麻省理工公開課《線性代數(shù)》苫昌,是一個質(zhì)量很高的線性代數(shù)課程,感興趣的可以查閱幸海。