PCA 降維算法詳解 以及代碼示例


轉載地址:http://blog.csdn.net/watkinsong/article/details/38536463

1. 前言 

PCA : principal component analysis ( 主成分分析)

最近發現我的一篇關於PCA算法總結以及個人理解的博客的訪問量比較高, 剛好目前又重新學習了一下PCA (主成分分析) 降維算法, 所以打算把目前掌握的做個全面的整理總結, 能夠對有需要的人有幫助。 自己再看自己寫的那個關於PCA的博客, 發現還是比較混亂的, 希望這里能過做好整理。 本文的所有總結參考了Andrew Ng的PCA教程, 有興趣的可以自己學習。

上一篇關於PCA 的博客: http://blog.csdn.net/watkinsong/article/details/8234766, 在這篇博客中,有關於我最初在讀研的時候關於PCA的認識, 但是不是很系統, 然后里面卻給出了很多我總結的網絡上的資料, 以及根據我個人使用的經驗總結的感悟, 所以還是收到了很多的好評, o(∩∩)o...哈哈, 謝謝各位的支持。

@copyright by watkins.song ^_^

2. PCA的應用范圍

PCA的應用范圍有:

1. 數據壓縮

1.1 數據壓縮或者數據降維首先能夠減少內存或者硬盤的使用, 如果內存不足或者計算的時候出現內存溢出等問題, 就需要使用PCA獲取低維度的樣本特征。

1.2 其次, 數據降維能夠加快機器學習的速度。 

2. 數據可視化

在很多情況下, 可能我們需要查看樣本特征, 但是高維度的特征根本無法觀察, 這個時候我們可以將樣本的特征降維到2D或者3D, 也就是將樣本的特征維數降到2個特征或者3個特征, 這樣我們就可以采用可視化觀察數據

3. PCA原理簡介

3.1 基礎入門

這里我只給出在需要使用PCA的時候需要了解的最基本的PCA的原理, 了解這些原理后對於正常的使用沒有問題, 如果想要深入了解PCA, 需要學習一些矩陣分析的知識, 更加詳細的PCA算法請見wikipedia。

首先, 我們定義樣本和特征, 假定有 m 個樣本, 每個樣本有 n 個特征, 可以如下表示:

由簡到難, 先看一下從2D 降維到1D的比較直觀的表示:

在上圖中, 假設只有兩個特征x1, x2, 然后需要降維到1D, 這個時候我們可以觀察途中X所表示的樣本點基本上分布在一條直線上, 那么就可以將所有的用(x1, x2)平面表示的坐標映射到圖像畫出的直線z上, 上圖中的黑色鉛筆線表示樣本點映射的過程。 

映射到直線Z后, 如果只用直線Z表示樣本的空間分布, 就可以用1個坐標表示每個樣本了, 這樣就將2D的特征降維到1D的特征。 同樣的道理, 如果將3D的特征降維到2D, 就是將具有3D特征的樣本從一個三維空間中映射到二維空間。

 

在上圖中, 將所有的二維特征的樣本點映射到了一維直線上,  這樣, 從上圖中可以看出在映射的過程中存在映射誤差

在上圖中, 用圓圈表示了樣本映射后的坐標位置。這些位置可以叫做近似位置, 以后還要用到這些位置計算映射誤差。

 因為在降維映射的過程中, 存在映射誤差, 所有在對高維特征降維之前, 需要做特征歸一化(feature normalization), 這個歸一化操作包括: (1) feature scaling (讓所有的特征擁有相似的尺度, 要不然一個特征特別小, 一個特征特別大會影響降維的效果) (2) zero mean normalization (零均值歸一化)。 

在上圖中, 也可以把降維的過程看作找到一個或者多個向量u1, u2, ...., un, 使得這些向量構成一個新的向量空間(需要學習矩陣分析哦), 然后把需要降維的樣本映射到這個新的樣本空間上。

對於2D -> 1D 的降維過程, 可以理解為找到一個向量u1,  u1表示了一個方向, 然后將所有的樣本映射到這個方向上, 其實, 一個向量也可以表示一個樣本空間。

對於3D -> 2D 的降維過程, 可以理解為找到兩個向量u1, u2, (u1, u2) 這兩個向量定義了一個新的特征空間, 然后將原樣本空間的樣本映射到新的樣本空間。

對於n-D -> k-D 的降維過程, 可以理解為找到 k 個向量 u1, u2, ..., uk, 這k個向量定義了新的向量空間, 然后進行樣本映射。

3.2  Cost Function

