本文是對PCA和SVD學習的整理筆記,爲了不不少重複內容的工做,我會在介紹概念的時候引用其餘童鞋的工做和內容,具體來源我會標記在參考資料中。php
PCA(主成分分析)經過線性變換將原始數據變換爲一組各維度線性無關的表示,可用於提取數據的主要特徵份量,經常使用於高維數據的降維。html
爲何須要降維?如下圖爲例,圖c中的點x y 呈現明顯線性相關,假如以數據其實以數據點分佈的方向的直線上的投影(一維)已經可以很好的描述這組數據特色了 。明顯的,將數據維度下降:1可以下降數據計算量 2壓縮數據重構 3.部分狀況下甚至可以改善數據特徵。app
那麼如何在降維時儘可能保留源數據的特徵,PCA就是一種。關於如何理解,PCA,一般能夠用兩種方式進行理解:一是讓降維後的數據分佈儘可能分散可以保留信息(方差儘可能大) 二是降維致使的信息損失儘可能小。關於第一種理解方式,你們能夠參考這裏,細緻而清晰。第二種方法一般須要簡單的公式推導,利用拉格朗日乘子將帶約束的優化轉化爲無約束優化後求導,有興趣的童鞋能夠參考這裏.less
上面兩篇文章關於兩個不一樣方向解釋PCA,那麼這裏就直接寫出PCA的降維方法,假設原數據爲X:機器學習
設有m條n個特徵的數據。ide
1)將原始數據按列組成n行m列矩陣X,即每一列表明一組數據函數
2)將X的每一行(表明一個屬性字段)進行零均值化,即減去這一行的均值post
3)求出協方差矩陣\[C=\frac{1}{m}XX^{T}\]學習
4)求出協方差矩陣的特徵值及對應的特徵向量(對\[XX^{T}\]進行特徵分解)優化
5)將特徵向量按對應特徵值大小從上到下按行排列成矩陣,取前k行組成矩陣P
6)Y=PX即爲降維到k維後的數據
好了,PCA的流程中彷佛和奇異值彷佛沒有什麼關係。可是,首先\[XX^{T}\]計算過程當中若是有較小的值很容易形成精度損失,其次特徵分解只能處理方針,有沒有更方便的方式得到降維矩陣P,這就要用到SVD了。
首先,關於特徵分解和奇異值分解的物理意義理解,我推薦看這裏:
總結一下,特徵值分解和奇異值分解都是給一個矩陣(線性變換)找一組特殊的基,特徵值分解找到了特徵向量這組基,在這組基下該線性變換隻有縮放效果。而奇異值分解則是找到另外一組基,這組基下線性變換的旋轉、縮放、投影三種功能獨立地展現出來了, 簡而言之:
1.特徵值分解實際上是對旋轉縮放兩種效應的歸併
2.奇異值分解實際上是歲旋轉縮放和投影效應的歸併
也就是說,奇異值分解能夠說是包含了特徵分解!來看Wikipedia的解釋:
在矩陣M的奇異值分解中
![]()
這裏的*標識轉置T。看到其中U就是MM*的特徵向量了,那麼也就是說利用奇異值分解也能夠作PCA了,並且還不用求\[XX^{T}\]!
不只如此,單獨觀看奇異值分解的式子,咱們也能夠利用主成分的思想,利用奇異值分解的公式對高維數據進行壓縮,具體看下面的代碼。
from PIL import Image import numpy as np import matplotlib.pyplot as plt def decide_k(s, ratio): sum_tmp = 0 sum_s = np.sum(s) k = 0 for i in s: k += 1 sum_tmp += i if (sum_tmp / sum_s) >= ratio: print("reduce dims is:", k) return k if k >= s.shape: raise ValueError('input dim could not less than compress dims') def svd_refactor(x, ratio=0.90): # compress to a k dims data before = x.shape[0] * x.shape[1] print("before compress:", before) # after svd, save cu cv and cs ,then we could use them to refactor picture mean_ = np.mean(x, axis=1, keepdims=True) x = x - mean_ u, s, v = np.linalg.svd(x) k = decide_k(s, ratio) c_u = u[:, :k] c_v = v[:k, :] c_s = s[0:k] after = c_u.shape[0] * c_u.shape[1] + c_v.shape[0] * c_v.shape[1] + c_s.shape[0] print("after compress:", after) print("ratio", after / before) # refactor s_s = np.diag(c_s) return np.dot(c_u, np.dot(s_s, c_v)) def pca_refactor(x, ratio=0.90): # compress to a k dims data before = x.shape[0] * x.shape[1] print("before pca:", before) # after svd, save cu cv and cs ,then we could use them to refactor picture mean_ = np.mean(x, axis=1, keepdims=True) x = x - mean_ u, s, v = np.linalg.svd(x) k = decide_k(s, ratio) c_u = u[:, :k] eig_vec = c_u.transpose() pca_result = np.dot(eig_vec, x) after = c_u.shape[0] * c_u.shape[1] + pca_result.shape[0] * pca_result.shape[1] print("after pca:", after) print("ratio", after / before) # since U*U=I return np.dot(c_u, pca_result) if __name__ == '__main__': img_file = Image.open('test.jpg').convert('L') # convert picture to gray img_array = np.array(img_file) print(img_array.shape) img_array = pca_refactor(img_array) plt.figure("beauty") plt.imshow(img_array, cmap=plt.cm.gray) plt.axis('off') plt.show()
其中 關於如何選擇下降維度到多少維的decide_k函數,採用了貢獻率。就是指當剩餘特徵值和的比例小於必定百分比(0.05)的時候捨棄他們。
李航《統計學習方法》
Relationship between SVD and PCA. How to use SVD to perform PCA?