PCA 降维原理及 OpenCV 实现

发布于 2024-08-20 06:50:29 字数 4826 浏览 7 评论 0

PCA(principal component analysis)就是主分量分析,是一种常用的数据分析方法。PCA 通过线性变换将原始数据变换为一组各维度线性无关的表示,可用于提取数据的主要特征分量,常用于高维数据的降维。通过数据降维可以实现数据的压缩,同时方便数据分析和提高算法的处理速度。

PCA 的原理就是通过正交变换,最大化样本协方差阵的对角元素,最小化非对角元素。但是 PCA 应用本身是基于一定假设的:

  1. 线性。即特征的变换是线性变换,作用有限,目前也有非线性的特征变换 kernel PCA。
  2. 处理的数据分布式服从指数族概率密度函数,即能通过均值和协方差来表征数据的分布,因为只有在这个情况下信噪比和协方差矩阵才能表示噪声和数据冗余。(好在实际应用中常见的数据是服从高斯分布或近似高斯分布)。

PCA 原理

关于 PCA 的理论与公式推导网络上很多,尴尬的是本人功底有限,理论方面这里就不列出啦。下面主要从 Opencv 应用的角度大概来讲讲自己对 PCA 具体怎么实现数据集降维的理解。

1、把原始数据中每个样本用一个向量表示,然后把所有样本组合起来构成一个矩阵,避免样本单位的影响,样本集需要标准化。
2、求该矩阵的协方差矩阵。(关于协方差矩阵的理解大家可以看看这篇文章)
3、求步骤 2 中得到的协方差矩阵的特征值和特征向量。

再将求出的特征向量按照特征值的大小进行组合形成一个映射矩阵,并根据指定的 PCA 保留的特征个数取出映射矩阵的前 n 行或者前 n 列作为最终的映射矩阵。映射矩阵是对原始数据的映射,从而来达到从高维度降维的目的。

代码帮助理解

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

#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 维

推荐

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

べ繥欢鉨o。

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

玍銹的英雄夢

文章 0 评论 0

我不会写诗

文章 0 评论 0

十六岁半

文章 0 评论 0

浸婚纱

文章 0 评论 0

qq_kJ6XkX

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文