PCA的原理与实现
PCA原理
引入
PCA主要用于数据降维。
假设将二维数据降到一维,如下图所示,则希望变换后的散度越大越好,如C和D因为太靠近就会重合,导致丢失信息。那就是使方差最大化。我们还希望不同特征之间不相关(如果两个特征线性相关,说明其中一个可以用另一个来表达,那信息就重复了),而要线性无关,即协方差为0。
所以我们需要让每一个特征内方差最大,不同特征间协方差为0。
初始矩阵为X,它是m×n维的矩阵,其中:m是该数据集有m条记录,n是每条记录中有n个特征:
目的是让X从m×n降维到m×r。
将该矩阵变换到新的坐标空间就是右乘新坐标空间的基向量组成的矩阵:
求方差
每一列的方差为:
这是没有对X进行均值化的情况,如果对X均值化:
X均值化后,Y=X·P也均值化了:
现在;Y的每一列的方差为:
写成矩阵形式:
求协方差
Y中任意两列的协方差为:
协方差公式中还包含均值,这里Y已经0均值化,所以就没有了。
上式写成矩阵形式:
满足两个条件
现在,我们要让Y的每一列列内的方差最大,不同列间的协方差为0。
我们正好想到,Y的协方差矩阵,正好就是这两个要求。因为Y被我们零均值化,所以,它的协方差矩阵,我们可以写为矩阵形式:
我们的要求是让Y每一列自身方差最大,任意两列协方差为0,正好不就是让Y的协方差矩阵Cov(Y)的对角线元素最大,让非对角元素为0,就是让\(Y^T\cdot Y\)是对角阵,且让这个矩阵的对角元素最大。也就是我们期望Cov(Y)是这个形式:
我们回想一下,P是m×r的满秩矩阵,我们的目的是找到这么一个矩阵P,使得\(Y^T\cdot Y\)满足上述要求,而上述公式根据大学学的线性代数,这个问题非常类似实矩阵对角化问题。
因为\(\frac{1}{m-1}X^T\cdot X\)是实对称矩阵,所以一定可以相似对角化,且特征值\(\lambda_i≥0,i\in(0,r)\),即存在满秩矩阵Q(n×n维)使得:
而这个正交矩阵Q是由特征向量组成的矩阵[ Q1, Q2,...Qn ],且Qi是与的顺序对应的,我们假设对角阵中的从大到小排列起来了,即\(\lambda_1≥\lambda_2≥...≥\lambda_n\),那么对应的特征向量Qi也要根据\(\lambda_i\)的顺序排列好,我们观察目标矩阵Cov(Y)和对角化矩阵\(\Lambda\),发现Cov(Y)矩阵是\(\Lambda\)的一部分,我们需要Cov(Y)的对角元素最大,也就是要取\(\Lambda\)中的\(\lambda_i\)最大的几个,而P也是根据选取的\(\lambda_i\)而从Q中选取。比如我们要降维到m×r维,只需取\(\Lambda\)中前r个特征值,然后从Q中找到这r个特征值对应的特征向量[ Q1,Q2,...Qr ],这些特征向量组成的矩阵就是我们要找的P矩阵。
说明下,虽然样本方差的分母是应该为m-1,但是上面所以式子分母都可以用m,分母采用m是因为这样算出来的样本方差Var(X)为一致估计量,不会太影响计算结果并且可以减小运算负担。
算法实现
算法流程
① 对原数据集零均值化。代码是:meanRemoved = dataMat - mean(dataMat,axis=0)
② 求出均值化X的协方差矩阵:公式是:,代码是:covMat = cov(meanRemoved,rowvar=0)
③ 求这个协方差矩阵的特征值,特征向量,代码是:eigVals, eigVects = linalg.eig(mat(covMat))
④ 把这些特征值按从大到小排列,返回特征值的下标,代码是:eigValInd = argsort(-eigVals)
⑤ 选出前topNfeat个特征值,返回这些选中的特征值的下标,并根据下标从特征向量矩阵eigVects中取出这些选中的特征向量组成矩阵P,这就是我们要找的变换矩阵P,代码是:redEigVects = eigVects[:,eigValInd[:topNfeat] ]
⑥ 返回降维后的数据,公式是:Y=X•P,代码是:lowDDataMat = meanRemoved * redEigVects
⑦ 原数据映射到新的空间中。公式是:,代码是:reconMat = (lowDDataMat * redEigVects.T) + meanValues
代码
import numpy as np
import matplotlib.pyplot as plt
def loadDataSet(fileName):
dataSetList = []
fr = open(fileName)
for row in fr.readlines():
cur_line = row.strip().split('\t')
proce_line = list(map(float,cur_line))
dataSetList.append(proce_line)
dataSetList = np.array(dataSetList)
return dataSetList
def pca(dataMat, topNfeat = 999999):
meanValues = np.mean(dataMat,axis=0) # 竖着求平均值,数据格式是m×n
meanRemoved = dataMat - meanValues # 0均值化 m×n维
covMat = np.cov(meanRemoved,rowvar=0) # 每一列作为一个独立变量求协方差 n×n维
eigVals, eigVects = np.linalg.eig(np.mat(covMat)) # 求特征值和特征向量 eigVects是n×n维
eigValInd = np.argsort(-eigVals) # 特征值由大到小排序,eigValInd十个arrary数组 1×n维
eigValInd = eigValInd[:topNfeat] # 选取前topNfeat个特征值的序号 1×r维
redEigVects = eigVects[:,eigValInd] # 把符合条件的几列特征筛选出来组成P n×r维
lowDDataMat = meanRemoved * redEigVects # 矩阵点乘筛选的特征向量矩阵 m×r维 公式Y=X*P
reconMat = (lowDDataMat * redEigVects.T) + meanValues # 转换新空间的数据 m×n维
return lowDDataMat, reconMat
def drawPoints(dataset1,dataset2): # 画图,dataset1是没降维的数据,dataset2是数据映射到新空间的数据
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.scatter(dataset1[:,0],dataset1[:,1],marker='s',s=40,color='red')
dataset2 = np.array(dataset2)
ax2.scatter(dataset2[:,0],dataset2[:,1],s=40,color='blue')
plt.savefig('dataset1.png')
plt.show()
if __name__ == '__main__':
data = loadDataSet('C:/Users/Peng Wei/Desktop/pca_dataset2.txt')
proccess_data, reconMat = pca(data,1)
drawPoints(data,reconMat)
原文地址:https://www.cnblogs.com/pengweiblog/p/12572257.html
- Android面试系列之AsyncTask
- Kali-Linux扩充弹药:Kali Linux metapackages
- 使用HackRF解调TDD-LTE信号
- 一个优秀的Android应用从建项目开始
- Ruby OpenSSL 私钥伪造脚本
- 基于 k8s 的 Jenkins 构建集群实践
- Visual C#.Net网络程序开发-Tcp篇(1) 祥细内容:
- 无服务器化的微服务持续交付
- Visual C#.Net网络程序开发-Tcp篇(2) 祥细内容:
- 看你是否够老 – ipman的vxd程序介绍的翻译
- Visual C#.Net网络程序开发-Tcp篇(3) 祥细内容:
- 安全科普:流量劫持能有多大危害?
- OpenSSL心脏出血漏洞全回顾
- Nmap扫描对比工具–libnmap实践
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- C++核心准则Con.3:默认情况下,传递参照常量的指针或引用
- C++核心准则Con.4:如果一个对象在构建之后值不会改变,使用const定义它
- C++核心准则Con.5:对于可以在编译时计算的值,使用constexpr进行声明
- DB2 Linux平台安装 Part 4 创建数据库
- VBA编写Ribbon Custom UI编辑器03——认识Ribbon的xml
- VBA编写Ribbon Custom UI编辑器04——解析xml
- VBA编写Ribbon Custom UI编辑器05——转换结构体XML
- MySQL 8.0.19 Linux平台安装 Part 1
- MySQL 8.0.19 Linux平台安装 Part 2
- 使用XtraBackup备份MySQL 8.0 Part 1 xtrabackup 8.0 安装
- 10个解放双手的 IDEA 插件,少些冤枉代码!
- 二叉树的 4 种遍历方式,你会多少?
- 【C++简明教程】Python和C++指定元素排序比较
- PG原生解码工具pg_recvlogical的使用-在脑裂时帮我们找回丢失的数据
- 使用XtraBackup备份MySQL 8.0 Part 4 对数据库进行全备