PCA降维原理及OpenCV实现

介绍

PCA(principal component analysis)就是主分量分析,是一种常用的数据分析方法。PCA通过线性变换将原始数据变换为一组各维度线性无关的表示,可用于提取数据的主要特征分量,常用于高维数据的降维。通过数据降维可以实现数据的压缩,同时方便数据分析和提高算法的处理速度。PCA的原理就是通过正交变换,最大化样本协方差阵的对角元素,最小化非对角元素。但是PCA应用本身是基于一定假设的:
1、线性。即特征的变换是线性变换,作用有限,目前也有非线性的特征变换kernel PCA。
2、处理的数据分布式服从指数族概率密度函数,即能通过均值和协方差来表征数据的分布,因为只有在这个情况下信噪比和协方差矩阵才能表示噪声和数据冗余。(好在实际应用中常见的数据是服从高斯分布或近似高斯分布)。

PCA原理

关于PCA的理论与公式推导网络上很多,尴尬的是本人功底有限,理论方面这里就不列出啦。下面主要从Opencv应用的角度大概来讲讲自己对PCA具体怎么实现数据集降维的理解。
1、把原始数据中每个样本用一个向量表示,然后把所有样本组合起来构成一个矩阵,避免样本单位的影响,样本集需要标准化。
2、求该矩阵的协方差矩阵。(关于协方差矩阵的理解大家可以看看这篇文章)
3、求步骤2中得到的协方差矩阵的特征值和特征向量。
(再将求出的特征向量按照特征值的大小进行组合形成一个映射矩阵,并根据指定的PCA保留的特征个数取出映射矩阵的前n行或者前n列作为最终的映射矩阵。映射矩阵是对原始数据的映射,从而来达到从高维度降维的目的)。

代码帮助理解

直接上代码吧,当作自己的备忘,有注释便于理解如何使用Opencv简单的实现PCA。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#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);
}

同时贴出结果图,可以看到6个列(维度)的特征值以及特征向量
PCA维

推荐

顺便给大家推荐一个图像去模糊算法,香港大学的贾佳亚大牛发明的,处于世界领先水平,而且个人主页上有丰富的源码。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!