主成分分析PCA(Principal Component Analysis)在sklearn中的应用及部分源码分析
最近太忙,又有一段时间没写东西了。
pca是机器学习中一个重要的降维技术,是特征提取的代表。关于pca的实现原理,在此不做过多赘述,相关参考书和各大神牛的博客都已经有各种各样的详细介绍。 如需学习相关数学理论,请移驾。T_T
简单说一下pca的实现,首先对于一个矩阵X,我们计算X·XT,显然这个一个半正定矩阵,可以做特征值分解,然后取出k个最大的特征值及其对应的特征向量就可以表达整个原矩阵。若X·XT=p-1Λp,因为p是单位矩阵,所以p-1=pT,即X·XT=p-1·Λ1/2·(p-1·Λ1/2)T,也就是降维的X后用来p-1·Λ1/2表示。
其实从SVD的角度来理解也是一样的,若X=UΣVT,则X·XT=UΣ2UT,同样我们用来UΣ来表示原X。
当我看sklearn的文档时,文档并没有具体解释它的方法得到的结果在数学上的表示什么,钻研了半天,看了源码后才知道。
sklearn的方法是通过SVD来实现的。这里着重介绍sklearn的pca类中的一个属性(components_)和两个方法(fit,transform)。
首先,给定一个矩阵,设置参数后,通过调用fit方法得到降维模型,也就是一个基矩阵。我们看一下fit中的部分关键代码。
...
self.mean_ = np.mean(X, axis=0)
X -= self.mean_
U, S, V = linalg.svd(X, full_matrices=False)
U, V = svd_flip(U, V)
components_ = V
...
self.components_ = components_[:n_components]
...
首先做的工作就是对数据进行按列中心化,然后做svd分解,然后把V的前k个向量保存为模型,模型的关键内容就是components_。(看似几百行代码,T_T)
接下来看看transfrom的部分关键源码。
...
X = np.dot(X, self.components_.T)
return X
我只贴这两句,T_T,因为真的只有这两句特别重要。也就是说我们要把一个新矩阵用到训练好的pca模型中时,其实只是做了一次矩阵乘法而已。
怎么来理解作者的做法呢?
其实就是我们上面的提到的,用svd方式来实现pca时,我们实际上用UΣ来表示降维后的数据。综合svd公式,可以看成XV=UΣ,也就是把原矩阵与V做乘法,实际上这里理解为投影,把X投影到V的单位正交基所表示的子空间中去得到X的低维表示。对于一个新的矩阵Y,同样用YV来表示其在V子空间降维后的结果,这也就是为什么transform方法为什么最关键的步骤只有一步乘法了。回过头看,fit训练模型就是要得到V,然后在transform降维时只需要一步乘法就可以了。
我们用下面的代码做个小实验。
import numpy as np
from sklearn.decomposition import PCA
from sklearn import preprocessing
from sklearn.utils.extmath import svd_flip svd = np.linalg.svd A = np.random.randint(0,5, (5,3)) print('A=')
print(A) pca = PCA(n_components=2, svd_solver='full') # print(A)
model = pca.fit(A)
print('model.components_=')
print(model.components_)
X = model.transform(A)
print('pca(A)=')
print(X) A = A - np.mean(A, axis=0)
u,s,v = svd(A, full_matrices=False) print('V=')
print(v[:2])
print('pva_by_svd=')
print(np.dot(A, v[:2].T))
运行得到的结果如下
A=
[[3 1 4]
[4 1 1]
[1 2 0]
[1 4 2]
[1 3 2]]
model.components_=
[[ 0.70734192 -0.61231721 0.35317848]
[ 0.19273774 -0.31363722 -0.92977624]]
pca(A)=
[[ 2.21911523 -1.47640531]
[ 1.86692171 1.50566115]
[-1.22059974 1.54358693]
[-1.73887721 -0.94323999]
[-1.12656 -0.62960277]]
V=
[[-0.70734192 0.61231721 -0.35317848]
[ 0.19273774 -0.31363722 -0.92977624]]
pva_by_svd=
[[-2.21911523 -1.47640531]
[-1.86692171 1.50566115]
[ 1.22059974 1.54358693]
[ 1.73887721 -0.94323999]
[ 1.12656 -0.62960277]]
咦,结果跟之前的分析有点小小的差别,通过svd和sklearn的结果对比,V的第一行和降维结果的第一列正负相反了。
多运行几次,这个现象并不一定出现。其实是通过上面svd_flip函数来实现的。
sklearn对奇异分解结果进行了一个处理,因为ui*σi*vi=(-ui)*σi*(-vi),也就是u和v同时取反得到的结果是一样的,而这会导致通过pca降维得到不一样的结果(虽然都是正确的)。为了追求唯一的表示,首先定位ui向量中绝对值最大的元素位置,如果它为负数,则ui和vi取反,否则不变。这部分的源码在这里。
主成分分析PCA(Principal Component Analysis)在sklearn中的应用及部分源码分析的更多相关文章
- R: 主成分分析 ~ PCA(Principal Component Analysis)
本文摘自:http://www.cnblogs.com/longzhongren/p/4300593.html 以表感谢. 综述: 主成分分析 因子分析 典型相关分析,三种方法的共同点主要是用来对数据 ...
- RocketMQ中Broker的HA策略源码分析
Broker的HA策略分为两部分①同步元数据②同步消息数据 同步元数据 在Slave启动时,会启动一个定时任务用来从master同步元数据 if (role == BrokerRole.SLAVE) ...
- 【Java】NIO中Selector的select方法源码分析
该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...
- RocketMQ中Broker的刷盘源码分析
上一篇博客的最后简单提了下CommitLog的刷盘 [RocketMQ中Broker的消息存储源码分析] (这篇博客和上一篇有很大的联系) Broker的CommitLog刷盘会启动一个线程,不停地 ...
- Flink中Idle停滞流机制(源码分析)
前几天在社区群上,有人问了一个问题 既然上游最小水印会决定窗口触发,那如果我上游其中一条流突然没有了数据,我的窗口还会继续触发吗? 看到这个问题,我蒙了???? 对哈,因为我是选择上游所有流中水印最小 ...
- List中的ArrayList和LinkedList源码分析
List是在面试中经常会问的一点,在我们面试中知道的仅仅是List是单列集合Collection下的一个实现类, List的实现接口又有几个,一个是ArrayList,还有一个是LinkedLis ...
- 【Android笔记】Thread类中关于join()方法的源码分析
1.join()方法的作用: 例如有一个线程对象为Thread1,在main()方法中调用Thread1.join()方法可使得当前线程(即主线程)阻塞,而执行Thread1线程. 2.源码分析(以上 ...
- Fabric2.2中的Raft共识模块源码分析
引言 Hyperledger Fabric是当前比较流行的一种联盟链系统,它隶属于Linux基金会在2015年创建的超级账本项目且是这个项目最重要的一个子项目.目前,与Hyperledger的另外几个 ...
- MapReduce中map并行度优化及源码分析
mapTask并行度的决定机制 一个job的map阶段并行度由客户端在提交job时决定,而客户端对map阶段并行度的规划的基本逻辑为:将待处理数据执行逻辑切片(即按照一个特定切片大小,将待处理数据划分 ...
随机推荐
- Linux 读取 (*.xls)文件读取,使用libxls库
首先下载libxls,项目地址:http://sourceforge.net/projects/libxls/,备用下载:libxls-1.4.0.zip 解压后使用: ./configure --p ...
- saltstack学习之一:服务架构以及相关配置安装运行
概要 saltstack是基于Python开发的C/S架构的一款批量管理工具,底层采用动态的连接总线(ZeroMQ消息队列pub/sub方式通信),使用ssl证书签发的方式进行认证管理,使其可以用于编 ...
- Omi-touch实战 移动端图片轮播组件的封装
pc端的轮播,移动端的轮播都很常见.一年前,我还为手机端没有左滑,右滑事件从而封装了一个swipe库,可以自定义超过多少滑动时间就不触发,也可以设置滑动多少距离才触发,这一个功能的代码就达到400多行 ...
- vue 动态加载组建
<component :is="comp1"></component> data () { return { comp1:'', } } require.e ...
- Ubuntu 16.04 下部署Node.js+MySQL微信小程序商城
转载于这篇文章 关于pm2看这篇文章 最近在研究小程序,申请了域名之后,再一次来配置环境,根据作者的步骤基本上完成了网站的架构,但由于环境路径等不同,配置上会有所不同,因此记录下来. 1.更新系统和安 ...
- 洛谷 P4409 [ZJOI2006] 皇帝的烦恼
题目链接-> OVO 题解: 很久没有写博客了,可能是因为最近太颓废了吧. 刚刚考完期末考试,无比期盼早点外出学习,不要面对成绩,害怕. #include <cstdio> #inc ...
- R语言学习 第十篇:包
包(Package)是实现特定功能的.预先写好的代码库(library),通俗地说,包是含有函数.数据等的功能模块.R拥有大量的软件包,许多包都是由某一领域的专家编写的,但并不是所有的包都有很高的质量 ...
- 一文让你熟练掌握Linux的ncat(nc)命令
一文让你熟练掌握Linux的ncat(nc)命令 ncat 或者说 nc 是一款功能类似 cat 的工具,但是是用于网络的.它是一款拥有多种功能的 CLI 工具,可以用来在网络上读.写以及重定向数据. ...
- 开启C语言的学习之门
本人是一枚工业界的码农,为了职业道路越来越宽广决定向上位机方面进军,C语言曾经在大学里面学过点皮毛但是离应用远远不够,尽量每天在工作之余更新自己学习的进度,同时也希望有大神能给予在编程道路上的指导,话 ...
- 初级Java工程师面试所遇面试题
1.servlet的生命周期 : 一.百度百科 : 1.客户端请求servlet: 2.加载servlet类到内存: 3.实例化并调用init()方法初始化servlet: 4.调用service() ...