首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【机器学习-无监督学习】降维与主成分分析

【机器学习-无监督学习】降维与主成分分析

作者头像
Francek Chen
发布2025-01-22 23:10:51
发布2025-01-22 23:10:51
36000
代码可运行
举报
运行总次数:0
代码可运行

机器学习是一门人工智能的分支学科,通过算法和模型让计算机从数据中学习,进行模型训练和优化,做出预测、分类和决策支持。Python成为机器学习的首选语言,依赖于强大的开源库如Scikit-learn、TensorFlow和PyTorch。本专栏介绍机器学习的相关算法以及基于Python的算法实现。

【GitCode】专栏资源保存在我的GitCode仓库:https://gitcode.com/Morse_Chen/Python_machine_learning


  在上一篇文章聚类中,我们介绍了无监督学习的重要问题之一:聚类问题,并主要讲解了k均值算法。结尾处我们提到,在解决复杂聚类问题时,第一步通常不会直接使用k均值算法,而是会先用其他手段提取数据的有用特征。对于高维复杂数据来说,其不同维度代表的特征可能存在关联,还有可能存在无意义的噪声干扰。因此,无论后续任务是有监督学习还是无监督学习,我们都希望能先从中提取出具有代表性、能最大限度保留数据本身信息的几个特征,从而降低数据维度,简化之后的分析和计算。这一过程通常称为数据降维(dimensionality reduction),同样是无监督学习中的重要问题。本文就来介绍数据降维中最经典的算法——主成分分析(principal component analysis,PCA)。

一、降维

(一)维数灾难

维数灾难(Curse of Dimensionality)通常是指在涉及到向量的计算的问题中,随着维数的增加,计算量呈指数倍增长的一种现象。在很多机器学习问题中,训练集中的每条数据经常伴随着上千、甚至上万个特征。要处理这所有的特征的话,不仅会让训练非常缓慢,还会极大增加搜寻良好解决方案的困难。这个问题就是我们常说的维数灾难。

图1 维数灾难

  维数灾难涉及数字分析、抽样、组合、机器学习、数据挖掘和数据库等诸多领域。在机器学习的建模过程中,通常指的是随着特征数量的增多,计算量会变得很大,如特征达到上亿维的话,在进行计算的时候是算不出来的。有的时候,维度太大也会导致机器学习性能的下降,并不是特征维度越大越好,模型的性能会随着特征的增加先上升后下降

(二)降维概述

1. 什么是降维?

降维(Dimensionality Reduction)是将训练数据中的样本(实例)从高维空间转换到低维空间,该过程与信息论中有损压缩概念密切相关。同时要明白的,不存在完全无损的降维。有很多种算法可以完成对原始数据的降维,在这些方法中,降维是通过对原始数据的线性变换实现的。

2. 为什么要降维

  • 高维数据增加了运算的难度。
  • 高维使得学习算法的泛化能力变弱(例如,在最近邻分类器中,样本复杂度随着维度成指数增长),维度越高,算法的搜索难度和成本就越大。
  • 降维能够增加数据的可读性,利于发掘数据的有意义的结构。

3. 降维的主要作用

(1)减少冗余特征,降低数据维度

  假设我们有两个特征:

x_1

:长度用厘米表示的身高;

x_2

:是用英寸表示的身高。这两个分开的特征

x_1

x_2

,实际上表示的内容相同,这样其实可以减少数据到一维,只有一个特征表示身高就够了。很多特征具有线性关系,具有线性关系的特征很多都是几余的特征,去掉冗余特征对机器学习的计算结果不会有影响。

(2)数据可视化

t-distributed Stochastic Neighbor Embedding(t-SNE)将数据点之间的相似度转换为概率。原始空间中的相似度由高斯联合概率表示,嵌入空间的相似度由“学生t分布”表示。虽然Isomap,LLE和variants等数据降维和可视化方法,更适合展开单个连续的低维的manifold。但如果要准确的可视化样本间的相似度关系,如对于下图所示的S曲线(不同颜色的图像表示不同类别的数据),t-SNE表现更好。因为t-SNE主要是关注数据的局部结构。

