03.PCA主成分分析

參考:
講一下numpy的矩陣特徵值分解與奇異值分解
主成分分析(PCA)原理詳解
源代碼: https://github.com/wucng/MLAndDL


1.特徵值分解

(1) 特徵值與特徵向量

如果一個向量vv是矩陣AA的特徵向量,將一定可以表示成下面的形式:

Av=λvAv=\lambda v

其中,λλ是特徵向量vv對應的特徵值,一個矩陣的一組特徵向量是一組正交向量。

(2) 特徵值分解矩陣

從上面我們知道了特徵矩陣與特徵向量的定義,即滿足:Av=λvAv=\lambda v

如果存在矩陣ARnxnA\in R^{n\mathrm{x}n} 有n個特徵值分別爲 λ1,λ2,...λn\lambda_1,\lambda_2,...\lambda_n,對應的特徵向量分別爲:v1,v2,...vnv_1,v_2,...v_n

把所有的特徵向量寫成矩陣形式:VRnxnV \in R^{n \mathrm{x} n},特徵值寫成對角陣 DRnxnD \in R^{n \mathrm{x} n},則有:AV=VDAV=VD

如果特徵矩陣VV 是可逆的 則有 A=VDV1=VDVTA=VDV^{-1}=VDV^T ,因爲特徵矩陣其實是正交矩陣 即:VVT=EVV^T=E,因此 V1=VTV^{-1}=V^T

如果矩陣AA是正定的(可逆) 則有 A=VDVTA=VDV^T

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(奇異值)分解

如果矩陣AA可逆,則可以進行特徵值分解;如果矩陣AA不可逆,則可以是SVD分解(並且AA可逆也能使用SVD分解)

奇異值分解是一個能適用於任意矩陣的一種分解的方法,對於任意矩陣A總是存在一個奇異值分解:

A=UΣVTA = U\Sigma V^T

假設AA是一個mnm*n的矩陣,那麼得到的UU是一個mmm*m的方陣,UU裏面的正交向量被稱爲左奇異向量。ΣΣ是一個mnm*n的矩陣,ΣΣ除了對角線其它元素都爲0,對角線上的元素稱爲奇異值。是VV的轉置矩陣,是一個nnn*n的矩陣,它裏面的正交向量被稱爲右奇異值向量。而且一般來講,我們會將ΣΣ上的值按從大到小的順序排列。

SVD分解矩陣A的步驟:

  • (1) 求AATAA^T的特徵值和特徵向量,用單位化的特徵向量構成 UU
  • ATAA^TA的特徵值和特徵向量,用單位化的特徵向量構成 VV
  • AATAA^T或者ATAA^TA的特徵值求平方根,然後構成 ΣΣ
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算法

輸入:數據集X={x1,x2,...,xn}XRnmX=\{x_1,x_2,...,x_n\},X \in R^ {n*m},需要降到kkXRnk,k<mX' \in R^ {n*k} , k<m

  1. 去平均值(即去中心化),即每一位特徵減去各自的平均值。

  2. 計算協方差矩陣XTXX^TX,注:這裏除或不除樣本數量nnn1n-1,其實對求出的特徵向量沒有影響。

  3. 用特徵值分解方法求協方差矩陣XTXX^TX的特徵值與特徵向量。

  4. 對特徵值從大到小排序,選擇其中最大的kk個。然後將其對應的kk個特徵向量(對應的列向量)分別作爲列向量組成特徵向量矩陣PP

  5. 將數據轉換到kk個特徵向量構建的新空間中,即Y=XPY=XP


  • (2) 基於SVD分解協方差矩陣實現PCA算法

輸入:數據集X={x1,x2,...,xn}XRnmX=\{x_1,x_2,...,x_n\},X \in R^ {n*m},需要降到kkXRnk,k<mX' \in R^ {n*k} , k<m

  1. 去平均值,即每一位特徵減去各自的平均值。

  2. 計算協方差矩陣XTXX^TX

  3. 通過SVD計算協方差矩陣的特徵值與特徵向量。

  4. 對特徵值從大到小排序,選擇其中最大的k個。然後將其對應的k個特徵向量(右奇異矩陣)分別作爲列向量組成特徵向量矩陣PP。(左奇異矩陣可以用於對行數的壓縮;右奇異矩陣可以用於對列(即特徵維度)的壓縮。)

  5. 將數據轉換到k個特徵向量構建的新空間中,即Y=XPY=XP

"""
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))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章