PCA(主成分分析法)的理解笔记及算法的实现

前几天搞定了Open3d库问题后,准备手撕PCA算法突然人麻了。我坚信学习是不断重复的过程,特此做个笔记,欢迎大家评论和交流!

感谢大佬的文章:

1、主成分分析(PCA)原理详解_Microstrong-CSDN博客_pca

2、CodingLabs - PCA的数学原理

3、(48条消息) 主成分分析PCA以及特征值和特征向量的意义_Mr.horse的博客-CSDN博客​​​​​​

4、(46条消息) 三维点云:PCA(下)open3d_eurus_的博客-CSDN博客 

--------------------------------------------------------分割线----------------------------------------------------------------

PCA 是有损的数据压缩方式,它常用于对高维数据进行降维,也就是把高维数据投影到方差大的几个主方向上,方便数据分析,方差越大,保留的信息也就越多这个也就是思想的核心

协方差矩阵:C=\frac{1}{m}*XX^{T}=\binom{\frac{1}{m}\sum_{i=1}^{m}(x_{i})^{2} {\frac{1}{m}\sum_{i=1}^{m}(x_{i}y_{i})}} {\frac{1}{m}\sum_{i=1}^{m}(x_{i}y_{i}){\frac{1}{m}\sum_{i=1}^{m}(y_{i})^{2}}}=\binom{Cov(x,x) | {Cov(x,y)}}{Cov(x,y) | {Cov(y,y)}}

1、为什么会推出这个形式的协方差矩阵呢?

答:是将协方差和方差统一到一个矩阵中,便于算法的实现。主对角线为方差,两边为协方差,且是个实对称矩阵。

2、方差的作用

答:公式\sigma ^{2}=\frac{1}{m}\sum_{i=0}^{m}(xi-E(x))^{2}

在PCA中往往都会先将样本归一化就是\mu _{i}=x_{i}-E(X),E(X)=\frac{1}{m}\sum_{i=1}^{M}x_{i}

所以整个均值就为0了,那么公式就可以表达为:\sigma ^{2}=\frac{1}{m}\sum_{i=1}^{m}(x_{i})^{2}

目的是为了在对应维度上的信息尽可能分散,无重合点,就是找方差最大。

3、协方差(这是协方差不是协方差矩阵,注意区分!)

答:对于高维来说,单一找方差最大的可能会找到一样的方向,这个时候就需要协方差起到约束作用,使得多方向线性无关最好正交。Cov(x,y)=0\rightarrow 不相关 {\rightarrow} 对于多维来说不相关就是独立。

那么就是对协方差矩阵C对角化,只有对角化Cov(x,y)=0,使基不独立互不干扰互不影响。

-----------------------------------------------------对C相似对角化--------------------------------------------------------

Y=\frac{1}{m}*XX^{T},对于n阶矩阵存在可逆矩阵P使得P^{-1}YP=\Lambda,又因为Y是实对称矩阵性质更好,不仅一定存在这样的P而且不同特征值对应的特征向量一定正交。

1、接下来对P进行单位化,为什么呢?

答:便于计算吧,对于A·B=|A||B|cos(a)当|B|=1时就是A在B上的投影,我们的坐标轴上的i,j(也可以叫做基会好理解点)都会默认为单位向量。打个比方向量b(3,5)其实x分量是投影到i轴上的长度为3,y分量是投影到j轴上的长度为5,如果i,j不是单位基的话你还要除以i,j的模才是b在这个基坐标下的坐标。

2、为什么特征值要从大到小排?

答:特征值可以简单理解为对应特征向量在总体所占的比重。PCA也可以从特征值理解特征值越小说明他的重要程度越小,我们越可以忽略它的重要性,不就可以达到PCA的思想核心了嘛,从N维把不重要的维度去掉降到K维(N<K)

----------------------------------------------------------PCA步骤----------------------------------------------------------

总结一下PCA的算法步骤:

设有m条n维数据。

1)将原始数据按列组成n行m列矩阵X

2)将X的每一行(代表一个属性字段)进行零均值化,即减去这一行的均值