4. 降维的优缺点

降维的优点:

  • 通过减少特征的维数,数据集存储所需的空间也相应减少,减少了特征维数所需的计算训练时间;
  • 数据集特征的降维有助于快速可视化数据;
  • 通过处理多重共线性消除冗余特征。

降维的缺点:

  • 由于降维可能会丢失一些数据;
  • 在主成分分析(PCA)降维技术中,有时需要考虑多少主成分是难以确定的,往往使用经验法则。

二、主成分与方差

  顾名思义,PCA的含义是将高维数据中的主要成分找出来。设原始数据

\mathcal D=\{\boldsymbol x_1,\boldsymbol x_2,\cdots,\boldsymbol x_m\}

,其中

\boldsymbol x_i\in\mathbb R^d

。我们的目标是通过某种变换

f:\mathbb R^d\rightarrow\mathbb R^k

,将数据的维度从

d

减小到

k

,且通常

k\ll d

。变换后的数据不同维度之间线性无关,这些维度就称为主成分。也就是说,如果将变换后的数据排成矩阵

\boldsymbol Z=(\boldsymbol z_1,\cdots,\boldsymbol z_m)^{\mathrm T}

,其中

\boldsymbol z_i=f(\boldsymbol x_i)

,那么

\boldsymbol Z

的列向量是线性无关的。从矩阵的知识可以立即得到

k<m

  PCA算法不仅希望变换后的数据特征线性无关,更要求这些特征之间相互独立,也即任意两个不同特征之间的协方差为零。因此,PCA算法在计算数据的主成分时,会从第一个主成分开始依次计算,并保证每个主成分与之前的所有主成分都是正交的,直到选取了预先设定的

k

个主成分为止。关于主成分选取的规则,我们以一个平面上的二元高斯分布为例来具体说明。如图2(a)所示,数据点服从以

(3,3)

为中心、

x_1

方向方差为1.5、

x_2

方向方差为0.5、协方差为0.8的二元高斯分布。从图中可以明显看出,当前的

x_1

x_2

两个特征之间存在关联,并不是独立的。

图2 一个二元高斯分布和两个维度的投影情况

  图2(b)展示了数据分别向

x_1

x_2

方向投影的结果。由于

x_1

方向方差更大,数据在该方向的投影分布也更广。也就是说,数据向

x_1

方向的投影保留了更多信息,

x_1

是一个比

x_2

更好的特征。如果我们不再把目光局限于

x_1

x_2

两个方向,而是考虑平面上的任意一个方向,那么如左图中红色虚线所示,沿直线

x_2=0.557x_1+1.330

的方向数据的方差是最大的,我们应当将此方向作为数据的特征之一。既然PCA算法希望选出的主成分保留最多的信息,那么就应当不断选取数据方差最大的方向作为主成分。因此,PCA计算的过程就是依次寻找方差最大方向的过程。

  为了方便计算,我们通常要先对数据进行中心化,把当前每个特征都变为均值为0的分布。用

x_i^{(j)}

表示数据

\boldsymbol x_i

的第

j

个维度,那么均值

\mu_j

\mu_j=\frac{1}{m}\sum_{i=1}^mx_i^{(j)}, \quad j=1,\cdots,d

将数据中心化,得到

x_i^{(j)}\leftarrow x_i^{(j)}-\mu_j, \quad i=1,\cdots,m

  以图2(a)的高斯分布为例,变换后的图像如图3(b)所示,其中心点已经从

(3,3)

变为了

(0,0)

。接下来在讨论时,我们默认数据都已经被中心化。下面,我们就来具体讲解如何寻找数据中方差最大的方向。

图3 数据的中心化

三、利用特征分解进行PCA

  为了找到方差最大的方向,我们先来计算样本在某个方向上的投影。设

\boldsymbol u

为方向向量,满足