既然樣本映射存在誤差, 就需要計算每次映射的誤差大小。 采用以下公式計算誤差大小:

 

X-approx表示的是樣本映射以后的新的坐標, 這個坐標如果位置如果用當前的樣本空間表示, 維度和 樣本X是一致的。

要特別注意, PCA降維和linear regression是不一樣的, 雖然看上去很一致, 但是linear regression的cost function的計算是樣本上線垂直的到擬合線的距離, 而PCA的cost function 是樣本點到擬合線的垂直距離。 差別如下圖所示:

 

3.3 PCA 計算過程

(A) Feature Normalization

首先要對訓練樣本的特征進行歸一化, 特別強調的是, 歸一化操作只能在訓練樣本中進行, 不能才CV集合或者測試集合中進行, 也就是說歸一化操作計算的各個參數只能由訓練樣本得到, 然后測試樣本根據這里得到的參數進行歸一化, 而不能直接和訓練樣本放在一起進行歸一化。 
另外, 在訓練PCA降維矩陣的過程中,也不能使用CV樣本或者測試樣本, 這樣做是不對的。 有很多人在使用PCA訓練降維矩陣的時候, 直接使用所有的樣本進行訓練, 這樣實際上相當於作弊的, 這樣的話降維矩陣是在包含訓練樣本和測試樣本以及CV樣本的情況下訓練得到的, 在進行測試的時候, 測試樣本會存在很大的優越性, 因為它已經知道了要降維到的空間情況。
 
特征歸一化直接給出代碼參考:
function [X_norm, mu, sigma] = featureNormalize(X)  
%FEATURENORMALIZE Normalizes the features in X   
%   FEATURENORMALIZE(X) returns a normalized version of X where  
%   the mean value of each feature is 0 and the standard deviation  
%   is 1. This is often a good preprocessing step to do when  
%   working with learning algorithms.  
  
mu = mean(X);  
X_norm = bsxfun(@minus, X, mu);  
  
sigma = std(X_norm);  
X_norm = bsxfun(@rdivide, X_norm, sigma);  
end  
注意: 這里的X是一個m * n 的矩陣, 有 m 個樣本, 每個樣本包含 n 個特征, 每一行表示一個樣本。 X_norm是最終得到的特征, 首先計算了所有訓練樣本每個特征的均值, 然后減去均值, 然后除以標准差。 
 

(B) 計算降維矩陣

 
B1. 首先計算樣本特征的 協方差矩陣
 
如下圖所示, 如果是每個樣本單獨計算, 則采用圖中橫線上的公式, 如果是采用矩陣化的計算, 則采用橫線下的公式。
 
 
B2. 計算協方差矩陣的特征值和特征向量
 
采用 奇異值分解的算法計算協方差矩陣的 特征值和特征向量,  奇異值分解是個比較復雜的概念, 如果有興趣可以查看wikipedia, 也可以直接使用matlab或者octave已經提供的奇異值分解的接口。
 
 
在上圖中, U 則是計算得到的協方差矩陣的所有特征向量, 每一列都是一個特征向量, 並且特征向量是根據特征大小由大到小進行排序的, U 的維度為 n * n 。 U 也被稱為降維矩陣。 利用U 可以將樣本進行降維。 默認的U 是包含協方差矩陣的所有特征向量, 如果想要將樣本降維到 k 維, 那么就可以選取 U 的前 k 列, Uk 則可以用來對樣本降維到  k 維。 這樣 Uk 的維度為 n * k

(C) 降維計算

獲得降維矩陣后, 即可通過降維矩陣將樣本映射到低維空間上。 降維公式如下圖所示:
 
如果是對於矩陣X 進行降維, X 是 m * n的, 那么降維后就變為 m * k 的維度, 每一行表示一個樣本的特征。

3.4  貢獻率 (降維的k的值的選擇)

在  http://blog.csdn.net/watkinsong/article/details/8234766 這篇文章中, 很多人問了關於貢獻率的問題, 這就是相當於選擇k的值的大小。 也就是選擇降維矩陣 U 中的特征向量的個數。 
k 越大, 也就是使用的U 中的特征向量越多, 那么導致的降維誤差越小, 也就是更多的保留的原來的特征的特性。 反之亦然。
 
從信息論的角度來看, 如果選擇的 k 越大, 也就是系統的熵越大, 那么就可以認為保留的原來樣本特征的不確定性也就越大, 就更加接近真實的樣本數據。 如果 k 比較小, 那么系統的熵較小, 保留的原來的樣本特征的不確定性就越少, 導致降維后的數據不夠真實。 (完全是我個人的觀點)
 
