一、介紹
PCA(principal component analysis)就是主分量分析,是一種常用的數據分析方法。PCA通過線性變換將原始數據變換爲一組各維度線性無關的表示,可用於提取數據的主要特徵分量,常用於高維數據的降維。通過數據降維可以實現數據的壓縮,同時方便數據分析和提高算法的處理速度。PCA的原理就是通過正交變換,最大化樣本協方差陣的對角元素,最小化非對角元素。但是PCA應用本身是基於一定假設的:
1.線性。即特徵的變換是線性變換,作用有限,目前也有非線性的特徵變換kernel PCA。
2.處理的數據分佈式服從指數族概率密度函數,即能通過均值和協方差來表徵數據的分佈,因爲只有在這個情況下信噪比和協方差矩陣才能表示噪聲和數據冗餘。(好在實際應用中常見的數據是服從高斯分佈或近似高斯分佈)。
二、PCA原理介紹
1、把原始數據中每個樣本用一個向量表示,然後把所有樣本組合起來構成一個矩陣,避免樣本的單位的影響,樣本集需要標準化。
2、求該矩陣的協方差矩陣。(關於協方差矩陣的理解大家可以看看這篇文章)
3、求步驟2中得到的協方差矩陣的特徵值和特徵向量。
三、代碼幫助理解
#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\ml\ml.hpp>
using namespace cv;
using namespace std;
void DoPca(const Mat &_data, int dim, Mat &eigenvalues, Mat &eigenvectors);
void printMat( Mat _data )
{
Mat data = cv::Mat_<double>(_data);
for ( int i=0; i<data.rows; i++ )
{
for ( int j=0; j< data.cols; j++ )
{
cout << data.at<double>(i,j) << " ";
}
cout << endl;
}
}
int main(int argc, char* argv[])
{
float A[ 60 ]={
1.5 , 2.3 , 1.5 , 2.3 , 1.5 , 2.3 ,
3.0 , 1.7 , 3.0 , 1.7 , 3.0 , 1.7 ,
1.2 , 2.9 , 1.2 , 2.9 , 1.2 , 2.9 ,
2.1 , 2.2 , 2.1 , 2.2 , 2.1 , 2.2 ,
3.1 , 3.1 , 3.1 , 3.1 , 3.1 , 3.1 ,
1.3 , 2.7 , 1.3 , 2.7 , 1.3 , 2.7 ,
2.0 , 1.7 , 2.0 , 1.7 , 2.0 , 1.7 ,
1.0 , 2.0 , 1.0 , 2.0 , 1.0 , 2.0 ,
0.5 , 0.6 , 0.5 , 0.6 , 0.5 , 0.6 ,
1.0 , 0.9 , 1.0 , 0.9 , 1.0 , 0.9 };
Mat DataMat = Mat::zeros( 10, 6, CV_32F );
//將數組A裏的數據放入DataMat矩陣中
for ( int i=0; i<10; i++ )
{
for ( int j=0; j<6; j++ )
{
DataMat.at<float>(i, j) = A[i * 6 + j];
}
}
// OPENCV PCA
PCA pca(DataMat, noArray(), CV_PCA_DATA_AS_ROW);
Mat eigenvalues;//特徵值
Mat eigenvectors;//特徵向量
DoPca(DataMat, 3, eigenvalues, eigenvectors);
cout << "eigenvalues:" << endl;
printMat( eigenvalues );
cout << "\n" << endl;
cout << "eigenvectors:" << endl;
printMat( eigenvectors );
system("pause");
return 0;
}
void DoPca(const Mat &_data, int dim, Mat &eigenvalues, Mat &eigenvectors)
{
assert( dim>0 );
Mat data = cv::Mat_<double>(_data);
int R = data.rows;
int C = data.cols;
if ( dim>C )
dim = C;
//計算均值
Mat m = Mat::zeros( 1, C, data.type() );
for ( int j=0; j<C; j++ )
{
for ( int i=0; i<R; i++ )
{
m.at<double>(0,j) += data.at<double>(i,j);
}
}
m = m/R;
//求取6列數據對應的均值存放在m矩陣中,均值: [1.67、2.01、1.67、2.01、1.67、2.01]
//計算協方差矩陣
Mat S = Mat::zeros( R, C, data.type() );
for ( int i=0; i<R; i++ )
{
for ( int j=0; j<C; j++ )
{
S.at<double>(i,j) = data.at<double>(i,j) - m.at<double>(0,j); // 數據矩陣的值減去對應列的均值
}
}
Mat Average = S.t() * S /(R);
//計算協方差矩陣的方式----(S矩陣的轉置 * S矩陣)/行數
//使用opencv提供的eigen函數求特徵值以及特徵向量
eigen(Average, eigenvalues, eigenvectors);
}