\Vert\boldsymbol u\Vert=1

,向量

\boldsymbol x

在方向

\boldsymbol u

上的投影为

\boldsymbol x^{\mathrm T}\boldsymbol u

。于是所有样本在方向

\boldsymbol u

上的方差为

\begin{aligned} \sigma_{\boldsymbol u} &= \frac{1}{m}\sum_{i=1}^m(\boldsymbol x_i^{\mathrm T}\boldsymbol u)^2 \\ &=\frac{1}{m}\sum_{i=1}^m\boldsymbol u^{\mathrm T}\boldsymbol x_i\boldsymbol x_i^{\mathrm T}\boldsymbol u \\ &= \boldsymbol u^{\mathrm T}\left(\frac{1}{m}\sum_{i=1}^m\boldsymbol x_i\boldsymbol x_i^{\mathrm T}\right)\boldsymbol u \\ &= \boldsymbol u^{\mathrm T}\boldsymbol\Sigma\boldsymbol u \end{aligned}

  矩阵

\boldsymbol\Sigma=\frac{1}{m}\sum\limits_{i=1}^m\boldsymbol x_i\boldsymbol x_i^{\mathrm T} \in \mathbb R^{d\times d}

称为样本的协方差矩阵。由于

m

是常数,简单起见,下面省略因子

\frac{1}{m}

。要令方差最大,就相当于求解下面的优化问题

\max_{\boldsymbol u}\boldsymbol u^{\mathrm T}\boldsymbol\Sigma\boldsymbol u \quad \text{s.t.}\Vert\boldsymbol u\Vert=1

  在求解该问题之前,我们先来介绍矩阵的特征值(eigenvalue)与特征分解(eigendecomposition)相关的知识。对于方阵

\boldsymbol A\in\mathbb R^{n\times n}

,如果向量

\boldsymbol\xi\in\mathbb R^n

与实数

\lambda

满足

\boldsymbol A\boldsymbol\xi=\lambda\boldsymbol\xi

,就称

\lambda

是矩阵

\boldsymbol A

的特征值,

\boldsymbol\xi

为矩阵的特征向量。特征向量

\boldsymbol x

的任意非零实数倍

r\boldsymbol x

满足

\boldsymbol A(r\boldsymbol x)=r(\boldsymbol A\boldsymbol x)=r(\lambda\boldsymbol x)=\lambda(r\boldsymbol x)

因而

r\boldsymbol x

也还是特征向量。简单来说,矩阵

\boldsymbol A

作用到其特征向量

\boldsymbol x

上的结果等价于对该向量做伸缩变换,伸缩的倍数等于特征值

\lambda

,但不改变向量所在的直线。从这个角度来看,

r\boldsymbol x

\boldsymbol x

共线,

r\boldsymbol x

自然就是特征向量。因此,我们通常更关心单位特征向量,即长度为1的特征向量。例如,矩阵

\boldsymbol A=\begin{pmatrix}2 & 1 \\ 0 &1\\ \end{pmatrix}

的特征值及其对应的单位特征向量有两组,分别为

\lambda_1=1

\xi_1=(-1,1)^{\mathrm T}

\lambda_2=2

\xi_2=(1,0)^{\mathrm T}

,大家可以自行计算验证。显然,对角矩阵

\text{diag}(d_1,\cdots,d_n)

的特征值就等于其对角线上的元素

d_1,\cdots,d_n

。特别地,

n

阶单位阵

\boldsymbol I_n

的特征值是1,且空间中的所有向量都是该特征值对应的特征向量。

  我们可以从线性变换的角度来理解矩阵的特征值和特征向量。一个

n

阶方阵

\boldsymbol A

可以看作是

n

维空间中的变换,将

n

维向量

\boldsymbol x

变为另一个

n

维向量

\boldsymbol A\boldsymbol x

。由于

\boldsymbol A(\boldsymbol x_1+\boldsymbol x_2)=\boldsymbol A\boldsymbol x_1+\boldsymbol A\boldsymbol x_2

