![Python机器学习(原书第3版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/531/38458531/b_38458531.jpg)
5.1 用主成分分析实现无监督降维
与特征选择类似,我们可以用不同的特征提取技术来减少数据集的特征数量。特征选择和特征提取的区别在于,当我们用诸如逆序选择之类的特征选择算法时,数据集的原始特征保持不变,而当我们用特征提取方法时,会将数据变换或投影到新特征空间。
在降维的背景下,我们可以把特征提取理解为数据压缩的一种方法,其目的是保持大部分的相关信息。在实际应用中,特征提取不仅可以优化存储空间或机器学习算法的计算效率,而且还可以通过减少维数诅咒提高预测性能,尤其是当我们处理非正则化模型的时候。
5.1.1 主成分分析的主要步骤
我们将在本书讨论主成分分析(PCA),这是一种无监督的线性变换技术,广泛应用于各种不同领域,特别是特征提取和降维。PCA的其他流行应用包括股票市场交易的探索性数据分析和去噪,以及生物信息学的基因组数据和基因表达水平分析。
PCA帮助我们根据特征之间的相关性来识别数据中的模式。简单地说,PCA旨在寻找高维数据中存在最大方差的方向,并将数据投影到维数小于或等于原始数据的新子空间。假设新特征轴彼此正交,该空间的正交轴(主成分)可以解释为方差最大的方向,如图5-1所示。其中x1和x2为原始特征轴,而PC1和PC2为主成分方向。
![107-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/107-01.jpg?sign=1738917234-0H5dsi0IDhquPJ2r0cir3kS8Bi1VjpA9-0-0272f6d94ccbbbbbfad6df38993db802)
图 5-1
如果用PCA降维,我们可以构建d×k维的变换矩阵W,它能把训练样本的特征向量x映射到新的k维特征子空间,该空间的维数比原来的d维特征空间要少。例如,假设我们有一个特征向量x:
![107-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/107-02.jpg?sign=1738917234-lpRgoKPBWNes2ouUQK0xakU4h2WoOoqB-0-4198be524c5db4520d26258e0c5d2c2c)
接着通过一个变换矩阵进行变换:
xW=z
结果以向量方式表达如下:
![108-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/108-01.jpg?sign=1738917234-f6qFyf6cfN8BbM8XJXDyQWAGzxwqsR3t-0-ed690e8b6e1fef0189967aed06f1f6d1)
在原高维数据转换到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章中一直使用的葡萄酒数据集开始:
![108-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/108-02.jpg?sign=1738917234-EgXIlT5jRzqSQaNbKtGcxxH64Ds5yaeR-0-a76ba5adda508982f848199b431c7f7d)
获取葡萄酒数据集
如果你是脱机工作或者UCI的服务器(https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data)暂时宕机的话,可以从本书的代码包中找到葡萄酒数据集(以及本书用到的其他全部数据集)。
要从本地目录加载葡萄酒数据集,可以把下面的命令:
![108-03](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/108-03.jpg?sign=1738917234-MatQrYoKP2aHuegMDYxFG0DvhnjCgTk9-0-0687c6cf72f25dacb966b4085e13cd4a)
换成:
![108-04](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/108-04.jpg?sign=1738917234-rrgMuEBSmWgud4nad3MfWYjNXbqdIfRQ-0-3978a39a59b2f36726a60e1fa381100e)
然后,我们将按照7:3的比例,把葡萄酒数据分割成独立的训练数据集和测试数据集,并且标准化为单位方差:
![109-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/109-01.jpg?sign=1738917234-FATziElMNHrbw0E6i6N8S8rABDjFWbay-0-b4587d801c8a73b4c667ed8f7c7a6c20)
在执行完前面的代码完成必要的预处理之后,我们会继续进行第2步,即构造协方差矩阵。d×d维对称协方差矩阵,其中d为数据集的维数,该矩阵成对地存储不同特征之间的协方差。例如特征xj和xk之间的整体协方差可以通过以下的方程计算:
![109-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/109-02.jpg?sign=1738917234-j1UDxAzWqjdRsk77xZRG2bsmBdyoKIZ1-0-43965cb268bd32de5a28a034173efe0f)
其中μj和μk分别为特征j和k的样本均值。请注意,如果我们把数据集标准化,那么样本的均值将为零。两个特征之间的正协方差表示特征值在相同的方向上增加或减少,而负协方差则表示特征在相反方向上变化。例如,我们可以把三个特征的协方差矩阵写成(请注意∑是希腊字母sigma的大写形式,不要与求和符号混淆):
![109-03](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/109-03.jpg?sign=1738917234-HkuXiprSINyXO4pXjFHnvIx4rMXa9no0-0-7337f8e6bfe1c00d1e2e22ae96a2fc72)
协方差矩阵的特征向量代表主成分(最大方差的方向),而相应的特征值将定义它们的大小。我们将从葡萄酒数据集的13×13维协方差矩阵中获得13个特征向量和特征值。
第3步,获得协方差矩阵的特征值和特征向量。正如我们在前面讲述线性代数时介绍过的,特征向量v满足以下条件:
∑ν=λν
这里λ是标量,即特征值。由于特征向量和特征值的手工计算很烦琐,所以我们将调用NumPy的linalg.eig
函数来获得葡萄酒数据集协方差矩阵的特征向量和特征值:
![109-04](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/109-04.jpg?sign=1738917234-bkT7qPTJymvzHhkOLW20L54RYAPnCdl0-0-a444b744f7c68376c24367976130bea5)
我们用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与特征值总和之比:
![110-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/110-01.jpg?sign=1738917234-wzceCLeCppYobPLXsTmlsWhQ9wIIKXM6-0-1c4e70c1dc26bcdc342b52753bebd392)
调用NumPy的cumsum
函数,我们可以计算出解释方差和,然后可以用Matplotlib的step
函数绘图:
![110-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/110-02.jpg?sign=1738917234-NBlWYr6NgXI2bdPsjn1qGGp8feZ6sikc-0-e2d92841dfe33af711d8ac62ca62fc15)
结果表明,第一主成分本身占方差的40%左右。此外,我们还可以看到把前两个主成分结合起来可以解释数据集中几乎60%的方差,如图5-2所示。
![110-03](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/110-03.jpg?sign=1738917234-Ezmk8e0wNj7ArkXBa3nOd2DDpQxsPmdX-0-d9cd62d048eb199c66f463e27cc920e0)
图 5-2
虽然解释方差图让我们回想起第4章中通过随机森林计算的特征重要值,但是要注意,PCA是一种无监督学习方法,这意味着有关分类标签的信息会被忽略。随机森林用类成员信息计算节点的杂质度,方差测量值沿特征轴的传播。
5.1.4 特征变换
在成功地把协方差矩阵分解为特征对之后,我们现在接着完成最后的三个步骤(5~7),将葡萄酒数据集变换到新的主成分轴。其余的步骤如下:
5)选择与前k个最大特征值对应的k个特征向量,其中k为新特征子空间的维数(k≤d)。
6)用前k个特征向量构建投影矩阵W。
7)用投影矩阵W变换d维输入数据集X以获得新的k维特征子空间。
通俗地说,我们将把特征对按特征值降序排列,从所选的特征向量构建投影矩阵,用投影矩阵把数据变换到低维子空间。
从把特征对按特征值降序排列开始:
![111-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/111-01.jpg?sign=1738917234-uM5M8Ch2YJes243gnbkDu16Wlq2MQ0mK-0-287ae53da4ff0489ecddf87881799bb7)
接着,我们收集对应前两个最大特征值的特征向量,从数据集中捕获大约60%的方差。请注意,我们在这里只选择两个特征向量来说明问题,因为本小节后面将在二维散点图中绘制数据。在实践中,主成分的数量必须通过计算效率和分类器性能之间的权衡来确定:
![111-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/111-02.jpg?sign=1738917234-JW4xtFhmTZ3rlPizCt9fes2gUPyYKfrt-0-f4f4f83e2472392e6a085c28575da577)
执行代码,依据前两个特征向量创建一个13×2维的投影矩阵W。
镜像投影
取决于你所用NumPy和LAPACK的具体版本,得到的矩阵W的正负号可能相反。请注意,这并不是个问题。如果v是矩阵∑的一个特征向量,那么我们有:
∑ν=λν
这里λ为特征向量,而-v也是一个特征向量,下面可以证明。用基本代数知识,我们可以在等式的两边乘以标量α:
α∑ν=αλν
因为矩阵乘法与标量乘法存在着关联性,所以我们可以得到下面的结果:
∑(αν)=λ(αν)
现在,我们可以看到αv是一个有相同特征值λ的特征向量,无论α=1还是α=-1,因此,v与-v都是特征向量。
现在,我们可以用投影矩阵将示例x(表示为13维的行向量)变换到PCA子空间(主成分1和2),从而获得x′,即由两个新特征组成的二维示例向量:
x′=xW
![112-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/112-01.jpg?sign=1738917234-Gtm6OsAauMmtqZdwKakiNe5Zsrbf2308-0-a15f790a9d2775e2f6d9730c8593f73d)
类似地,我们可以通过计算矩阵点积将整个124×13维训练数据集变换成两个主成分:
X′=XW
![112-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/112-02.jpg?sign=1738917234-DkQglBtGZ9zP1QmBGlPEEj4YN5Ka9tR7-0-d03ebd0292b7834683f11dd6d62bf87e)
最后我们把目前存储为124×2维矩阵的葡萄酒训练数据集在二维散点图上完成可视化:
![112-03](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/112-03.jpg?sign=1738917234-FLjlIfS1ZTFyDBgR9OUZGc4RdMKLa9lb-0-ab086251be56986e2d085a4b480b176e)
从结果图中我们可以看到,与第二主成分(y轴)相比,数据更多的是沿着x轴(第一主成分)传播,这与前面得出的解释方差比结论一致。然而,这里线性分类器就能够很好地区分不同的类别,如图5-3所示。
![112-04](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/112-04.jpg?sign=1738917234-yceufNupBobMhvaKb8BRy5qdbrjLZTjq-0-b56d3820a27ea33dfbfa12817fe40590)
图 5-3
虽然在图5-3中我们对分类标签信息进行了编码,但是必须要记住,PCA是一种不使用任何分类标签信息的无监督学习技术。
5.1.5 用scikit-learn实现主成分分析
在前一小节中,我们的详细解释对掌握PCA的内部运作很有帮助。现在,我们将讨论如何使用scikit-learn的PCA
类。
PCA
是scikit-learn的另一个转换器类,我们首先用训练数据来拟合模型,然后用相同模型参数转换训练数据和测试数据。现在,让我们把scikit-learn中的PCA
类应用到葡萄酒训练数据集上,通过逻辑回归分类转换后的样本,调用plot_decision_regions
函数(在第2章定义)实现决策区域的可视化。
![113-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/113-01.jpg?sign=1738917234-n4fGjfjK6kVRfD9eSnd9oTPfHaxi3qTd-0-565616d4ae8bf19736648df3e3b1c423)
为了方便起见,可以将上面显示的plot_decision_regions
代码放入当前工作目录中的单独代码文件中,例如plot_decision_regions_script.py
,然后将其导入当前的Python会话中。
![113-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/113-02.jpg?sign=1738917234-FFnUWhGKkbqQlCiTRNcGDpuxzUSeXgxF-0-13899d34d07340ce077fa8a609541b21)
![114-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/114-01.jpg?sign=1738917234-qYz0Qy7Lj4fAFAVX6vdHzqOOn2zPHcq9-0-134737c0f65ee45267e90efab4cc7776)
通过执行前面的代码,现在应该看到训练数据的决策区域减少为两个主成分轴,如图5-4所示。
![114-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/114-02.jpg?sign=1738917234-pZwY7A3t5aLo8cGRu11Sicr5r9E0E9CS-0-9382693f41432474dec9a75377dd7fca)
图 5-4
比较scikit-learn实现的PCA与我们自己实现的PCA在预测方面的差异时,可能会发现两个结果图如同镜子里的图像,一正一反。注意,这并不是哪个实现出了差错,造成差异的原因在于特征求解器,有些特征向量可能有正负号的问题。
这并没有什么大惊小怪的,如果需要,可以把数据乘上-1来直接反转镜像。要注意的是,通常我们把特征向量调整为单位长度1。为完整起见,我们在转换后的测试数据集上绘制逻辑回归的决策区域,看它是否能很好地完成数据分类任务:
![114-03](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/114-03.jpg?sign=1738917234-yKH1j6YpjUtdXoHtdNbtoRFd0oRxcZ6B-0-6f8d13fd2ffa8adb586607f66ad1105b)
在测试数据集上执行上述代码绘出决策区域之后,我们可以看到逻辑回归在该二维特征子空间上的表现相当不错,在测试数据集中只存在很少的样本分类错误,如图5-5所示。
![115-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/115-01.jpg?sign=1738917234-7eIW9yTjY3D5BTH8vL2w7TQ6JYYPMGjv-0-e1ae73d3896f1333430edc6f810ea4bb)
图 5-5
如果对不同主成分的解释方差比感兴趣,我们可以简单地把参数n_components
设置为None
来初始化PCA
类,这样就可以保留所有的主成分,然后通过调用explained_variance_ratio_
属性访问解释方差比:
![115-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/115-02.jpg?sign=1738917234-NJqpxBNI2oV9K1s6Cvkfca93kJXmxadU-0-a46137725e7f7632ef8d96c8430b540f)
请注意,当我们初始化PCA
类时,设置n_components=None
,系统将返回排过序的所有主成分而不是进行降维。