3)求出协方差矩阵C=\frac{1}{m}*XX^{T}

4)求出协方差矩阵的特征值及对应的特征向量(向量还要单位化)

5)将特征向量按对应特征值大小从上到下按行排列成矩阵,取前k行组成矩阵P

6)Y=PX即为降维到k维后的数据

P其实就是变换矩阵,X在这组基下能找到信息量最大的方向,特征值对于变换后坐标对应维度的方差。Ax=\lambda x几何意义是没有旋转只对向量拉伸或者缩短多少\lambda倍,\lambda就是特征值。打个比方三维降到二维,数据点投影到这个主成分轴上越多说明\lambda越大,这个轴拉伸\lambda倍 。

------------------------------------------------算法的实现------------------------------------------------------------------

# 功能:计算PCA的函数
# 输入:
#     data:点云,NX3的矩阵
#     correlation:区分np的cov和corrcoef,不输入时默认为False
#     sort: 特征值排序,排序是为了其他功能方便使用,不输入时默认为True
# 输出:
#     eigenvalues:特征值
#     eigenvectors:特征向量
def PCA(data, correlation=False, sort=True)
    mean = np.array([np.mean(data[:, i]) for i in range(data.shape[1])])#对每一列求均值
    #print("mean:" , mean)
    x = data-mean         #归一化
    #print("normal:", x)
    #print("shape:", x.shape)
    c = np.dot(np.transpose(x),x)/5  #我这里是行和列是反的,所以转置在前,不过我们一般都习惯把样本按行特征按列存储
    print("C:", c)#协方差矩阵
    eigenvalues,eigenvectors=np.linalg.eig(c) #解出特征值特征向量,注意返回的是归一化后的特征向量
    print("eigenvalues:" ,eigenvalues)#特征值
    print("eigenvectors",eigenvectors)#特征向量
    #sum=np.array([np.sum(pow(eigenvectors[:,i],2)) for i in range(data.shape[1])])
    #mo=np.array([np.sqrt(sum[i]) for i in range(data.shape[1])])
    #print("sum",sum)
    #print("mo", mo)
    #singlevector=eigenvectors/mo
    #print("singlevector", singlevector)
    Y=np.dot(data,eigenvectors[1])#这里也是py里面的矩阵和我们习惯的要转置的
    print("Y",Y)#变换后的对应主成分向量上的投影值
    #eigenvectors=singlevector

    #if sort:
        #sort = eigenvalues.argsort()[::-1]#降序排序
        #eigenvalues = eigenvalues[sort]#这里面0是最大的
        #eigenvectors = eigenvectors[:, sort]
        #print("eigenvalues:",eigenvalues)
        #print("eigenvectors:", eigenvectors)
    #return eigenvalues, eigenvectors

if __name__ == '__main__':
    #测试
    X = np.array([[-1, -2], [-1, 0], [0, 0], [2, 1], [0, 1]])
    PCA(X)

    #三维点云计算及画出主成分向量
    #w, v = PCA(points)#自己三维点云数据传进来
    #point_cloud_vector1 = v[:, 0] #点云主方向对应的向量
    #point_cloud_vector2 = v[:, 1]
    #print('the main orientation of this pointcloud is: ', point_cloud_vector1)
    #print('the main orientation of this pointcloud is: ', point_cloud_vector2)
    # 在原点云中画图
    #point = [[0, 0, 0], point_cloud_vector1*100, point_cloud_vector2*100]  # 画点:原点、第一主成分、第二主成分
    #lines = [[0, 1], [0, 2]]  # 画出三点之间两两连线
    #colors = [[1, 0, 0], [0, 0, 0]]
    # 构造open3d中的LineSet对象,用于主成分显示
    #line_set = o3d.geometry.LineSet(points=o3d.utility.Vector3dVector(point), lines=o3d.utility.Vector2iVector(lines))
    #line_set.colors = o3d.utility.Vector3dVector(colors)
    #o3d.visualization.draw_geometries([point_cloud_o3d, line_set])  # 显示原始点云和PCA后的连线

最后的结果是

                                                                                 如有错误欢迎评论留言指出感谢!共同进步!

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值