參考:
講一下numpy的矩陣特徵值分解與奇異值分解
主成分分析(PCA)原理詳解
源代碼: https://github.com/wucng/MLAndDL
1.特徵值分解
(1) 特徵值與特徵向量
如果一個向量是矩陣的特徵向量,將一定可以表示成下面的形式:
其中,是特徵向量對應的特徵值,一個矩陣的一組特徵向量是一組正交向量。
(2) 特徵值分解矩陣
從上面我們知道了特徵矩陣與特徵向量的定義,即滿足:
如果存在矩陣 有n個特徵值分別爲 ,對應的特徵向量分別爲:
把所有的特徵向量寫成矩陣形式:,特徵值寫成對角陣 ,則有:
如果特徵矩陣 是可逆的 則有 ,因爲特徵矩陣其實是正交矩陣 即:,因此
如果矩陣是正定的(可逆) 則有
import numpy as np
from numpy.linalg import eig,svd
A = np.random.random_sample([8,8])
C = np.dot(A.T, A)
# 特徵值分解
vals, vecs = eig(C)
# 重構
Lambda = np.diag(vals) # 特徵值對角陣
new_C = np.dot(np.dot(vecs, Lambda), vecs.T) # 與C=A.T*A相等
print(np.allclose(new_C,C)) # True
2.SVD(奇異值)分解
如果矩陣可逆,則可以進行特徵值分解;如果矩陣不可逆,則可以是SVD分解(並且可逆也能使用SVD分解)
奇異值分解是一個能適用於任意矩陣的一種分解的方法,對於任意矩陣A總是存在一個奇異值分解:
假設是一個的矩陣,那麼得到的是一個的方陣,裏面的正交向量被稱爲左奇異向量。是一個的矩陣,除了對角線其它元素都爲0,對角線上的元素稱爲奇異值。是的轉置矩陣,是一個的矩陣,它裏面的正交向量被稱爲右奇異值向量。而且一般來講,我們會將上的值按從大到小的順序排列。
SVD分解矩陣A的步驟:
- (1) 求的特徵值和特徵向量,用單位化的特徵向量構成 。
- 求的特徵值和特徵向量,用單位化的特徵向量構成 。
- 將或者的特徵值求平方根,然後構成 。
import numpy as np
from numpy.linalg import eig,svd
A = np.random.random_sample([8,6])
u, s, vh = np.linalg.svd(A) # 這裏vh爲V的轉置
print(u.shape,s.shape,vh.shape)
print(np.allclose(A, np.dot(u[:, :len(s)] * s, vh))) # True
3.PCA算法
- (1) 基於特徵值分解協方差矩陣實現PCA算法
輸入:數據集,需要降到維 。
-
去平均值(即去中心化),即每一位特徵減去各自的平均值。
-
計算協方差矩陣,注:這裏除或不除樣本數量或,其實對求出的特徵向量沒有影響。
-
用特徵值分解方法求協方差矩陣的特徵值與特徵向量。
-
對特徵值從大到小排序,選擇其中最大的個。然後將其對應的個特徵向量(對應的列向量)分別作爲列向量組成特徵向量矩陣。
-
將數據轉換到個特徵向量構建的新空間中,即。
- (2) 基於SVD分解協方差矩陣實現PCA算法
輸入:數據集,需要降到維 。
-
去平均值,即每一位特徵減去各自的平均值。
-
計算協方差矩陣。
-
通過SVD計算協方差矩陣的特徵值與特徵向量。
-
對特徵值從大到小排序,選擇其中最大的k個。然後將其對應的k個特徵向量(
右奇異矩陣
)分別作爲列向量
組成特徵向量矩陣。(左奇異矩陣可以用於對行數的壓縮;右奇異矩陣可以用於對列(即特徵維度)的壓縮。) -
將數據轉換到k個特徵向量構建的新空間中,即。
"""
Author:wucng
Time: 20200109
Summary: PCA 基於特徵值分解
源代碼: https://github.com/wucng/MLAndDL
"""
import scipy
# from scipy.misc import imread,imshow
from imageio import imread,imsave
import numpy as np
from numpy.linalg import eig,svd
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
class PCASelf(object):
def __init__(self,n_components=1,mode="svd"):
"""
:param n_components: 主成分數(壓縮後的特徵數)
:param mode: "eig" 特徵矩陣分解,"svd" 奇異值分解
"""
self.n_components = n_components
self.mode = mode
def fit_transform(self,X:np.array):
# 去平均值
X = X-np.mean(X,0)
# 協方差矩陣
A = np.matmul(X.T,X)#/len(X)
if self.mode == "eig":
# 計算協方差矩陣的特徵值與特徵向量
vals, vecs = eig(A)
else:
# u, s, vh = np.linalg.svd(A) # 這裏vh爲V的轉置
_, vals, vecs = svd(A)
# 對特徵值從大到小排序,選擇其中最大的k個
index = np.argsort(vals*(-1))[:self.n_components] # 默認是從小到大排序,乘上-1 後就變成從大到小排序
# 根據選擇的K個特徵值組成新的特徵向量矩陣(列對應特徵向量,而不是行)
P = vecs[:,index]
# 特徵壓縮後的矩陣
return np.matmul(X,P)
if __name__=="__main__":
# 自定義方法
X = np.array([[-1, 1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
pca = PCASelf(1)
print(pca.fit_transform(X))
# sklearn 方法
print(PCA(1).fit_transform(X))