5.1 用主成分分析实现无监督降维
与特征选择类似,我们可以用不同的特征提取技术来减少数据集的特征数量。特征选择和特征提取的区别在于,当我们用诸如逆序选择之类的特征选择算法时,数据集的原始特征保持不变,而当我们用特征提取方法时,会将数据变换或投影到新特征空间。
在降维的背景下,我们可以把特征提取理解为数据压缩的一种方法,其目的是保持大部分的相关信息。在实际应用中,特征提取不仅可以优化存储空间或机器学习算法的计算效率,而且还可以通过减少维数诅咒提高预测性能,尤其是当我们处理非正则化模型的时候。
5.1.1 主成分分析的主要步骤
我们将在本书讨论主成分分析(PCA),这是一种无监督的线性变换技术,广泛应用于各种不同领域,特别是特征提取和降维。PCA的其他流行应用包括股票市场交易的探索性数据分析和去噪,以及生物信息学的基因组数据和基因表达水平分析。
PCA帮助我们根据特征之间的相关性来识别数据中的模式。简单地说,PCA旨在寻找高维数据中存在最大方差的方向,并将数据投影到维数小于或等于原始数据的新子空间。假设新特征轴彼此正交,该空间的正交轴(主成分)可以解释为方差最大的方向,如图5-1所示。其中x1和x2为原始特征轴,而PC1和PC2为主成分方向。
图 5-1
如果用PCA降维,我们可以构建d×k维的变换矩阵W,它能把训练样本的特征向量x映射到新的k维特征子空间,该空间的维数比原来的d维特征空间要少。例如,假设我们有一个特征向量x:
接着通过一个变换矩阵进行变换:
xW=z
结果以向量方式表达如下:
在原高维数据转换到k维新子空间(通常k≤d)的结果中,第一主成分的方差最大。假设这些成分与主成分之间互不相关(正交),那么所有的后续主成分都有最大的方差,即使输入特征相关,结果主成分也都相互正交(无关)。请注意,PCA的方向对数据尺度非常敏感,需要在进行PCA之前对特征进行标准化,如果以不同的尺度测量特征值,则需要确保所有特征的重要性保持均衡。
在深入讨论PCA降维算法之前,先用几个简单的步骤来概括该方法:
1)标准化d维数据集。
2)构建协方差矩阵。
3)将协方差矩阵分解为特征向量和特征值。
4)以降序对特征值排序,从而对相应的特征向量排序。
5)选择对应k个最大特征值的k个特征向量,其中k为新特征子空间的维数(k≤d)。
6)由前k个特征向量构造投影矩阵W。
7)用投影矩阵W变换d维输入数据集X以获得新的k维特征子空间。
下面我们先用Python逐步实现一个PCA的示例。然后再展示如何用scikit-learn更便捷地实现PCA。
5.1.2 逐步提取主成分
我们将在本小节讨论PCA的前四个步骤:
1)标准化数据集。
2)构建协方差矩阵。
3)获取协方差矩阵的特征值和特征向量。
4)以降序对特征值排序,从而对特征向量排序。
第1步,我们从加载在第4章中一直使用的葡萄酒数据集开始:
获取葡萄酒数据集
如果你是脱机工作或者UCI的服务器(https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data)暂时宕机的话,可以从本书的代码包中找到葡萄酒数据集(以及本书用到的其他全部数据集)。
要从本地目录加载葡萄酒数据集,可以把下面的命令:
换成:
然后,我们将按照7:3的比例,把葡萄酒数据分割成独立的训练数据集和测试数据集,并且标准化为单位方差:
在执行完前面的代码完成必要的预处理之后,我们会继续进行第2步,即构造协方差矩阵。d×d维对称协方差矩阵,其中d为数据集的维数,该矩阵成对地存储不同特征之间的协方差。例如特征xj和xk之间的整体协方差可以通过以下的方程计算:
其中μj和μk分别为特征j和k的样本均值。请注意,如果我们把数据集标准化,那么样本的均值将为零。两个特征之间的正协方差表示特征值在相同的方向上增加或减少,而负协方差则表示特征在相反方向上变化。例如,我们可以把三个特征的协方差矩阵写成(请注意∑是希腊字母sigma的大写形式,不要与求和符号混淆):
协方差矩阵的特征向量代表主成分(最大方差的方向),而相应的特征值将定义它们的大小。我们将从葡萄酒数据集的13×13维协方差矩阵中获得13个特征向量和特征值。
第3步,获得协方差矩阵的特征值和特征向量。正如我们在前面讲述线性代数时介绍过的,特征向量v满足以下条件:
∑ν=λν
这里λ是标量,即特征值。由于特征向量和特征值的手工计算很烦琐,所以我们将调用NumPy的linalg.eig
函数来获得葡萄酒数据集协方差矩阵的特征向量和特征值:
我们用numpy.cov
函数计算标准化的训练数据集的协方差矩阵。用linalg.eig
函数完成特征分解,从而产生包含13个特征值的向量(eigen_vals
),所对应的特征向量存储在13×13维矩阵(eigen_vecs
)的列中。
用Numpy进行特征分解
numpy.linalg.eig
函数可以处理对称和非对称方阵操作。但是你可能会发现,在某些情况下它会返回复特征值。
numpy.linalg.eigh
是一个相关函数,它可以分解埃尔米特(Hermetian)矩阵,从数值的角度来说,这是一个解决对称矩阵(例如协方差矩阵)的更稳定的方法,numpy.linalg.eigh
始终返回实特征值。
5.1.3 总方差和解释方差
因为我们想要通过将数据集压缩到新特征子空间来降低维数,所以只选择包含最多信息(方差)的特征向量(主成分)的子集。特征值代表特征向量的大小,通过对特征值的降序排列,我们可以找出前k个最重要的特征向量。但是,在收集k个信息最丰富的特征向量之前,我们先把特征值的方差解释比画出来。特征值λj的方差解释比就是特征值λj与特征值总和之比:
调用NumPy的cumsum
函数,我们可以计算出解释方差和,然后可以用Matplotlib的step
函数绘图:
结果表明,第一主成分本身占方差的40%左右。此外,我们还可以看到把前两个主成分结合起来可以解释数据集中几乎60%的方差,如图5-2所示。
图 5-2
虽然解释方差图让我们回想起第4章中通过随机森林计算的特征重要值,但是要注意,PCA是一种无监督学习方法,这意味着有关分类标签的信息会被忽略。随机森林用类成员信息计算节点的杂质度,方差测量值沿特征轴的传播。
5.1.4 特征变换
在成功地把协方差矩阵分解为特征对之后,我们现在接着完成最后的三个步骤(5~7),将葡萄酒数据集变换到新的主成分轴。其余的步骤如下:
5)选择与前k个最大特征值对应的k个特征向量,其中k为新特征子空间的维数(k≤d)。
6)用前k个特征向量构建投影矩阵W。
7)用投影矩阵W变换d维输入数据集X以获得新的k维特征子空间。
通俗地说,我们将把特征对按特征值降序排列,从所选的特征向量构建投影矩阵,用投影矩阵把数据变换到低维子空间。
从把特征对按特征值降序排列开始:
接着,我们收集对应前两个最大特征值的特征向量,从数据集中捕获大约60%的方差。请注意,我们在这里只选择两个特征向量来说明问题,因为本小节后面将在二维散点图中绘制数据。在实践中,主成分的数量必须通过计算效率和分类器性能之间的权衡来确定:
执行代码,依据前两个特征向量创建一个13×2维的投影矩阵W。
镜像投影
取决于你所用NumPy和LAPACK的具体版本,得到的矩阵W的正负号可能相反。请注意,这并不是个问题。如果v是矩阵∑的一个特征向量,那么我们有:
∑ν=λν
这里λ为特征向量,而-v也是一个特征向量,下面可以证明。用基本代数知识,我们可以在等式的两边乘以标量α:
α∑ν=αλν
因为矩阵乘法与标量乘法存在着关联性,所以我们可以得到下面的结果:
∑(αν)=λ(αν)
现在,我们可以看到αv是一个有相同特征值λ的特征向量,无论α=1还是α=-1,因此,v与-v都是特征向量。
现在,我们可以用投影矩阵将示例x(表示为13维的行向量)变换到PCA子空间(主成分1和2),从而获得x′,即由两个新特征组成的二维示例向量:
x′=xW
类似地,我们可以通过计算矩阵点积将整个124×13维训练数据集变换成两个主成分:
X′=XW
最后我们把目前存储为124×2维矩阵的葡萄酒训练数据集在二维散点图上完成可视化:
从结果图中我们可以看到,与第二主成分(y轴)相比,数据更多的是沿着x轴(第一主成分)传播,这与前面得出的解释方差比结论一致。然而,这里线性分类器就能够很好地区分不同的类别,如图5-3所示。
图 5-3
虽然在图5-3中我们对分类标签信息进行了编码,但是必须要记住,PCA是一种不使用任何分类标签信息的无监督学习技术。
5.1.5 用scikit-learn实现主成分分析
在前一小节中,我们的详细解释对掌握PCA的内部运作很有帮助。现在,我们将讨论如何使用scikit-learn的PCA
类。
PCA
是scikit-learn的另一个转换器类,我们首先用训练数据来拟合模型,然后用相同模型参数转换训练数据和测试数据。现在,让我们把scikit-learn中的PCA
类应用到葡萄酒训练数据集上,通过逻辑回归分类转换后的样本,调用plot_decision_regions
函数(在第2章定义)实现决策区域的可视化。
为了方便起见,可以将上面显示的plot_decision_regions
代码放入当前工作目录中的单独代码文件中,例如plot_decision_regions_script.py
,然后将其导入当前的Python会话中。
通过执行前面的代码,现在应该看到训练数据的决策区域减少为两个主成分轴,如图5-4所示。
图 5-4
比较scikit-learn实现的PCA与我们自己实现的PCA在预测方面的差异时,可能会发现两个结果图如同镜子里的图像,一正一反。注意,这并不是哪个实现出了差错,造成差异的原因在于特征求解器,有些特征向量可能有正负号的问题。
这并没有什么大惊小怪的,如果需要,可以把数据乘上-1来直接反转镜像。要注意的是,通常我们把特征向量调整为单位长度1。为完整起见,我们在转换后的测试数据集上绘制逻辑回归的决策区域,看它是否能很好地完成数据分类任务:
在测试数据集上执行上述代码绘出决策区域之后,我们可以看到逻辑回归在该二维特征子空间上的表现相当不错,在测试数据集中只存在很少的样本分类错误,如图5-5所示。
图 5-5
如果对不同主成分的解释方差比感兴趣,我们可以简单地把参数n_components
设置为None
来初始化PCA
类,这样就可以保留所有的主成分,然后通过调用explained_variance_ratio_
属性访问解释方差比:
请注意,当我们初始化PCA
类时,设置n_components=None
,系统将返回排过序的所有主成分而不是进行降维。