以及

\boldsymbol A(r\boldsymbol x_1)=r\boldsymbol A\boldsymbol x_1

对任意向量

\boldsymbol x_1

\boldsymbol x_2

和实数

r

成立,该变换是一个线性变换。事实上,当坐标轴确定之后,线性变换与矩阵之间有一一对应关系。如果把向量都看作从原点指向向量所表示坐标的有向线段,可以证明,任意一个线性变换总可以分解成绕原点旋转和长度伸缩的组合。因此,矩阵与向量相乘

\boldsymbol A\boldsymbol x

可以理解为将向量

\boldsymbol x

旋转某一角度,再将长度变为某一倍数。而矩阵的特征向量,就是在该变换作用下只伸缩不旋转的向量,并且其对应的特征值就是伸缩的倍数。当特征值

\lambda>0

时,变换后的向量与原向量方向相同;

\lambda<0

时方向相反;

\lambda=0

则表示矩阵会把所有该直线上的向量压缩为零向量。

  从这一角度继续,我们可以引入正定矩阵(positive definite)和半正定矩阵(positive semidefinite)的概念。如果对任意非零向量

\boldsymbol x

,都有

\boldsymbol x^{\mathrm T}\boldsymbol A\boldsymbol x\ge0

,就称

\boldsymbol A

为半正定矩阵;如果严格有

\boldsymbol x^{\mathrm T}\boldsymbol A\boldsymbol x>0

,就称

\boldsymbol A

为正定矩阵。如果将

\boldsymbol y=\boldsymbol A\boldsymbol x

看作变换后的向量,那么

\boldsymbol x^{\mathrm T}y\ge0

就表示

\boldsymbol x

\boldsymbol y

之间的夹角小于等于90°。由此,我们可以立即得到半正定矩阵的所有特征值都非负,否则负特征值会使特征向量在变换后反向,与原向量夹角为180°,产生矛盾。

  为什么我们要引入半正定矩阵呢?在上面的优化问题中,我们得到的目标函数是

\boldsymbol u^{\mathrm T}\boldsymbol\Sigma\boldsymbol u

,其中

\boldsymbol\Sigma

是协方差矩阵。事实上,该矩阵一定是半正定矩阵。设

\boldsymbol y

是任一非零向量,那么

\boldsymbol y^{\mathrm T}\boldsymbol\Sigma\boldsymbol y=\boldsymbol y^{\mathrm T}\left(\sum_{i=1}^m\boldsymbol x_i\boldsymbol x_i^{\mathrm T}\right)\boldsymbol y=\sum_{i=1}^m\boldsymbol y^{\mathrm T}\boldsymbol x_i\boldsymbol x_i^{\mathrm T}\boldsymbol y=\sum_{i=1}^m(\boldsymbol x_i^{\mathrm T}\boldsymbol y)^2 \ge 0

  根据定义,

\boldsymbol\Sigma

是半正定矩阵。因此,我们可以用半正定矩阵的一些性质来帮助我们求解该优化问题。这里,我们不加证明地给出一条重要性质:对于

d

阶对称半正定矩阵

\boldsymbol\Sigma

,总可以找到它的

d

个单位特征向量

\boldsymbol e_1,\cdots,\boldsymbol e_d

,使得这些特征向量是两两正交的,也即对任意

1\le i

j\le d

,都有

\Vert\boldsymbol e_i\Vert=1, \quad \boldsymbol e_i^{\mathrm T}\boldsymbol e_j=\begin{cases}1, \quad i=j \\ 0, \quad i \ne j\end{cases}

有兴趣的可以在线性代数的相关资料中找到该性质的证明。利用该性质,记

\boldsymbol Q=(\boldsymbol e_1,\cdots,\boldsymbol e_d)\in\mathbb R^{d\times d}

,有

(\boldsymbol Q^{\mathrm T}\boldsymbol Q)_{ij}=\boldsymbol e_i^{\mathrm T}\boldsymbol e_j=\mathbb I(i=j)

于是,

\boldsymbol Q^{\mathrm T}\boldsymbol Q

对角线上的元素全部为1,其他元素全部为0,恰好是单位矩阵

\boldsymbol I_d

。因此,

\boldsymbol Q

的逆矩阵就是其转置,

\boldsymbol Q^{-1}=\boldsymbol Q^{\mathrm T}

。因为组成该矩阵的向量是相互正交的,我们将这样的矩阵称为正交矩阵(orthogonal matrix)。根据逆矩阵的性质,我们有

\boldsymbol I_d=\boldsymbol Q^{\mathrm T}\boldsymbol Q=\boldsymbol Q\boldsymbol Q^{\mathrm T}=(\boldsymbol e_1,\cdots,\boldsymbol e_d)\left(\begin{matrix}\boldsymbol e_1^{\mathrm T} \\ \vdots \\ \boldsymbol e_d^{\mathrm T}\end{matrix}\right)=\sum_{i=1}^d\boldsymbol e_i\boldsymbol e_i^{\mathrm T}

设特征向量

\boldsymbol e_i

对应的特征值是

\lambda_i

,那么矩阵

\boldsymbol\Sigma

可以写为

\begin{aligned} \boldsymbol\Sigma &= \boldsymbol\Sigma\boldsymbol I_d = \boldsymbol\Sigma\sum_{i=1}^d\boldsymbol e_i\boldsymbol e_i^{\mathrm T} = \sum_{i=1}^d(\boldsymbol\Sigma\boldsymbol e_i)\boldsymbol e_i^{\mathrm T} = \sum_{i=1}^d\lambda_i\boldsymbol e_i\boldsymbol e_i^{\mathrm T} \\[3ex] &= (\boldsymbol e_1,\boldsymbol e_2,\cdots,\boldsymbol e_d)\begin{pmatrix}\lambda_1 & 0 &\cdots & 0 \\ 0 & \lambda_2 & \cdots & 0 \\ \vdots & \vdots & & \vdots \\ 0 & 0 & \cdots & \lambda_d\end{pmatrix}\begin{pmatrix}\boldsymbol e_1^{\mathrm T} \\ \boldsymbol e_2^{\mathrm T} \\ \vdots \\ \boldsymbol e_d^{\mathrm T}\end{pmatrix} \\[2ex] &= \boldsymbol Q\boldsymbol\Lambda\boldsymbol Q^{\mathrm T} \end{aligned}

其中,

\boldsymbol\Lambda=\text{diag}(\lambda_1,\cdots,\lambda_d)

是由特征向量所对应的特征值依次排列而成的对角矩阵。上式表明,一个半正定矩阵可以分解成3个矩阵的乘积,其中

\boldsymbol Q

是其正交的特征向量构成的正交矩阵,

\boldsymbol\Lambda

是其特征值构成的对角矩阵,这样的分解就称为矩阵的特征分解。

  利用特征分解,我们可以很容易地计算

\boldsymbol u^{\mathrm T}\boldsymbol\Sigma\boldsymbol u

的值。首先,由于

\boldsymbol e_1,\cdots,\boldsymbol e_d

是维空间中的个正交向量,

\boldsymbol u

一定可以唯一表示成这些向量的线性组合,即

\boldsymbol u=\sum\limits_{i=1}^d\alpha_i\boldsymbol e_i

,其中系数

\alpha_i

等于向量

\boldsymbol u

\boldsymbol e_i

方向上的投影长度。我们可以将这组向量想象成相互垂直的坐标轴,而

(\alpha_1,\cdots,\alpha_d)

就相当于

\boldsymbol u

在这组坐标轴下的坐标。这样,

\boldsymbol Q^{\mathrm T}\boldsymbol u

可以化为

