(白寧超 2018年10月22日10:14:18)html
摘要:主成分分析(英語:Principal components analysis,PCA)是一種分析、簡化數據集的技術。主成分分析常常用於減小數據集的維數,同時保持數據集中的對方差貢獻最大的特徵。經常應用在文本處理、人臉識別、圖片識別、天然語言處理等領域。能夠作在數據預處理階段很是重要的一環,本文首先對基本概念進行介紹,而後給出PCA算法思想、流程、優缺點等等。最後經過一個綜合案例去實現應用。(本文原創,轉載必須註明出處.)python
降維是對數據高維度特徵的一種預處理方法。降維是將高維度的數據保留下最重要的一些特徵,去除噪聲和不重要的特徵,從而實現提高數據處理速度的目的。在實際的生產和應用中,降維在必定的信息損失範圍內,能夠爲咱們節省大量的時間和成本。降維也成爲了應用很是普遍的數據預處理方法。git
咱們正經過電視觀看體育比賽,在電視的顯示器上有一個足球。顯示器大概包含了100萬像素點,而球則多是由較少的像素點組成,例如說一千個像素點。人們實時的將顯示器上的百萬像素轉換成爲一個三維圖像,該圖像就給出運動場上球的位置。在這個過程當中,人們已經將百萬像素點的數據,降至爲三維。這個過程就稱爲降維(dimensionality reduction)github
數據降維的目的:算法
適用範圍:api
常見降維技術(PCA的應用目前最爲普遍)數組
主成分分析(Principal Component Analysis, PCA):通俗理解:就是找出一個最主要的特徵,而後進行分析。機器學習
主成分分析(英語:Principal components analysis,PCA)是一種分析、簡化數據集的技術。主成分分析常常用於減小數據集的維數,同時保持數據集中的對方差貢獻最大的特徵。這是經過保留低階主成分,忽略高階主成分作到的。這樣低階成分每每可以保留住數據的最重要方面。可是,這也不是必定的,要視具體應用而定。因爲主成分分析依賴所給數據,因此數據的準確性對分析結果影響很大。函數
主成分分析由卡爾·皮爾遜於1901年發明,用於分析數據及創建數理模型。其方法主要是經過對協方差矩陣進行特徵分解,以得出數據的主成分(即特徵向量)與它們的權值(即特徵值)。PCA是最簡單的以特徵量分析多元統計分佈的方法。其結果能夠理解爲對原數據中的方差作出解釋:哪個方向上的數據值對方差的影響最大?換而言之,PCA提供了一種下降數據維度的有效辦法;若是分析者在原數據中除掉最小的特徵值所對應的成分,那麼所得的低維度數據一定是最優化的(也即,這樣下降維度一定是失去訊息最少的方法)。主成分分析在分析複雜數據時尤其有用,好比人臉識別。oop
PCA是最簡單的以特徵量分析多元統計分佈的方法。一般狀況下,這種運算能夠被看做是揭露數據的內部結構,從而更好的解釋數據的變量的方法。若是一個多元數據集可以在一個高維數據空間座標系中被顯現出來,那麼PCA就可以提供一幅比較低維度的圖像,這幅圖像即爲在訊息最多的點上原對象的一個‘投影’。這樣就能夠利用少許的主成分使得數據的維度下降了。PCA跟因子分析密切相關,而且已經有不少混合這兩種分析的統計包。而真實要素分析則是假定底層結構,求得微小差別矩陣的特徵向量。
PCA 場景
例如: 考察一我的的智力狀況,就直接看數學成績就行(存在:數學、語文、英語成績)
PCA 思想
去除平均值 計算協方差矩陣 計算協方差矩陣的特徵值和特徵向量 將特徵值排序 保留前N個最大的特徵值對應的特徵向量 將數據轉換到上面獲得的N個特徵向量構建的新空間中(實現了特徵壓縮)
PCA 原理
PCA 算法流程
下面咱們看看具體的算法流程。
輸入:n維樣本集\( D=(x^{(1)},x^{(2)},...,x^{(m)}) \),要降維到的維數n.
輸出:降維後的樣本集\( D^′\)
1) 對全部的樣本進行中心化:\( x^{(i)}=x^{(i)}−\frac{1}{m}\sum_{j=1}^{m}x^{(j)} \)
2) 計算樣本的協方差矩陣\( XX^T\)
3) 對矩陣\( XX^T\)進行特徵值分解
4)取出最大的n'個特徵值對應的特徵向量\( (w_1,w_2,...,w_n^′) \), 將全部的特徵向量標準化後,組成特徵向量矩陣W。
5)對樣本集中的每個樣本\( x^{(i)}\),轉化爲新的樣本\( z^{(i)}=W^Tx^{(i)} \)
6) 獲得輸出樣本集\( D^′=(z^{(1)},z^{(2)},...,z^{(m)}) \)
PCA 優缺點
優勢:下降數據的複雜性,識別最重要的多個特徵。
缺點:不必定須要,且可能損失有用信息。
適用數據類型:數值型數據。
真實的訓練數據老是存在各類各樣的問題:
這時能夠採用主成分分析(PCA)的方法來解決部分上述問題。PCA的思想是將n維特徵映射到k維上(k<n),這k維是全新的正交特徵。這k維特徵稱爲主元,是從新構造出來的k維特徵,而不是簡單地從n維特徵中去除其他n-k維特徵。
收集數據:提供文本文件,文件名:testSet.txt.文本文件部分數據格式以下:
10.235186 11.321997 10.122339 11.810993 9.190236 8.904943 9.306371 9.847394 8.330131 8.340352 10.152785 10.123532 10.408540 10.821986 9.003615 10.039206 9.534872 10.096991 9.498181 10.825446 9.875271 9.233426 10.362276 9.376892 10.191204 11.250851
數據集處理代碼實現以下
'''加載數據集''' def loadDataSet(fileName, delim='\t'): fr = open(fileName) stringArr = [line.strip().split(delim) for line in fr.readlines()] datArr = [list(map(float, line)) for line in stringArr] #注意這裏和python2的區別,須要在map函數外加一個list(),不然顯示結果爲 map at 0x3fed1d0 return mat(datArr)
在等式 Av=λv 中,v 是特徵向量, λ 是特徵值。表示 若是特徵向量 v 被某個矩陣 A 左乘,那麼它就等於某個標量 λ 乘以 v.
幸運的是: Numpy 中有尋找特徵向量和特徵值的模塊 linalg,它有 eig() 方法,該方法用於求解特徵向量和特徵值。具體代碼實現以下:
'''pca算法 cov協方差=[(x1-x均值)*(y1-y均值)+(x2-x均值)*(y2-y均值)+...+(xn-x均值)*(yn-y均值)]/(n-1) Args: dataMat 原數據集矩陣 topNfeat 應用的N個特徵 Returns: lowDDataMat 降維後數據集 reconMat 新的數據集空間 ''' def pca(dataMat, topNfeat=9999999): # 計算每一列的均值 meanVals = mean(dataMat, axis=0) # print('meanVals', meanVals) # 每一個向量同時都減去均值 meanRemoved = dataMat - meanVals # print('meanRemoved=', meanRemoved) # rowvar=0,傳入的數據一行表明一個樣本,若非0,傳入的數據一列表明一個樣本 covMat = cov(meanRemoved, rowvar=0) # eigVals爲特徵值, eigVects爲特徵向量 eigVals, eigVects = linalg.eig(mat(covMat)) # print('eigVals=', eigVals) # print('eigVects=', eigVects) # 對特徵值,進行從小到大的排序,返回從小到大的index序號 # 特徵值的逆序就能夠獲得topNfeat個最大的特徵向量 eigValInd = argsort(eigVals) # print('eigValInd1=', eigValInd) # -1表示倒序,返回topN的特徵值[-1到-(topNfeat+1)不包括-(topNfeat+1)] eigValInd = eigValInd[:-(topNfeat+1):-1] # print('eigValInd2=', eigValInd) # 重組 eigVects 最大到最小 redEigVects = eigVects[:, eigValInd] # print('redEigVects=', redEigVects.T) # 將數據轉換到新空間 # print( "---", shape(meanRemoved), shape(redEigVects)) lowDDataMat = meanRemoved * redEigVects reconMat = (lowDDataMat * redEigVects.T) + meanVals # print('lowDDataMat=', lowDDataMat) # print('reconMat=', reconMat) return lowDDataMat, reconMat
接下來咱們查看降維後的數據與原始數據可視化效果,咱們將原始數據採用綠色△表示,降維後的數據採用紅色○表示。可視化代碼以下:
'''降維後的數據和原始數據可視化''' def show_picture(dataMat, reconMat): fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(dataMat[:, 0].flatten().A[0], dataMat[:, 1].flatten().A[0], marker='^', s=90,c='green') ax.scatter(reconMat[:, 0].flatten().A[0], reconMat[:, 1].flatten().A[0], marker='o', s=50, c='red') plt.show()
調用代碼:
# 2 主成分分析降維特徵向量設置 lowDmat, reconMat = pca(dataMat, 1) print(shape(lowDmat)) # 3 將降維後的數據和原始數據一塊兒可視化 show_picture(dataMat, reconMat)
運行結果顯示:
半導體是在一些極爲先進的工廠中製造出來的。設備的生命早期有限,而且花費極其巨大。雖然經過早期測試和頻繁測試來發現有瑕疵的產品,但仍有一些存在瑕疵的產品經過測試。若是咱們經過機器學習技術用於發現瑕疵產品,那麼它就會爲製造商節省大量的資金。具體來說,它擁有590個特徵。咱們看看可否對這些特徵進行降維處理。對於數據的缺失值的問題,將缺失值NaN(Not a Number縮寫),所有用平均值來替代(若是用0來處理的策略就太差了)。收集數據:提供文本文件,文件名:secom.data.文本文件部分數據格式以下:
3030.93 2564 2187.7333 1411.1265 1.3602 100 97.6133 0.1242 1.5005 0.0162 -0.0034 0.9455 202.4396 0 7.9558 414.871 10.0433 0.968 192.3963 12.519 1.4026 -5419 2916.5 -4043.75 751 0.8955 1.773 3.049 64.2333 2.0222 0.1632 3.5191 83.3971 9.5126 50.617 64.2588 49.383 66.3141 86.9555 117.5132 61.29 4.515 70 352.7173 10.1841 130.3691 723.3092 1.3072 141.2282 1 624.3145 218.3174 0 4.592
將數據集中NaN替換成平均值,代碼實現以下:
'''將NaN替換成平均值函數''' def replaceNanWithMean(): datMat = loadDataSet('./secom.data', ' ') numFeat = shape(datMat)[1] for i in range(numFeat): # 對value不爲NaN的求均值 # .A 返回矩陣基於的數組 meanVal = mean(datMat[nonzero(~isnan(datMat[:, i].A))[0], i]) # 將value爲NaN的值賦值爲均值 datMat[nonzero(isnan(datMat[:, i].A))[0],i] = meanVal return datMat
咱們拿到數據進行數據預處理以後,再跑下程序,看看中間結果若是,分析數據代碼以下:
'''分析數據''' def analyse_data(dataMat): meanVals = mean(dataMat, axis=0) meanRemoved = dataMat-meanVals covMat = cov(meanRemoved, rowvar=0) eigvals, eigVects = linalg.eig(mat(covMat)) eigValInd = argsort(eigvals) topNfeat = 20 eigValInd = eigValInd[:-(topNfeat+1):-1] cov_all_score = float(sum(eigvals)) sum_cov_score = 0 for i in range(0, len(eigValInd)): line_cov_score = float(eigvals[eigValInd[i]]) sum_cov_score += line_cov_score ''' 咱們發現其中有超過20%的特徵值都是0。 這就意味着這些特徵都是其餘特徵的副本,也就是說,它們能夠經過其餘特徵來表示,而自己並無提供額外的信息。 最前面15個值的數量級大於10^5,實際上那之後的值都變得很是小。 這就至關於告訴咱們只有部分重要特徵,重要特徵的數目也很快就會降低。 最後,咱們可能會注意到有一些小的負值,他們主要源自數值偏差應該四捨五入成0. ''' print('主成分:%s, 方差佔比:%s%%, 累積方差佔比:%s%%' % (format(i+1, '2.0f'), format(line_cov_score/cov_all_score*100, '4.2f'), format(sum_cov_score/cov_all_score*100, '4.1f')))
去均值化的特徵值結果顯示以下:
[ 5.34151979e+07 2.17466719e+07 8.24837662e+06 2.07388086e+06 1.31540439e+06 4.67693557e+05 2.90863555e+05 2.83668601e+05 2.37155830e+05 2.08513836e+05 1.96098849e+05 1.86856549e+05 1.52422354e+05 1.13215032e+05 1.08493848e+05 1.02849533e+05 1.00166164e+05 8.33473762e+04 8.15850591e+04 7.76560524e+04 ... 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 ]
數據分析結果以下:
主成分: 1, 方差佔比:59.25%, 累積方差佔比:59.3% 主成分: 2, 方差佔比:24.12%, 累積方差佔比:83.4% 主成分: 3, 方差佔比:9.15%, 累積方差佔比:92.5% 主成分: 4, 方差佔比:2.30%, 累積方差佔比:94.8% 主成分: 5, 方差佔比:1.46%, 累積方差佔比:96.3% 主成分: 6, 方差佔比:0.52%, 累積方差佔比:96.8% 主成分: 7, 方差佔比:0.32%, 累積方差佔比:97.1% 主成分: 8, 方差佔比:0.31%, 累積方差佔比:97.4% 主成分: 9, 方差佔比:0.26%, 累積方差佔比:97.7% 主成分:10, 方差佔比:0.23%, 累積方差佔比:97.9% 主成分:11, 方差佔比:0.22%, 累積方差佔比:98.2% 主成分:12, 方差佔比:0.21%, 累積方差佔比:98.4% 主成分:13, 方差佔比:0.17%, 累積方差佔比:98.5% 主成分:14, 方差佔比:0.13%, 累積方差佔比:98.7% 主成分:15, 方差佔比:0.12%, 累積方差佔比:98.8% 主成分:16, 方差佔比:0.11%, 累積方差佔比:98.9% 主成分:17, 方差佔比:0.11%, 累積方差佔比:99.0% 主成分:18, 方差佔比:0.09%, 累積方差佔比:99.1% 主成分:19, 方差佔比:0.09%, 累積方差佔比:99.2% 主成分:20, 方差佔比:0.09%, 累積方差佔比:99.3%
咱們發現其中有超過20%的特徵值都是0。這就意味着這些特徵都是其餘特徵的副本,也就是說,它們能夠經過其餘特徵來表示,而自己並無提供額外的信息。最前面值的數量級大於10^5,實際上那之後的值都變得很是小。這就至關於告訴咱們只有部分重要特徵,重要特徵的數目也很快就會降低。最後,咱們可能會注意到有一些小的負值,他們主要源自數值偏差應該四捨五入成0.
根據實驗結果咱們繪製半導體數據中前七個主要成分所佔的方差百分好比下
主成分 | 方差百分比(%) | 累積方差百分比(%) |
---|---|---|
1 | 59.25 | 59.3 |
2 | 24.12 | 83.4 |
3 | 9.15 | 92.5 |
4 | 2.30 | 94.8 |
5 | 1.46 | 96.3 |
6 | 0.52 | 96.8 |
7 | 0.32 | 97.1 |
20 | 0.09 | 99.3 |
調用咱們上文寫的代碼以下:
lowDmat, reconMat = pca(dataMat, 20) print(shape(lowDmat)) show_picture(dataMat, reconMat)
運行結果以下:
本文版權歸做者全部,旨在技術交流使用。未經做者贊成禁止轉載,轉載後需在文章頁面明顯位置給出原文鏈接,不然相關責任自行承擔。