【轉】淺談對主成分分析(PCA)算法的理解


以前對PCA算法有過一段時間的研究,但沒整理成文章,最近項目又打算用到PCA算法,故趁熱打鐵整理下PCA算法的知識。本文觀點旨在拋磚引玉,不是權威,更不能盡信,只是本人的一點體會。

主成分分析(PCA)是多元統計分析中用來分析數據的一種方法,它是用一種較少數量的特征對樣本進行描述以達到降低特征空間維數的方法,它的本質實際上是K-L變換。PCA方法最著名的應用應該是在人臉識別中特征提取及數據維,我們知道輸入200*200大小的人臉圖像,單單提取它的灰度值作為原始特征,則這個原始特征將達到40000維,這給后面分類器的處理將帶來極大的難度。著名的人臉識別Eigenface算法就是采用PCA算法,用一個低維子空間描述人臉圖像,同時用保存了識別所需要的信息。下面先介紹下PCA算法的本質K-L變換。

1、K-L變換(卡洛南-洛伊(Karhunen-Loeve)變換):最優正交變換  

  • 一種常用的特征提取方法;
  • 最小均方誤差意義下的最優正交變換;
  • 在消除模式特征之間的相關性、突出差異性方面有最優的效果。
離散K-L變換:對向量 x(可以想象成  M維=width*height 的人臉圖像原始特征)用確定的完備正交歸一向量系 uj展開:
 
 
 
這個公式由來我想應該是任一 n維歐式空間 V均存在正交基,利用施密特正交化過程即可構建這個正交基。
現在我們希望用 d個有限項來估計向量 x,公式如下:
 
計算該估計的均方誤差如下:
 
要使用均方誤差最小,我們采用Langrange乘子法進行求解:
 
                                                           
 
因此,當滿足上式時,
取得最小值。
 
 
即相關矩陣R的d個特征向量(對應d個特征值從大到小排列)為基向量來展開向量x時,其均方誤差最小,為:
 
因此,K-L變換定義:當取矩陣R的d個最大特征值對應的特征向量來展開x時,其截斷均方誤差最小。 這d個特征向量組成的正交坐標系稱作x所在的D維空間的d維K-L變換坐標系, x在K-L坐標系上的展開系數向量y稱作x的K-L變換。
 
總結下, K-L變換的方法:對相關矩陣 R的特征值由大到小進行排隊,
均方誤差最小x近似於:
                                                               
矩陣形式:
 
上式兩邊乘以 U的轉置,得
                                             
 

向量y就是變換(降維)后的系數向量,在人臉識別Eigenface算法中就是用系數向量y代替原始特征向量x進行識別。


 
下面,我們來看看相關矩陣 R到底是什么樣子。
 
因此,我們可以看出相關矩陣 R是一個實對稱矩陣(或者嚴謹的講叫正規矩陣),正規矩陣有什么特點呢??學過《矩陣分析》的朋友應該知道:
若矩陣R是一個實對稱矩陣,則必定存在正交矩陣U,使得R相似於對角形矩陣,即:
 
 因此,我們可以得出這樣一個結論:
                                                     
 
 降維后的系數向量 y 的相關矩陣是對角矩陣,即 通過K-L變換消除原有向量x的各分量間的相關性,從而有可能去掉那些帶有較少信息的分量以達到降低特征維數的目的。
 
2、主成分分析(PCA)
 
主成分分析(PCA)的原理就是將一個高維向量 x,通過一個特殊的特征向量矩陣 U,投影到一個低維的向量空間中,表征為一個低維向量 y,並且僅僅損失了一些次要信息。也就是說, 通過低維表征的向量和特征向量矩陣,可以基本重構出所對應的原始高維向量。
在人臉識別中,特征向量矩陣 U稱為特征臉(eigenface)空間,因此其中的特征向量 ui進行量化后可以看出人臉輪廓,在下面的實驗中可以看出。
以人臉識別為例,說明下PCA的應用。
設有 N個人臉訓練樣本,每個樣本由其像素灰度值組成一個向量 xi,則樣本圖像的像素點數即為 xi的維數, M=width*height ,由向量構成的訓練樣本集為
該樣本集的平均向量為:
平均向量又叫平均臉。
 
樣本集的 協方差矩陣為:
 