\begin{aligned} \boldsymbol Q^{\mathrm T}\boldsymbol u=\boldsymbol Q^{\mathrm T}\sum_{i=1}^d\alpha_i\boldsymbol e_i = \begin{pmatrix}\sum\limits_{i=1}^d\alpha_i\boldsymbol e_1^{\mathrm T}\boldsymbol e_i \\ \vdots \\ \sum\limits_{i=1}^d\alpha_i\boldsymbol e_d^{\mathrm T}\boldsymbol e_i\end{pmatrix} = (\alpha_1,\cdots,\alpha_d)=\boldsymbol\alpha \end{aligned}

这里,我们用到了

\boldsymbol e_i^{\mathrm T}\boldsymbol e_j=\mathbb I(i=j)

的性质,把求和中

\boldsymbol e_i^{\mathrm T}\boldsymbol e_j

都消去了。接下来,

\boldsymbol u^{\mathrm T}\boldsymbol\Sigma\boldsymbol u

可以计算得:

\boldsymbol u^{\mathrm T}\boldsymbol u=\boldsymbol u^{\mathrm T}\boldsymbol Q\boldsymbol\Lambda\boldsymbol Q^{\mathrm T}\boldsymbol u=\boldsymbol\alpha^{\mathrm T}\boldsymbol\Lambda\boldsymbol\alpha=\sum_{i=1}^d\lambda_i\alpha_i^2

  在上面的优化问题

\max\limits_{\boldsymbol u}\boldsymbol u^{\mathrm T}\boldsymbol\Sigma\boldsymbol u \ \ \text{s.t.}\Vert\boldsymbol u\Vert=1

中,我们还要求

\boldsymbol u

是方向向量,即

\Vert\boldsymbol u\Vert=1

。这一要求给系数

\alpha_i

添加了限定条件:

\begin{aligned} \sum_{i=1}^d\alpha_i^2 &= \sum_{i=1}^d(\boldsymbol u^{\mathrm T}\boldsymbol e_i)^2=\sum_{i=1}^d\boldsymbol u^{\mathrm T}\boldsymbol e_i\boldsymbol e_i^{\mathrm T}\boldsymbol u \\ &= \boldsymbol u^{\mathrm T}\left(\sum_{i=1}^d\boldsymbol e_i\boldsymbol e_i^{\mathrm T}\right)\boldsymbol u \\ &= \boldsymbol u^{\mathrm T}\boldsymbol I_d\boldsymbol u \\ &= \boldsymbol u^{\mathrm T}\boldsymbol u = \Vert\boldsymbol u\Vert^2=1 \end{aligned}

因此,原优化问题等价于

\max_{\boldsymbol u}\sum_{i=1}^d\lambda_i\alpha_i^2, \quad \text{s.t.} \quad \sum_{i=1}^d\alpha_i^2=1

  由于

\boldsymbol\Sigma

是半正定矩阵,其特征值

\lambda_i

必定非负,该问题的解就是

\boldsymbol\Sigma

的最大特征值

\lambda_{\max}

,不妨设其为

\lambda_u

。为了使上式取到最大值,应当有

\alpha_{\boldsymbol u}=\boldsymbol u^{\mathrm T}\boldsymbol e_{\boldsymbol u}=1

,且其他的

\alpha_i

全部为零。因此,使方差最大的方向就是该特征值

\lambda_{\boldsymbol u}

对应的特征向量

\boldsymbol e_{\boldsymbol u}

的方向。

  上面的计算结果表面,用PCA寻找第一个主成分的过程就是求解PCA最大特征值及其对应特征向量的过程。事实上,由于协方差矩阵

\boldsymbol\Sigma

半正定,其所有特征向量正交,恰好也满足我们最开始“每个主成分都与之前的所有主成分正交”的要求。如果排除掉第一主成分,第二主成分就对应

\boldsymbol\Sigma

第二大特征值的特征向量,依次类推。因此,如果我们要把

d

维的数据降到

k

维,只需要计算

\boldsymbol\Sigma

最大的

k

个特征值对应的特征向量即可。设这

k

个特征向量依次为

\boldsymbol e_1,\cdots,\boldsymbol e_k

,矩阵

\boldsymbol W=(\boldsymbol e_1,\cdots,\boldsymbol e_k)\in\mathbb R^{d\times d}

,原数据矩阵

\boldsymbol X=(\boldsymbol x_1\cdots,\boldsymbol x_m)^{\mathrm T}\in\mathbb R^{d\times d}

,那么降维后的数据为

\text{PCA}(\boldsymbol X)=\boldsymbol X\boldsymbol W

四、动手实现PCA算法

  下面,我们在NumPy库中线性代数工具的帮助下实现PCA算法。首先,我们导入数据集PCA_dataset.csv并将其可视化。数据集的每一行包含两个数

x_1

x_2

,代表平面上点的坐标

(x_1,x_2)

代码语言:javascript
代码运行次数:0
运行
复制
import numpy as np
import matplotlib.pyplot as plt

# 导入数据集
data = np.loadtxt('PCA_dataset.csv', delimiter=',')
print('数据集大小:', len(data))

# 可视化
plt.figure()
plt.scatter(data[:, 0], data[:, 1], color='blue', s=10)
plt.axis('square')
plt.ylim(-2, 8)
plt.grid()
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$')
plt.show()

  接下来,我们按上面讲解的方式实现PCA算法。numpy.linalg中提供了许多线性代数相关的工具,可以帮助我们计算矩阵的特征值与特征向量。

代码语言:javascript
代码运行次数:0
运行
复制
def pca(X, k):
    d, m = X.shape
    if d < k:
        print('k应该小于特征数')
        return X, None

    # 中心化
    X = X - np.mean(X, axis=0)
    # 计算协方差矩阵
    cov = X.T @ X
    # 计算特征值和特征向量
    eig_values, eig_vectors = np.linalg.eig(cov)
    # 获取最大的k个特征值的下标
    idx = np.argsort(-eig_values)[:k]
    # 对应的特征向量
    W = eig_vectors[:, idx]
    # 降维
    X = X @ W
    return X, W

  最后,我们在数据集上测试该PCA函数的效果,并将变换后的数据绘制出来。由于原始数据只有二维,为了演示PCA的效果,我们仍然设置

k=2

,不进行降维。但是,从结果中仍然可以看出PCA计算的主成分方向。相比于原始数据,变换后的数据最“长”的方向变成了横轴的方向。

代码语言:javascript
代码运行次数:0
运行
复制
X, W = pca(data, 2)
print('变换矩阵:\n', W)

# 绘图
plt.figure()
plt.scatter(X[:, 0], X[:, 1], color='blue', s=10)
plt.axis('square')
plt.ylim(-5, 5)
plt.grid()
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$')
plt.show()

五、用sklearn实现PCA算法

  Sklearn库中同样提供了实现好的PCA算法,我们可以直接调用它来完成PCA变换。可以看出,虽然结果图像与我们上面直接实现的版本有180°的旋转,变换矩阵的元素也互为相反数,但PCA本质上只需要找到主成分的方向,因此两者得到的结果是等价的。

使用scikit-learn库中decomposition模块的PCA类可以创建PCA模型,其基本语法格式和参数说明如下。

sklearn.decomposition.PCA(n_components=None, *, copy=True, whiten=False, svd_solver='auto', tol=0.0, iterated_power='auto', n_oversamples=10, power_iteration_normalizer='auto', random_state=None)

参数名称

参数说明

n_components

接收int或str。表示所要保留的主成分个数n,即保留下来的特征个数n,赋值为int时,表示降维的维度,如n_components=1,将把原始数据降到一个维度。赋值为str时,表示降维的模式,如取值为’mle’时,将自动选取特征个数n,使得满足所要求的方差百分比。默认为None。

copy

接收bool。表示是否在运行算法时,将原始训练数据复制一份。若为True,则运行后,原始训练数据的值不会有任何改变,因为是在原始数据的副本上进行运算;若为False,则运行后,原始训练数据的值会发生改变。默认为True。

