機器學習-PCA降維原理與實現

一、爲什麼要進行數據降維

機器學習領域中所謂的降維就是指採用某種映射方法,將原高維空間中的數據映射到低維度的空間中。之所以要進行數據降維,是因爲在原始的高維數據中,存在很多冗餘以及噪聲信息,通過數據降維,我們可以減少冗餘信息,提高識別的精度,同時降低維度也可以提升機器學習的速度。

二、原理

PCA 全稱爲主成分分析方法(Principal Component Analysis),它的目標是通過某種線性投影,將高維的數據映射到低維的空間中表示,並期望在所投影的維度上數據的方差最大,以此使用較少的數據維度,同時保留住較多的原數據點的特性。

舉個🌰,下圖中的數據爲 2 維,現在想只通過 1 個維度來表示這堆數據,通過將數據投影到 z 軸上,原始的點{x1,x2}\{x_1,x_2\}在新的 z 軸上的數據爲z1z_1

image-20200209150316294

那麼怎麼找到這個投影軸呢?這裏的過程比較複雜,感興趣的同學可以看 [PCA的數學原理(轉)],主要的過程就是通過協方差矩陣來求解特徵向量從而獲取降維後的軸。

假設原始數據表示爲 XRm×nX \in \R^{m×n},數據維度爲 nn ,PCA 算法的流程如下:

  1. 均值標準化

獲取每個維度的均值,設 μj\mu_j 爲第 jj 個維度的均值,則
μj=1mi=1mxj(i)(1) \mu_j=\frac{1}{m}\sum_{i=1}^{m}x_{j}^{(i)}\tag{1}
再對原始的數據進行替換,
xj=xjμj(2) x_{j}=x_{j}-\mu_j \tag{2}

  1. 求解協方差矩陣

經過均值標準化之後的數據的協方差矩陣爲
Σ=XTX(3) \Sigma=X^TX\tag{3}

  1. 獲取特徵向量

一般來說, Σ\Sigma 會有 nn 個特徵值,對應 nn 個特徵向量,如果需要將原始數據從 nn 維降低到 kk 維,則只需要選取特徵值最大的 kk 個特徵值對應的特徵向量即可,我們將其設爲 UU

  1. 降低數據維度

低維數據可以表示爲
Z=XURm×k(4) Z=XU \in \R^{m×k}\tag{4}

這樣就將原始的 nn 維數據降低爲 kk 維。,這是如果想恢復原始數據怎麼辦?可以恢復部分維度,被壓縮的部分是找不回來的,通過壓縮後的數據還原到原始數據的公式爲
Xapprox=ZUT+μ(5) X_{\text{approx}}=ZU^T+\mu\tag{5}
舉個🌰,假設原始數據爲X=(1210002101)X=\left(\begin{array}{cc}{-1} & {-2} \\ {-1} & {0} \\ {0} & {0} \\ {2} & {1} \\ {0} & {1}\end{array}\right),維度爲 n=2n=2,下面我們根據上述過程計算 1 個維度下的值。

