1. 程式人生 > >PCA, SVD以及代碼示例

PCA, SVD以及代碼示例

swe erro ict ef7 計算 component pict 需要 wikipedia

本文是對PCA和SVD學習的整理筆記,為了避免很多重復內容的工作,我會在介紹概念的時候引用其他童鞋的工作和內容,具體來源我會標記在參考資料中。

一.PCA (Principle component analysis)

PCA(主成分分析)通過線性變換將原始數據變換為一組各維度線性無關的表示,可用於提取數據的主要特征分量,常用於高維數據的降維。

為什麽需要降維?以下圖為例,圖c中的點x y 呈現明顯線性相關,假如以數據其實以數據點分布的方向的直線上的投影(一維)已經能夠很好的描述這組數據特點了 。明顯的,將數據維度降低:1能夠降低數據計算量 2壓縮數據重構 3.部分情況下甚至能夠改善數據特征。

技術分享

  那麽如何在降維時盡量保留源數據的特征,PCA就是一種。關於如何理解,PCA,通常可以用兩種方式進行理解:一是讓降維後的數據分布盡量分散能夠保留信息(方差盡量大) 二是降維導致的信息損失盡量小。關於第一種理解方式,大家可以參考這裏,細致而清晰。第二種方法通常需要簡單的公式推導,利用拉格朗日乘子將帶約束的優化轉化為無約束優化後求導,有興趣的童鞋可以參考這裏.

上面兩篇文章關於兩個不同方向解釋PCA,那麽這裏就直接寫出PCA的降維方法,假設原數據為X:

    設有m條n個特征的數據。

    1)將原始數據按列組成n行m列矩陣X,即每一列代表一組數據

    2)將X的每一行(代表一個屬性字段)進行零均值化,即減去這一行的均值

    3)求出協方差矩陣\[C=\frac{1}{m}XX^{T}\]

    4)求出協方差矩陣的特征值及對應的特征向量(對\[XX^{T}\]進行特征分解)

    5)將特征向量按對應特征值大小從上到下按行排列成矩陣,取前k行組成矩陣P

    6)Y=PX即為降維到k維後的數據

好了,PCA的流程中似乎和奇異值似乎沒有什麽關系。但是,首先\[XX^{T}\]計算過程中如果有較小的值很容易造成精度損失,其次特征分解只能處理方針,有沒有更方便的方式獲得降維矩陣P,這就要用到SVD了。

二 SVD(Singular value decomposition)

首先,關於特征分解和奇異值分解的物理意義理解,我推薦看這裏:

總結一下,特征值分解和奇異值分解都是給一個矩陣(線性變換)找一組特殊的基,特征值分解找到了特征向量這組基,在這組基下該線性變換只有縮放效果。而奇異值分解則是找到另一組基,這組基下線性變換的旋轉、縮放、投影三種功能獨立地展示出來了, 簡而言之:

1.特征值分解其實是對旋轉縮放兩種效應的歸並

2.奇異值分解其實是歲旋轉縮放和投影效應的歸並

 也就是說,奇異值分解可以說是包含了特征分解!來看Wikipedia的解釋:

在矩陣M的奇異值分解中

技術分享
  • V的列(columns)組成一套對技術分享的正交"輸入"或"分析"的基向量。這些向量是技術分享的特征向量。
  • U的列(columns)組成一套對技術分享的正交"輸出"的基向量。這些向量是技術分享的特征向量。
  • Σ對角線上的元素是奇異值,可視為是在輸入與輸出間進行的標量的"膨脹控制"。這些是技術分享技術分享的特征值的非負平方根,並與UV的行向量相對應。

這裏的*標識轉置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)的時候舍棄他們。

Reference:

李航《統計學習方法》

PCA的數學原理

機器學習中的數學(5)-強大的矩陣奇異值分解(SVD)及其應用

機器學習中的SVD和PCA.知乎

奇異值的物理意義是什麽?

矩陣的奇異值與特征值有什麽相似之處與區別之處

從PCA和SVD的關系拾遺

PCA, SVD以及代碼示例