whiten

接收bool。表示是否白化,使得每个特征具有相同的方差。默认为False。

svd_solver

接收{‘auto’, ‘full’, ‘arpack’, ‘randomized’},用于奇异值分解的算法。‘auto’会根据输入数据选择最佳的求解器。默认为’auto’。

tol

接收float。奇异值的容忍度。在使用’arpack’求解器时使用。默认为0.0。

iterated_power

接收int或’auto’。在使用’randomized’求解器时,幂法迭代的次数。默认为’auto’。

n_oversamples

接收int。随机SVD的过采样数量。默认值为10。

power_iteration_normalizer

接收{‘auto’, ‘LU’, ‘none’}。在幂迭代中使用的归一化方法。默认为’auto’。

random_state

接收int、RandomState实例或None。控制估计器的随机性。如果设置为固定整数,则可以确保结果的可重复性。默认为None。

代码语言:javascript
代码运行次数:0
运行
复制
from sklearn.decomposition import PCA

# 中心化
X = data - np.mean(data, axis=0)
pca_res = PCA(n_components=2).fit(X)
W = pca_res.components_.T
print ('sklearn计算的变换矩阵:\n', W)
X_pca = X @ W

# 绘图
plt.figure()
plt.scatter(X_pca[:, 0], X_pca[:, 1], color='blue', s=10)
plt.axis('square')
plt.ylim(-5, 5)
plt.grid()
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$')
plt.show()

六、拓展:奇异值分解

  本文介绍了数据降维的常用算法之一:PCA算法。数据降维是无监督学习的重要问题,在机器学习中有广泛的应用。由于从现实生活中采集的数据往往非常复杂,包含大量的冗余信息,通常我们必须对其进行降维,选出有用的特征供给后续模型使用。此外,有时我们还希望将高维数据可视化,也需要从数据中挑选2到3个最有价值的维度,将数据投影后绘制出来。除了PCA之外,现在常用的降维算法还有线性判别分析(linear discriminant analysis,LDA)、一致流形逼近与投影(uniform manifold approximation and projection,UMAP)、t分布随机近邻嵌入(t-distributed stochastic neighbor embedding,t-SNE)等。这些算法的特点各不相同,也有不同的适用场景。

  关于PCA的计算方式,由于计算协方差矩阵

\boldsymbol\Sigma=\boldsymbol X\boldsymbol X^{\mathrm T}

和特征分解的时间复杂度较高,实践中通常会采用矩阵的奇异值分解(singular value decomposition,SVD)来代替特征分解。上面我们用到的sklearn库中的PCA算法就是以奇异值分解为基础的实现的。相比于特征分解,奇异值分解不需要实际计算出

\boldsymbol\Sigma

,并且存在更高效的迭代求解方法。感兴趣的可以查阅相关资料,了解特征分解和奇异值分解的异同。

1. 奇异值分解的构造

2. 奇异值分解用于数据压缩

3. 奇异值分解的几何解释

4. 奇异值分解——SVD与PCA的关系

  实际上,如果将矩阵

\boldsymbol A_{m\times n}

看作是一个样本集合,其中的行看作特征随机变量,列看作每一个样本。当对数据集进行规范化后,矩阵

\boldsymbol A^{\mathrm T}\boldsymbol A

就是样本集合的协方差矩阵。这样,SVD分解后的右奇异矩阵就是PCA分析中的特征向量组成的矩阵。

  最后,大家可以在SETOSA网站的PCA算法动态展示平台上观察PCA的结果随数据分布的变化,加深对算法的理解。

:以上文中的数据集及相关资源下载地址: 链接:https://pan.quark.cn/s/15d427a80558 提取码:vYkb

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、降维
    • (一)维数灾难
    • (二)降维概述
  • 二、主成分与方差
  • 三、利用特征分解进行PCA
  • 四、动手实现PCA算法
  • 五、用sklearn实现PCA算法
  • 六、拓展:奇异值分解
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档