首先計算均值,我們發現每列數據的均值爲 0,那麼則可以直接進行協方差矩陣的計算
Σ=15(1102020011)(1210002101)=(65454565) \Sigma=\frac{1}{5}\left(\begin{array}{ccccc}{-1} & {-1} & {0} & {2} & {0} \\ {-2} & {0} & {0} & {1} & {1}\end{array}\right) \left(\begin{array}{cc}{-1} & {-2} \\ {-1} & {0} \\ {0} & {0} \\ {2} & {1} \\ {0} & {1}\end{array}\right)=\left(\begin{array}{cc}{\frac{6}{5}} & {\frac{4}{5}} \\ {\frac{4}{5}} & {\frac{6}{5}}\end{array}\right)
然後求其特徵值和特徵向量,具體求解方法不再詳述,可以參考相關資料。求解後特徵值爲
λ1=2,λ2=2/5 \lambda_{1}=2, \lambda_{2}=2 / 5
其對應的特徵向量分別是
c1=(11),c2=(11) c_{1}=\left(\begin{array}{l}{1} \\ {1}\end{array}\right), c_{2}=\left(\begin{array}{c}{-1} \\ {1}\end{array}\right)
其中對應的特徵向量分別是一個通解,c1c_{1}c2c_{2} 可取任意實數。那麼標準化後的特徵向量爲
(1/21/2),(1/21/2) \left(\begin{array}{c}{1 / \sqrt{2}} \\ {1 / \sqrt{2}}\end{array}\right),\left(\begin{array}{c}{-1 / \sqrt{2}} \\ {1 / \sqrt{2}}\end{array}\right)
由於需要降低到 1 維,所以我們取特徵值 λ1\lambda_1 對應的特徵向量作爲矩陣 U=(1/21/2)U=\left(\begin{array}{c}{1 / \sqrt{2}} \\ {1 / \sqrt{2}}\end{array}\right),降維後的數據爲
Z=(1210002101)(1/21/2)=(3/21/203/21/2) Z =\left(\begin{array}{cc}{-1} & {-2} \\ {-1} & {0} \\ {0} & {0} \\ {2} & {1} \\ {0} & {1}\end{array}\right)\left(\begin{array}{c}{1 / \sqrt{2}} \\ {1 / \sqrt{2}}\end{array}\right)=\left(\begin{array}{c}-3/\sqrt{2} \\ -1/\sqrt{2} \\ 0 \\ 3/\sqrt{2} \\ 1/\sqrt{2}\end{array}\right)
注意⚠️:通過前面的方法我們知道還需要手動設置一個 kk 值,那麼怎麼選擇最優的 kk 值呢,一般來說,選取的 kk 值通常要保留 99% 的方差,kk 值的選取可以參考下面的過程:

  1. k=1n1k=1 \to n-1

  2. 通過式 (3)(4)(5)(3)、(4)、(5)計算 U,z(1),z(2),,z(m),xapprox(1),xapprox(2),,xapprox(m)U,z^{(1)},z^{(2)},\ldots,z^{(m)},x_{\text{approx}}^{(1)},x_{\text{approx}}^{(2)},\ldots,x_{\text{approx}}^{(m)}

  3. 校對是否滿足以下條件
    1mi=1mx(i)xapprox(i)21mi=1mx(i)20.01 \frac{\frac{1}{m} \sum_{i=1}^{m}\left\|x^{(i)}-x_{\text {approx}}^{(i)}\right\|^{2}}{\frac{1}{m} \sum_{i=1}^{m}\left\|x^{(i)}\right\|^{2}}\leq0.01
    如果滿足上述條件,則可以選擇該 kk

三、實現

3.1 Python 手動實現

'''
@Author: huzhu
@Date: 2019-11-20 09:18:15
@Description: 
'''
import numpy as np
import matplotlib.pyplot as plt 

def load_data(file_name, delim='\t'):
    fr = open(file_name)
    str_arr = [line.strip().split(delim) for line in fr.readlines()]
    dat_arr = [list(map(float,line)) for line in str_arr]
    return np.mat(dat_arr)

def pca(data_mat, topNfeat = 999999):
    '''
    @description: PCA
    @return: low_data_mat, recon_mat
    '''
    mean_val = np.mean(data_mat, axis = 0)
    mean_removed = mean_val - data_mat
    # get the covrariance matrix
    cov_mat = np.cov(mean_removed, rowvar=0)
    # get the eigenvalue and eigenvector
    eigen_vals, eigen_vecs = np.linalg.eig(cov_mat)
    # sort, sort goes smallest to largest
    eigen_val_ind = np.argsort(eigen_vals)
    # cut off unwanted dimensions
    eigen_val_ind = eigen_val_ind[:-(topNfeat+1):-1]
    print(eigen_val_ind)
    # reorganize eig vects largest to smallest
    red_eigen_vecs = eigen_vecs[:,eigen_val_ind] 
    print(red_eigen_vecs)
    # low dimension data
    low_data_mat = mean_removed * red_eigen_vecs
    # transfor low data to original dimension
    recon_mat = (low_data_mat * red_eigen_vecs.T) + mean_val
    return low_data_mat, recon_mat

if __name__ == '__main__':
    data_mat = load_data("testSet.txt")
    low_data_mat, recon_mat = pca(data_mat, 1)
    plt.figure()
    plt.scatter(data_mat[:,0].flatten().A[0], data_mat[:,1].flatten().A[0], marker='^', s = 90)
    plt.scatter(recon_mat[:,0].flatten().A[0], recon_mat[:,1].flatten().A[0], marker='o', s = 50, c = "red")
    plt.show()

降維後的結果如下圖所示。

PCA

3.2 庫函數實現

import numpy as np
from sklearn.decomposition import PCA
X = np.array([[-1, -2], [-1, 0], [0, 0], [2, 1], [0, 1]])
pca = PCA(n_components=1)
newX = pca.fit_transform(X)
print(newX)
print(pca.explained_variance_ratio_)

結果爲

[[ 2.12132034]
 [ 0.70710678]
 [-0.        ]
 [-2.12132034]
 [-0.70710678]]
[0.83333333]

可以看到和我們上述舉例的數據相同(符號相反是求解特徵向量的時候符號不同所導致的)。

完整代碼和數據可以參考 [我的 github]

四、參考

[1] https://zhuanlan.zhihu.com/p/21580949

[2] https://www.jianshu.com/p/8642d5ea5389

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章