求出協方差矩陣的特征向量 ui和對應的特征值 ,這些特征向量組成的矩陣 U就是人臉空間的正交基底,用它們的線性組合可以重構出樣本中任意的人臉圖像,(如果有朋友不太理解這句話的意思,請看下面的總結2。)並且圖像信息集中在特征值大的特征向量中,即使丟棄特征值小的向量也不會影響圖像質量。
將協方差矩陣的特征值按大到小排序: 。由大於 對應的特征向量構成主成分,主成分構成的變換矩陣為:
                               
這樣每一幅人臉圖像都可以投影到 構成的特征臉子空間中, U的維數為 M×d。有了這樣一個降維的子空間,任何一幅人臉圖像都可以向其作投影 ,即並獲得一組坐標系數,即低維向量 y,維數 d×1,為稱為 KL分解系數。這組系數表明了圖像在子空間的位置,從而可以作為人臉識別的依據。

有朋友可能不太理解,第一部分講K-L變換的時候,求的是相關矩陣 的特征向量和特征值,這里怎么求的是協方差矩陣 ?
其實協方差矩陣也是:
,可以看出其實 用代替 x就成了相關矩陣 R,相當於原始樣本向量都減去個平均向量,實質上還是一樣的,協方差矩陣也是實對稱矩陣。

總結下:
1、在人臉識別過程中,對輸入的一個測試樣本 x,求出它與平均臉的偏差 ,則 在特征臉空間 U投影,可以表示為系數向量 y
                                     
U的維數為 M×d的維數為 M×1y的維數 d×1。若 M為200*200=40000維,取200個主成分,即200個特征向量,則最后投影的系數向量 y維數降維200維。
2、根據1中的式子,可以得出:
                                
這里的 x就是根據投影系數向量 y 重構出的人臉圖像,丟失了部分圖像信息,但不會影響圖像質量。
 
 3、PCA算法實驗  
 
在計算機視覺庫OpenCV中較新的版本中,封裝了PCA算法的類。下面是對PCA算法做的一些實驗,有助於加深對PCA算法的理解。代碼來自於 tornadomeet,我並沒有作太多修改,加多一些說明。
運行環境為:WindowsXP+QT+OpenCV2.3.1。
 
部分函數說明如下:

Mat Mat::reshape(int cn, int rows=0) const

  該函數是改變Mat的尺寸,即保持尺寸大小=行數*列數*通道數 不變。其中第一個參數為變換后Mat的通道數,如果為0,代表變換前后通道數不變。第二個參數為變換后Mat的行數,如果為0也是代表變換前后通道數不變。但是該函數本身不復制數據

 

  void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 ) const

  該函數其實是對原Mat的每一個值做一個線性變換。參數1為目的矩陣,參數2為目d矩陣的類型,參數34變換的系數,看完下面的公式就明白了:

  

 

  PCA::PCA(InputArray data, InputArray mean, int flags, int maxComponents=0)

  該構造函數的第一個參數為要進行PCA變換的輸入Mat;參數2為該Mat的均值向量;參數3為輸入矩陣數據的存儲方式,如果其值為CV_PCA_DATA_AS_ROW則說明輸入Mat的每一行代表一個樣本,同理當其值為CV_PCA_DATA_AS_COL時,代表輸入矩陣的每一列為一個樣本;最后一個參數為該PCA計算時保留的最大主成分的個數。如果是缺省值,則表示所有的成分都保留。

 

  Mat PCA::project(InputArray vec) const

  該函數的作用是將輸入數據vec(該數據是用來提取PCA特征的原始數據)投影到PCA主成分空間中去,返回每一個樣本主成分特征組成的矩陣。因為經過PCA處理后,原始數據的維數降低了,因此原始數據集中的每一個樣本的維數都變了,由改變后的樣本集就組成了本函數的返回值。下面由一個圖說明:

                                                              

 

  Mat PCA::backProject(InputArray vec) const

  一般調用backProject()函數前需調用project()函數,因為backProject()函數的參數vec就是經過PCA投影降維過后的矩陣dst。 因此backProject()函數的作用就是用vec來重構原始數據集(關於該函數的本質就是上面總結2的公式)。由一個圖說明如下:

                                                                 

  另外PCA類中還有幾個成員變量,mean,eigenvectors, eigenvalues等分別對應着原始數據的均值,協方差矩陣的特征值和特征向量。

 

實驗結果:

