前几天搞定了Open3d库问题后,准备手撕PCA算法突然人麻了。我坚信学习是不断重复的过程,特此做个笔记,欢迎大家评论和交流!
感谢大佬的文章:
1、主成分分析(PCA)原理详解_Microstrong-CSDN博客_pca
3、(48条消息) 主成分分析PCA以及特征值和特征向量的意义_Mr.horse的博客-CSDN博客
4、(46条消息) 三维点云:PCA(下)open3d_eurus_的博客-CSDN博客
--------------------------------------------------------分割线----------------------------------------------------------------
PCA 是有损的数据压缩方式,它常用于对高维数据进行降维,也就是把高维数据投影到方差大的几个主方向上,方便数据分析,方差越大,保留的信息也就越多这个也就是思想的核心
协方差矩阵:
1、为什么会推出这个形式的协方差矩阵呢?
答:是将协方差和方差统一到一个矩阵中,便于算法的实现。主对角线为方差,两边为协方差,且是个实对称矩阵。
2、方差的作用
答:公式
在PCA中往往都会先将样本归一化就是
所以整个均值就为0了,那么公式就可以表达为:
目的是为了在对应维度上的信息尽可能分散,无重合点,就是找方差最大。
3、协方差(这是协方差不是协方差矩阵,注意区分!)
答:对于高维来说,单一找方差最大的可能会找到一样的方向,这个时候就需要协方差起到约束作用,使得多方向线性无关最好正交。 不相关
对于多维来说不相关就是独立。
那么就是对协方差矩阵C对角化,只有对角化Cov(x,y)=0,使基不独立互不干扰互不影响。
-----------------------------------------------------对C相似对角化--------------------------------------------------------
令,对于n阶矩阵存在可逆矩阵P使得
,又因为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)求出协方差矩阵
4)求出协方差矩阵的特征值及对应的特征向量(向量还要单位化)
5)将特征向量按对应特征值大小从上到下按行排列成矩阵,取前k行组成矩阵P
6)Y=PX即为降维到k维后的数据
P其实就是变换矩阵,X在这组基下能找到信息量最大的方向,特征值对于变换后坐标对应维度的方差。几何意义是没有旋转只对向量拉伸或者缩短多少
倍,
就是特征值。打个比方三维降到二维,数据点投影到这个主成分轴上越多说明
越大,这个轴拉伸
倍 。
------------------------------------------------算法的实现------------------------------------------------------------------
# 功能:计算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后的连线
最后的结果是
如有错误欢迎评论留言指出感谢!共同进步!