机器学习手记[11]---PCA变换基础
来源:互联网 发布:拳皇2002网络对战 编辑:程序博客网 时间:2024/05/29 10:45
1. 初衷
1 达到降维的目的
2 将原来的维度进行转换,转换到新的维度组上,维度之间相互正交,且方差大小依次递减。
2. 需求
数据样本数有40个,特征有3个,如何降维到2个维度,且两个维度相互正交线性无关.
目的:将一组特征向量,通过线性变换,转换为一组线性无关的变量。
3. 步骤
1. 生成【3*40】的实验数据
维度为3,样本数为40,我们生成两个类的数据,每个类的样本是20个,但在PCA变换把整个3*40均当作一个类进行处理。
def loadData(): mu_vec1 = np.array([0,0,0]) cov_mat1 = np.array([[1,0,0],[0,1,0],[0,0,1]]) class1_sample = np.random.multivariate_normal(mu_vec1, cov_mat1, 20).T assert class1_sample.shape == (3,20), "The matrix has not the dimensions 3x20" mu_vec2 = np.array([1,1,1]) cov_mat2 = np.array([[1,0,0],[0,1,0],[0,0,1]]) class2_sample = np.random.multivariate_normal(mu_vec2, cov_mat2, 20).T assert class2_sample.shape == (3,20), "The matrix has not the dimensions 3x20" all_samples=np.concatenate((class1_sample,class2_sample),axis=1) assert all_samples.shape==(3,60),"The matrix has not the dimensions 3x40" return all_samples
2. 求特征协方差矩阵【3*40】
- 计算样本集在三个维度的平均值。
- 计算样本集在三个维度上的协方差之和
def scatterMatCreate(dataArray): all_samples=dataArray meanFirst=np.mean(all_samples[0]) meanSecond=np.mean(all_samples[1]) meanThird=np.mean(all_samples[2]) meanVector=np.array([meanFirst,meanSecond,meanThird]) scatterMat=np.zeros((3,3)) for i in range(all_samples.shape[1]): matPartA=(all_samples[:,i]-meanVector).reshape(3,1) matPartB=(all_samples[:,i]-meanVector).reshape(1,3) scatterMat+=matPartA * matPartB scatterMat/float(all_samples.shape[1]) #将各个样本的方差进行累加 return scatterMat
特征向量组中包含的向量有三个,其中对应特征值最大的特征向量是主特征向量,这个向量对应的方向反应了整个数据集具有最大方差的方向,特征值次大的特征向量对应的则反应了次大的方差方向,依次类推。
在特征向量组【3*3】里面,每个特征向量的维度是【3*1】,因此我们筛选的两个特征向量组的大小是【3*2】
这三个特征向量是相互正交的。
def eigenCompute(scatterMat): eigenValArray,eigenVecArray=np.linalg.eig(scatterMat) sortedIndex=np.argsort(eigenValArray) topVecNum=2 eigenFinalList=[] for i in range(topVecNum): eigenVec=eigenVecArray[:,sortedIndex[i]] #注意计算好eigen的大小shape eigenVec 3*1 eigenVec=list(eigenVec) eigenFinalList.append(eigenVec) eigenMat=np.array(eigenFinalList).reshape(2,3) return eigenMat
4 根据特征值大小,选取前两个特征值对应的特征向量,进行PCA特征变换
利用特征变换向量组进行特征变换。 EigenMat【2*3】*Data【3*40】=newData【2*40】
<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="python">def PCArun(): dataArray=loadData() #源数据 scatterMat=scatterMatCreate(dataArray) #协方差矩阵 eigenMat=eigenCompute(scatterMat) #计算特征值和特征向量 newMat=eigenMat.dot(dataArray) #进行PCA特征变换 return newMat
特征变换后的效果
5 恢复源维度信息
PCA实现了三维到二维的降维,而如果我们想从二维返回到三维信息呢?
5.1 如果我们不降维的话
我们直接将特征向量组进行特征变换,那么我们得到的PCAData数据也是三维的,三个维度相互正交。
DataMat*EigenMat=PCAData
如果要返回到来的数据,我们求逆。
DataMat=PCAData*inverse(EigenMat)
但是由于特征矩阵比较特殊是正交矩阵, 它的逆矩阵,等于,它的转置矩阵。
因此DataMat=PCAData*transpose(EigenMat)
这样我们求的DataMat和原始数据是完全一样的,因为我们没有降维,没有信息损失。 也就是说我们还是用三个维度去描述我们的数据,只不过这三个轴的位置不一样了,但是数据本身并没有变化。
5.2 如果我们降维的话
降维的话, 其实特征矩阵也满足这个条件,也就是说,我们还可以利用矩阵的逆等于矩阵的转置这个条件,进行计算。
PCA变换:
DataMat*EigenMat=PCAData 这个时候PCAData是二维的,DataMat是三维的。
PCA逆变换
DataMat=PCAData*transpose(EigenMat)
这个时候DataMat相对于PCAData肯定是有信息损失的。
可视化如下:
从视角A我们看到是散乱均匀分布的,但是当我们旋转的时候,我们就会发现,PCA变换恢复得到矩阵其实是少了一个维度的信息的。
整体的源代码如下,
# -*- coding: utf-8 -*-import numpy as npfrom matplotlib import pyplot as pltfrom mpl_toolkits.mplot3d import Axes3Dfrom mpl_toolkits.mplot3d import proj3dfrom matplotlib.patches import FancyArrowPatchdef loadData(): mu_vec1 = np.array([0,0,0]) cov_mat1 = np.array([[1,0,0],[0,1,0],[0,0,1]]) class1_sample = np.random.multivariate_normal(mu_vec1, cov_mat1, 20).T assert class1_sample.shape == (3,20), "The matrix has not the dimensions 3x20" mu_vec2 = np.array([1,1,1]) cov_mat2 = np.array([[1,0,0],[0,1,0],[0,0,1]]) class2_sample = np.random.multivariate_normal(mu_vec2, cov_mat2, 20).T assert class2_sample.shape == (3,20), "The matrix has not the dimensions 3x20" all_samples=np.concatenate((class1_sample,class2_sample),axis=1) assert all_samples.shape==(3,40),"The matrix has not the dimensions 3x40" return all_samples #协方差矩阵def scatterMatCreate(dataArray): all_samples=dataArray meanFirst=np.mean(all_samples[0]) meanSecond=np.mean(all_samples[1]) meanThird=np.mean(all_samples[2]) meanVector=np.array([meanFirst,meanSecond,meanThird]) scatterMat=np.zeros((3,3)) for i in range(all_samples.shape[1]): matPartA=(all_samples[:,i]-meanVector).reshape(3,1) matPartB=(all_samples[:,i]-meanVector).reshape(1,3) scatterMat+=matPartA * matPartB scatterMat/float(all_samples.shape[1]) #将各个样本的方差进行累加 return scatterMat #特征向量提取 def eigenCompute(scatterMat): eigenValArray,eigenVecArray=np.linalg.eig(scatterMat) sortedIndex=np.argsort(-eigenValArray) #降序排列 topVecNum=2 eigenFinalList=[] for i in range(topVecNum): eigenVec=eigenVecArray[:,sortedIndex[i]] eigenVec=list(eigenVec) eigenFinalList.append(eigenVec) eigenMat=np.array(eigenFinalList).reshape(2,3) return eigenMat#显示三维数据def imgShow3DData(dataArray): dim=dataArray.shape[0] fig=plt.figure(figsize=(10,10)) #plt-->figure-->subplot-->plot ax=fig.add_subplot(111,projection='3d') ax.plot(dataArray[0,:],dataArray[1,:],dataArray[2,:],'o',markersize=8,color='green',alpha=0.5, label='Original Points') def imgShowPCAData(dataArray): dim=dataArray.shape[0] fig=plt.figure(figsize=(10,10)) #plt-->figure-->subplot-->plot ax=fig.add_subplot(111) ax.plot(dataArray[0,:],dataArray[1,:],'o',markersize=8,color='green',alpha=0.5, label='PCAed Points')#三维矢量箭头的显示方法class Arrow3D(FancyArrowPatch): def __init__(self,xs,ys,zs,*args,**kwargs): FancyArrowPatch.__init__(self,(0,0),(0,0),*args,**kwargs) self._verts3d=xs,ys,zs def draw(self,renderer): xs3d,ys3d,zs3d=self._verts3d xs,ys,zs=proj3d.proj_transform(xs3d,ys3d,zs3d,renderer.M) self.set_positions((xs[0],ys[0]),(xs[1],ys[1])) FancyArrowPatch.draw(self,renderer)#显示原始数据和两个特征向量方向 def imgShowEigenVec(dataArray,eigenMat): fig=plt.figure(figsize=(10,10)) #plt-->figure-->subplot-->plot ax=fig.add_subplot(111,projection='3d') ax.plot(dataArray[0,:],dataArray[1,:],dataArray[2,:],'o',markersize=8,color='green',alpha=0.5, label='Original Points') meanFirst=np.mean(dataArray[0]) meanSecond=np.mean(dataArray[1]) meanThird=np.mean(dataArray[2]) meanVector=np.array([meanFirst,meanSecond,meanThird]) for v in eigenMat: a=Arrow3D([meanVector[0],v[0]],[meanVector[1],v[1]],[meanVector[2],v[2]], mutation_scale=20, lw=3,arrowstyle="-|>",color="r") ax.add_artist(a)#PCA变换半恢复 def restoredDataArray(PCADataArray, eigenMat): restoredDataArray=eigenMat.T.dot(PCADataArray) imgShow3DData(restoredDataArray)#主函数 def PCArun(): dataArray=loadData() scatterMat=scatterMatCreate(dataArray) #协方差矩阵 eigenMat=eigenCompute(scatterMat) #特征向量提取 PCADataArray=eigenMat.dot(dataArray) #PCA变换 imgShow3DData(dataArray) #显示原始三维数据 imgShowEigenVec(dataArray,eigenMat) #显示原始数据和两个特征向量方向 imgShowPCAData(PCADataArray) #显示PCA变换后的2维数据 restoredDataArray(PCADataArray, eigenMat) #显示PCA变换半恢复后的数据
改编自:
可视化和数据源参考 http://sebastianraschka.com/Articles/2014_pca_step_by_step.html#introduction
PCA变换后的半恢复参考
http://www.cs.otago.ac.nz/cosc453/student_tutorials/principal_components.pdf
- 机器学习手记[11]---PCA变换基础
- 【机器学习基础】非线性变换
- 【机器学习基础】非线性变换
- 【机器学习基础】非线性变换
- 机器学习经典算法11-PCA
- 机器学习笔记(11)-PCA/SVD
- 机器学习之PCA
- [机器学习] PCA理论
- 机器学习算法-PCA
- 机器学习PCA实现
- 机器学习----LDA、PCA
- 机器学习之PCA
- 机器学习基础——PCA(主成分分析)
- 机器学习 8 PCA cont.
- 机器学习实战之PCA
- [机器学习] PCA matlab代码
- 机器学习实战之PCA
- 机器学习(十):PCA
- Hilite Edit View
- LA-3415 & POJ-2771 Guardian of Decency 解题报告
- Ubuntu破解root密码
- 数组与内存控制
- 分享一个链接
- 机器学习手记[11]---PCA变换基础
- Linux错误码整理
- C语言从txt文件中逐行读入数据存到数组中
- Text Editor: The Basic of Basics
- hdu 3468 Treasure Hunting【二分匹配+最短路】
- UITableView 的一些效果 透明和section的高度
- C语言中字符串常量到底存在哪了?
- Column based selection in rich edit control.
- l蓝桥杯备战- Huffuman树