實驗是用4個人人臉圖像,其中每個人分別有5張,共計20張人臉圖片。用這些圖片組成原始數據集來提取他們的PCA主特征臉。該20張圖片如下所示:

                                                   

軟件運行結果:

實驗中保留4個特征向量作為人臉圖像的正交基底,運行結果如下:

                                                              

   其中第一行的3張人臉分別為20張原圖中的3張,這里取的是3個不同人的。

  第二行中顯示的3張人臉重構的人臉圖像,可以看出由於只取了4個特征向量作為正交基底,因此重構后的人臉圖像一些細節會丟失。如果增加保留的特征向量個數,則能較好的重構出人臉圖像。

  3行的人臉圖為取的原始數據協方差矩陣特征向量的最前面3個,因此這3個人臉為最具代表人臉特征的3PCA人臉特征。

實驗主要部分代碼:

pcaface.h

 

復制代碼
 1 #ifndef PCAFACE_H
 2 #define PCAFACE_H
 3 #include <opencv2/core/core.hpp>
 4 #include <opencv2/highgui/highgui.hpp>
 5 #include <opencv2/imgproc/imgproc.hpp>
 6 
 7 using namespace cv;
 8 
 9 #include <QDialog>
10 
11 namespace Ui {
12 class PCAFace;
13 }
14 
15 class PCAFace : public QDialog
16 {
17     Q_OBJECT
18     
19 public:
20     explicit PCAFace(QWidget *parent = 0);
21     ~PCAFace();
22 
23     Mat normalize(const Mat& src);
24 
25     
26 protected:
27     void changeEvent(QEvent *e);
28     
29 private slots:
30     void on_startButton_clicked();
31 
32     void on_closeButton_clicked();
33 
34 private:
35     Ui::PCAFace *ui;
36     Mat src_face1, src_face2, src_face3;
37     Mat project_face1, project_face2, project_face3;
38     Mat dst;
39     Mat pca_face1, pca_face2, pca_face3;
40     vector<Mat> src;
41     int total;
42 };
43 
44 #endif // PCAFACE_H
復制代碼

 

pcaface.cpp

 

復制代碼
  1 #include "pcaface.h"
  2 #include "ui_pcaface.h"
  3 #include <QString>
  4 #include <iostream>
  5 #include <stdio.h>
  6 
  7 using namespace std;
  8 
  9 PCAFace::PCAFace(QWidget *parent) :
 10     QDialog(parent),
 11     ui(new Ui::PCAFace)
 12 {
 13     ui->setupUi(this);
 14     src_face1 = imread("./images/1.pgm", 0);
 15 
 16     //下面的代碼為設置圖片顯示區域自適應圖片的大小
 17     ui->face1Browser->setFixedHeight(src_face1.rows+1);
 18     ui->face1Browser->setFixedWidth(src_face1.cols+1);
 19     ui->face2Browser->setFixedHeight(src_face1.rows+1);
 20     ui->face2Browser->setFixedWidth(src_face1.cols+1);
 21     ui->face3Browser->setFixedHeight(src_face1.rows+1);
 22     ui->face3Browser->setFixedWidth(src_face1.cols+1);
 23 
 24     ui->face4Browser->setFixedHeight(src_face1.rows+1);
 25     ui->face4Browser->setFixedWidth(src_face1.cols+1);
 26     ui->face5Browser->setFixedHeight(src_face1.rows+1);
 27     ui->face5Browser->setFixedWidth(src_face1.cols+1);
 28     ui->face6Browser->setFixedHeight(src_face1.rows+1);
 29     ui->face6Browser->setFixedWidth(src_face1.cols+1);
 30 
 31     ui->face7Browser->setFixedHeight(src_face1.rows+1);
 32     ui->face7Browser->setFixedWidth(src_face1.cols+1);
 33     ui->face8Browser->setFixedHeight(src_face1.rows+1);
 34     ui->face8Browser->setFixedWidth(src_face1.cols+1);
 35     ui->face9Browser->setFixedHeight(src_face1.rows+1);
 36     ui->face9Browser->setFixedWidth(src_face1.cols+1);
 37 
 38     for(int i = 1; i <= 15; i++)
 39     {
 40         stringstream ss;
 41         string num;
 42         ss<<i;        //將整數i讀入字符串流
 43         ss>>num;      //將字符串流中的數據傳入num,這2句代碼即把數字轉換成字符
 44         string image_name = ("./images/" + num + ".pgm");//需要讀取的圖片全名
 45         src.push_back(imread(image_name, 0));
 46     }
 47     total= src[0].rows*src[0].cols;
 48 }
 49 
 50 PCAFace::~PCAFace()
 51 {
 52     delete ui;
 53 }
 54 
 55 void PCAFace::changeEvent(QEvent *e)
 56 {
 57     QDialog::changeEvent(e);
 58     switch (e->type()) {
 59     case QEvent::LanguageChange:
 60         ui->retranslateUi(this);
 61         break;
 62     default:
 63         break;
 64     }
 65 }
 66 
 67 //將Mat內的內容歸一化到0~255,歸一化后的類型為但通道整型
 68 Mat PCAFace::normalize(const Mat& src) {
 69     Mat srcnorm;
 70     cv::normalize(src, srcnorm, 0, 255, NORM_MINMAX, CV_8UC1);
 71     return srcnorm;
 72 }
 73 
 74 
 75 void PCAFace::on_startButton_clicked()
 76 {
 77     //先顯示3張原圖
 78     ui->face1Browser->append("<img src=./images/1.pgm>");
 79     ui->face2Browser->append("<img src=./images/7.pgm>");
 80     ui->face3Browser->append("<img src=./images/14.pgm>");
 81 
 82     //mat數組用來存放讀取進來的所有圖片的數據,其中mat的每一列對應1張圖片,該實現在下面的for函數中
 83     Mat mat(total, src.size(), CV_32FC1);
 84     for(int i = 0; i < src.size(); i++)
 85     {
 86         Mat col_tmp = mat.col(i);
 87         src[i].reshape(1, total).col(0).convertTo(col_tmp, CV_32FC1, 1/255.);
 88     }
 89     int number_principal_compent = 4;   //保留最大的主成分數
 90 
 91     //構造pca數據結構
 92     PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, number_principal_compent);
 93 
 94 
 95     //pca.eigenvectors中的每一行代表輸入數據協方差矩陣一個特征向量,且是按照該協方差矩陣的特征值進行排序的
 96     pca_face1 = normalize(pca.eigenvectors.row(0)).reshape(1, src[0].rows);  //第一個主成分臉
 97     imwrite("./result/pca_face1.jpg", pca_face1);//顯示主成分特征臉1
 98     ui->face7Browser->append("<img src=./result/pca_face1.jpg>");
 99 
100     pca_face2 = normalize(pca.eigenvectors.row(1)).reshape(1, src[0].rows);  //第二個主成分臉
101     imwrite("./result/pca_face2.jpg", pca_face2);//顯示主成分特征臉2
102     ui->face8Browser->append("<img src=./result/pca_face2.jpg>");
103 
104     pca_face3 = normalize(pca.eigenvectors.row(2)).reshape(1, src[0].rows);  //第三個主成分臉
105     imwrite("./result/pca_face3.jpg", pca_face3);//顯示主成分特征臉3
106     ui->face9Browser->append("<img src=./result/pca_face3.jpg>");
107 
108     //將原始數據通過PCA方向投影,即通過特征向量的前面幾個作用后的數據,因此這里的dst的尺寸變小了
109     dst = pca.project(mat);
110 
111     //通過方向投影重構原始人臉圖像
112     project_face1 = normalize(pca.backProject(dst).col(1)).reshape(1, src[0].rows);
113     imwrite("./result/project_face1.jpg", project_face1);
114     ui->face4Browser->append("<img src=./result/project_face1.jpg>");
115 
116     project_face2 = normalize(pca.backProject(dst).col(7)).reshape(1, src[0].rows);
117     imwrite("./result/project_face2.jpg", project_face2);
118     ui->face5Browser->append("<img src=./result/project_face2.jpg>");
119 
120     project_face3 = normalize(pca.backProject(dst).col(14)).reshape(1, src[0].rows);
121     imwrite("./result/project_face3.jpg", project_face3);
122     ui->face6Browser->append("<img src=./result/project_face3.jpg>");
123 }
124 
125 void PCAFace::on_closeButton_clicked()
126 {
127     close();
128 }
復制代碼

 

實驗的工程代碼可以在上面的那個鏈接下載,環境搭建可以參考我之前寫個一個文章http://www.cnblogs.com/liu-jun/archive/2012/09/26/Jacky_Liu.html

作者:Jacky_Liu,轉載或分享請注明出處。


免責聲明!

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



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