PCA(Principal Components Analysis)主成分分析是一个简单的机器学习算法,利用正交变换把由线性相关变量表示的观测数据转换为由少量线性无关比变量表示的数据,实现降维的同时尽量减少精度的损失,线性无关的变量称为主成分。大致流程如下:

  首先对给定数据集(数据是向量)进行规范化,使得数据集的平均值为0,方差为1(规范化是为了使数据散布在原点附近,而不是远离原点的某块区域,便于后面的计算)。之后对每个数据进行正交变换,把数据投影到几个少量的相互正交的方向(这些方向构成了数据空间的一个子空间)上。数据在每个方向上都有对应的坐标,而用这些方向和对应的坐标(坐标×方向的累加)就能近似表示原来高维的数据。因此这些方向的求解就是PCA的关键,这些方向有如下性质:

  如果再由这些坐标通过对应的方向映射回原来的数据,精度损失是同等方向数量的方向集合(或者叫同维度的子空间吧)中最小的,而数据集在各个方向上的坐标的方差之和是同等方向数量的方向集合中最大的,也正对应着方差越大表示这个方向上保存数据的信息量越大(方差越小所有数据越集中在一个点上信息量当然越小)。数据集在这些方向的上的坐标的方差,从大到小排序,就把这每一个方向称为第一主成分、第二主成分……

主成分推导

  接下来推导什么样的方向是主成分,即什么样的方向集合能保存原数据集更多的信息。下面把数据映射为各个方向上的坐标称为编码(即降维),再反映射回来称为解码。

  定义矩阵$X\in R^{m\times n}$为数据集,由$m$个$n$维行向量数据$\{x_1,x_2,...,x_m\}$组成。首先求得数据均值与方差(以下是按元素进行的运算):

$\displaystyle\overline{x}=\sum\limits_{i=1}^{m}\frac{x_i}{m}$

$\displaystyle s=\frac{1}{m-1} \sum\limits_{i=1}^{m}\left(x_i-\overline{x}\right)^2 $

  然后规范化样本:

$\displaystyle x_i^\ast = \frac{x_i-\overline{x}}{\sqrt{s}}, i=1,2,\ldots,m$

  获得规范化后的数据集$X^*$。随后PCA将把$X^*$编码成矩阵$Y^*\in R^{m\times k},k<n$,通过右乘列向量单位正交的方向矩阵$D\in R^{n\times k}$得到编码后的矩阵$Y^*$:

$Y^* = X^*\cdot D$

  而再解码回来是再乘上$D^T$:

$\hat{X^*} = Y^*\cdot D^{T}$

  因为矩阵$D$并不可逆,所以编码是不可逆的,也就是说解码并不能完全恢复回$X^*$。为了最小化精度损失,容易想到,优化$D$来最小化$\hat{X^*}$与$X^*$之差的Frobenius范数:

$ \begin{aligned} &D = \displaystyle\text{arg}\min\limits_D{||\hat{X^*} - X^*||^2_F}\\ &D = \text{arg}\min\limits_D{|| X^*DD^T - X^*||^2_F}\\ &D = \text{arg}\min\limits_D{\text{Tr}[(X^*DD^T - X^*)^T(X^*DD^T - X^*)]}\\ &D = \text{arg}\min\limits_D{\text{Tr}[DD^TX^{*T}X^*DD^T-DD^TX^{*T}X^*-X^{*T}X^*DD^T+X^{*T}X^*]}\\ \end{aligned} $

  去掉与$D$不相关的$X^{*T}X^*$项,再由迹的性质,矩阵乘法循环右移迹的值不变:

$ \begin{aligned} &D = \text{arg}\min\limits_D{\text{Tr}[X^*DD^TDD^TX^{*T}-X^*DD^TX^{*T}-D^TX^{*T}X^*D]}\\ &D = \text{arg}\min\limits_D{\text{Tr}[X^*DD^TX^{*T}-X^*DD^TX^{*T}-D^TX^{*T}X^*D]}\\ &D = \text{arg}\max\limits_D{\text{Tr}[D^TX^{*T}X^*D]}\\ \end{aligned} $

  容易发现当$D$的列向量取$X^{*T}X^*$的前$k$大特征值对应的特征向量时,有这个迹的值最大,等于前$k$大特征值之和,且此时压缩的精度损失最少。

性质

  经过上面的推导,我们可以发现,$X^{*T}X^*$实际上就是$X$行向量每个元素之间的协方差矩阵$\Sigma_x$(没有除以$m-1$)。定义$\lambda_1,\lambda_2,...,\lambda_n$为$\Sigma_x$从大到小排列的特征值。我们可以推出下面两个性质。

  性质一、编码得到的矩阵$Y^*\in R^{m\times k}$的行向量各个元素对应的协方差矩阵$\displaystyle\Sigma_y = \text{diag}(\lambda_1,\lambda_2,...,\lambda_k)$ 。

  性质二、如果不进行压缩,即编码矩阵形如$D\in R^{n\times n}$,从而$Y^*\in R^{m\times n}$,那么$Y^*$的行向量各个元素方差之和等于$X^*$($X$也一样)的行向量各个元素方差之和,也就是:

$\displaystyle\sum\limits_{i=1}^n \Sigma_{y_{ii}}= \sum\limits_{i=1}^n \Sigma_{x_{ii}} = \sum\limits_{i=1}^n (X^{*T}X^*)_{ii}$

  首先证明性质一。

  由之前的定义,$X^*$是$X$经过行标准化后的矩阵,所以对$X^*$的任意列进行求和有

$\displaystyle\sum\limits_{i=1}^mX^*_{ij}=0,\;\;j=1,2,...,n$

  而对于$Y^*=X^*D$,对$Y^*$的任意列进行求和有

$ \begin{aligned} \sum\limits_{i=1}^m Y^*_{ij} &= \sum\limits_{i=1}^mX^*_{i:}D_{:j} = \sum\limits_{i=1}^m\sum\limits_{k=1}^nX^*_{ik}D_{kj}\\ &= \sum\limits_{k=1}^nD_{kj}\sum\limits_{i=1}^mX^*_{ik}= 0,\;\;j=1,2,...,k \end{aligned} $

  每列求和都为0,说明,$Y^*$行向量每个元素的均值都为0,无需再计算行向量的均值进行标准化。因此,可以直接计算$Y^*$的协方差矩阵:

$ \begin{aligned} \Sigma_y = Y^{*T}Y^* = D^TX^{*T}X^*D=\text{diag}(\lambda_1,\lambda_2,...,\lambda_k) \end{aligned} $

  有性质一以后,性质二就容易了:

  如果不进行压缩,由性质一可得,$Y^*$行向量各元素方差之和就是$X^{*T}X^*$的特征值之和,矩阵特征值之和又等于矩阵的迹,而$X^{*T}X^*$的迹就是$X^*$行向量各个元素的方差之和。所以得到性质二。

  另外,映射矩阵$D$的列向量在人脸识别中就是所谓的特征脸(PCA+SVM实现人脸识别)。

手动代码实现

  实验代码:

import matplotlib.pyplot as plt
import pylab #原本在jupyter里才能显示的图片,可以用窗口显示
import numpy as np def PCA_img(img,n):
t = np.mean(img,0) #以行向量为压缩单元,求行平均
s = np.std(img,0) #求行标准差
img=(img-t)/s #每行减去平均值进行规范化
v,vectors = np.linalg.eig(np.dot(img.T,img)/(len(img)-1)) #生成规范化矩阵后,转置乘自身再除以(行数-1),即为行向量各个元素的协方差矩阵,求协方差矩阵的特征值与特征向量
indv = v.argsort() #将特征值排序,从小到大
vectors = vectors[:,indv] #将特征向量按特征值大小排序
vectors = vectors[:,-n:] #选择前n大特征值对应的特征向量,作为映射矩阵。在人脸识别中就是特征脸
img = np.dot(np.dot(img,vectors),vectors.T) #规范化矩阵先乘映射矩阵再乘映射矩阵的转置,也就是先压缩再解压缩
img = np.multiply(img,s)+t #把减掉的均值加回去变为原矩阵
return img img=plt.imread("aaa.jpg") #读取图片 img.flags["WRITEABLE"] = True
img = img.astype(float)
n = 50
img[:,:,0] = PCA_img(img[:,:,0],n)
img[:,:,1] = PCA_img(img[:,:,1],n)
img[:,:,2] = PCA_img(img[:,:,2],n)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(img/255)
print(img)
pylab.show()

  原图是1200×1920的图:

  压缩为1200×50后再解压回去:

sklearn包PCA

  sklearn中计算PCA比直接使用numpy矩阵计算快得多,这是因为它里面实现了比较高效的随机算法,虽然每次算出来的值不一样,但是相差不大。代码如下:

import sklearn.decomposition as dc 

pca = dc.PCA(n_components = 10)       #规定PCA要取前k大特征值对应的特征向量的个数,即要映射到几维,PCA默认压缩行,比如100*100压缩到100*10
pca.fit(A) #将要用于压缩的矩阵传入,用来“学习”压缩矩阵
D = pca.components_ #获取“学习”到的编码矩阵(压缩矩阵)D。原矩阵A经过AD^T会得到压缩后的矩阵(这里的D是上面推算的D^T)
C = pca.transform(B) #传入新的矩阵B,用“学习”到的编码矩阵进行压缩,即BD^T。在人脸特征提取中,同一个人的不同人脸照片,用同一个压缩矩阵压缩后的坐标向量通常相似,因此可以用于人脸识别的降维

PCA——主成分分析的更多相关文章

  1. 用PCA(主成分分析法)进行信号滤波

    用PCA(主成分分析法)进行信号滤波 此文章从我之前的C博客上导入,代码什么的可以参考matlab官方帮助文档 现在网上大多是通过PCA对数据进行降维,其实PCA还有一个用处就是可以进行信号滤波.网上 ...

  2. 机器学习之PCA主成分分析

    前言            以下内容是个人学习之后的感悟,转载请注明出处~ 简介 在用统计分析方法研究多变量的课题时,变量个数太多就会增加课题的复杂性.人们自然希望变量个数较少而得到的 信息较多.在很 ...

  3. PCA主成分分析Python实现

    作者:拾毅者 出处:http://blog.csdn.net/Dream_angel_Z/article/details/50760130 Github源代码:https://github.com/c ...

  4. 机器学习 - 算法 - PCA 主成分分析

    PCA 主成分分析 原理概述 用途 - 降维中最常用的手段 目标 - 提取最有价值的信息( 基于方差 ) 问题 - 降维后的数据的意义 ? 所需数学基础概念 向量的表示 基变换 协方差矩阵 协方差 优 ...

  5. PCA(主成分分析)方法浅析

    PCA(主成分分析)方法浅析 降维.数据压缩 找到数据中最重要的方向:方差最大的方向,也就是样本间差距最显著的方向 在与第一个正交的超平面上找最合适的第二个方向 PCA算法流程 上图第一步描述不正确, ...

  6. PCA主成分分析(上)

    PCA主成分分析 PCA目的 最大可分性(最大投影方差) 投影 优化目标 关键点 推导 为什么要找最大特征值对应的特征向量呢? 之前看3DMM的论文的看到其用了PCA的方法,一开始以为自己对于PCA已 ...

  7. PCA主成分分析方法

    PCA: Principal Components Analysis,主成分分析. 1.引入 在对任何训练集进行分类和回归处理之前,我们首先都需要提取原始数据的特征,然后将提取出的特征数据输入到相应的 ...

  8. 【建模应用】PCA主成分分析原理详解

    原文载于此:http://blog.csdn.net/zhongkelee/article/details/44064401 一.PCA简介 1. 相关背景 上完陈恩红老师的<机器学习与知识发现 ...

  9. PCA主成分分析+白化

    参考链接:http://deeplearning.stanford.edu/wiki/index.php/%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90 h ...

  10. CS229 6.6 Neurons Networks PCA主成分分析

    主成分分析(PCA)是一种经典的降维算法,基于基变换,数据原来位于标准坐标基下,将其投影到前k个最大特征值对应的特征向量所组成的基上,使得数据在新基各个维度有最大的方差,且在新基的各个维度上数据是不相 ...

随机推荐

  1. Codeforces Round #603 C. Everyone is a Winner!

    题意:给你一个整数n,求所有n/k的值(k∈{1,2,3...,n,.......}). 题解:最简单的方法是用枚举1~sqrt(n),把除数和商放进set中,就能直接水过,但后来看其他人的题解了解到 ...

  2. hdu3506 Monkey Party

    Problem Description Far away from our world, there is a banana forest. And many lovely monkeys live ...

  3. Python 装包与拆包

    装包就是把未命名的参数放到元组中,把命名参数放到字典中 a = 1, 2 print(a) (1, 2) 拆包将一个结构中的数据拆分为多个单独变量中 *args **kwargs def run1(* ...

  4. Web安全之SQL注入(原理,绕过,防御)

    首先了解下Mysql表结构 mysql内置的information_schema数据库中有三个表非常重要1 schemata:表里包含所有数据库的名字2 tables:表里包含所有数据库的所有的表,默 ...

  5. Redis-sentinel 哨兵(HA)

    Sentinel 介绍 Redis-Sentinel 是 Redis 官方推荐的高可用性(HA)解决方案,当用 Redis 做 Master-slave 的高可用方案时,假如Master 宕机了,Re ...

  6. 爬虫入门五 gooseeker

    title: 爬虫入门五 gooseeker date: 2020-03-16 16:00:00 categories: python tags: crawler gooseeker是一个简单的爬虫软 ...

  7. Gym 101464C - 计算几何+二分(uva1463)

    不是很难,但是我觉得对代码能力的要求还是挺高的. 注意模块化. 因为是浮点数,所以二分用的很多很多. 参考 https://blog.csdn.net/njupt_lyy/article/detail ...

  8. exgcd&&中国剩余定理专题练习

    hdu1573求中国剩余定理解的个数 #include <iostream> #include <cstdio> using namespace std; int a[100] ...

  9. LOJ6285 数列分块入门9(分块 区间众数)题解

    题意:给出区间内的最小众数 思路:分块,离散化每个数,开vector记录每个数p出现的位置,这样就能二分出L,R以内p的个数了.众数有一个性质,用mode(a)表示集合a的众数,那么mode(a∪b) ...

  10. 通过改写 原始对象的paint 方法,来设置对象的border颜色

    解决方案: //通过改写 原始对象的paint 方法,来设置对象的border颜色 1. package test1;import java.awt.Color;import java.awt.Gra ...