關於 k 的選擇, 可以參考如下公式: 
 
上面這個公式 要求 <= 0.01, 也就是說保留了系統的99%的不確定性。 
需要計算的就是, 找到一個最小的 k 使得上面的公式成立, 但是如果計算上面公式, 計算量太大, 並且對於每一個  k  取值都需要重新計算降維矩陣。 
可以采用下面的公式計算 k 的取值, 因為在 對協方差矩陣進行奇異值分解的時候返回了 S , S 為協方差矩陣的特征值, 並且 S 是對角矩陣, 維度為  n * n, 計算 k 的取值如下:
 

3.5  重構 (reconstruction, 根據降維后數據重構原數據), 數據還原

獲得降維后的數據, 可以根據降維后的數據還原原始數據。
還原原始數據的過程也就是獲得樣本點映射以后在原空間中的估計位置的過程, 即計算 X-approx的過程。
 
 
使用降維用的降維矩陣 Uk, 然后將 降維后的樣本 z 還原回原始特征, 就可以用上圖所示的公式。
 
 

4. PCA的應用示例

貌似本頁已經寫的太多了, 所以這里示例另外給出。 
 
由於篇幅問題, 這里只給出代碼, 關於代碼的解釋和插圖, 請訪問上面鏈接
 
%% Initialization  
clear ; close all; clc  
  
fprintf('this code will load 12 images and do PCA for each face.\n');  
fprintf('10 images are used to train PCA and the other 2 images are used to test PCA.\n');  
  
m = 4000; % number of samples  
trainset = zeros(m, 32 * 32); % image size is : 32 * 32  
  
for i = 1 : m  
    img = imread(strcat('./img/', int2str(i), '.bmp'));  
    img = double(img);  
    trainset(i, :) = img(:);  
end  
  
  
%% before training PCA, do feature normalization  
mu = mean(trainset);  
trainset_norm = bsxfun(@minus, trainset, mu);  
  
sigma = std(trainset_norm);  
trainset_norm = bsxfun(@rdivide, trainset_norm, sigma);  
  
%% we could save the mean face mu to take a look the mean face  
imwrite(uint8(reshape(mu, 32, 32)), 'meanface.bmp');  
fprintf('mean face saved. paused\n');  
pause;  
  
%% compute reduce matrix  
X = trainset_norm; % just for convience  
[m, n] = size(X);  
  
U = zeros(n);  
S = zeros(n);  
  
Cov = 1 / m * X' * X;  
[U, S, V] = svd(Cov);  
fprintf('compute cov done.\n');  
  
%% save eigen face  
for i = 1:10  
    ef = U(:, i)';  
    img = ef;  
    minVal = min(img);  
    img = img - minVal;  
    max_val = max(abs(img));  
    img = img / max_val;  
    img = reshape(img, 32, 32);  
    imwrite(img, strcat('eigenface', int2str(i), '.bmp'));  
end  
  
fprintf('eigen face saved, paused.\n');  
pause;  
  
%% dimension reduction  
k = 100; % reduce to 100 dimension  
test = zeros(10, 32 * 32);  
for i = 4001:4010  
    img = imread(strcat('./img/', int2str(i), '.bmp'));  
    img = double(img);  
    test(i - 4000, :) = img(:);  
end  
  
% test set need to do normalization  
test = bsxfun(@minus, test, mu);  
  
% reduction  
Uk = U(:, 1:k);  
Z = test * Uk;  
fprintf('reduce done.\n');  
  
%% reconstruction  
%% for the test set images, we only minus the mean face,  
% so in the reconstruct process, we need add the mean face back  
Xp = Z * Uk';  
% show reconstructed face  
for i = 1:5  
    face = Xp(i, :) + mu;  
    face = reshape((face), 32, 32);  
    imwrite(uint8(face), strcat('./reconstruct/', int2str(4000 + i), '.bmp'));  
end  
  
%% for the train set reconstruction, we minus the mean face and divide by standard deviation during the train  
% so in the reconstruction process, we need to multiby standard deviation first,   
% and then add the mean face back  
trainset_re = trainset_norm * Uk; % reduction  
trainset_re = trainset_re * Uk'; % reconstruction  
for i = 1:5  
    train = trainset_re(i, :);  
    train = train .* sigma;  
    train = train + mu;  
    train = reshape(train, 32, 32);  
    imwrite(uint8(train), strcat('./reconstruct/', int2str(i), 'train.bmp'));  
end  
  
fprintf('job done.\n');  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM