PCA, SVD以及代碼示例

    本文是對PCA和SVD學習的整理筆記,爲了不不少重複內容的工做,我會在介紹概念的時候引用其餘童鞋的工做和內容,具體來源我會標記在參考資料中。php

一.PCA (Principle component analysis)

       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了。

二 SVD(Singular value decomposition)

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

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

1.特徵值分解實際上是對旋轉縮放兩種效應的歸併

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

 也就是說,奇異值分解能夠說是包含了特徵分解!來看Wikipedia的解釋:

在矩陣M的奇異值分解中

M = U\Sigma V^*, \,
  • V的列(columns)組成一套對M\,正交"輸入"或"分析"的基向量。這些向量是M^*\,M特徵向量
  • U的列(columns)組成一套對M\,正交"輸出"的基向量。這些向量是MM^*\,特徵向量
  • Σ對角線上的元素是奇異值,可視爲是在輸入與輸出間進行的標量的"膨脹控制"。這些是MM^*\,M^*\,M特徵值的非負平方根,並與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:

李航《統計學習方法》

Relationship between SVD and PCA. How to use SVD to perform PCA?

PCA的數學原理

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

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

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

矩陣的奇異值與特徵值有什麼類似之處與區別之處

從PCA和SVD的關係拾遺

相關文章
相關標籤/搜索