降维、特征提取与流形学习
前面讨论过,利用无监督学习进行数据变换可能有很多种目的。最常见的目的就是可视化、压缩数据,以及寻找信息量更大的数据表示以用于进一步的处理。
为了实现这些目的,最简单也最常用的一种算法就是主成分分析。我们也将学习另外两种算法:非负矩阵分解(NMF
)和 t-SNE
,前者通常用于特征提取,后者通常用于二维散点图的可视化。
1、主成分分析(PCA)
主成分分析(principal component analysis
,PCA
)是一种旋转数据集的方法,旋转后的特征在统计上不相关。在做完这种旋转之后,通常是根据新特征对解释数据的重要性来选择它的一个子集。下面的例子展示了 PCA
对一个模拟二维数据集的作用:
mglearn.plots.plot_pca_illustration()
第一张图(左上)显示的是原始数据点,用不同颜色加以区分。算法首先找到方差最大的方向,将其标记为 “成分 1”(Component 1
)。这是数据中包含最多信息的方向(或向量),换句话说,沿着这个方向的特征之间最为相关。然后,算法找到与第一个方向正交(成直角)且包含最多信息的方向。在二维空间中,只有一个成直角的方向,但在更高维的空间中会有(无穷)多的正交方向。虽然这两个成分都画成箭头,但其头尾的位置并不重要。我们也可以将第一个成分画成从中心指向左上,而不是指向右下。利用这一过程找到的方向被称为主成分(principal component
),因为它们是数据方差的主要方向。一般来说,主成分的个数与原始特征相同。
第二张图(右上)显示的是同样的数据,但现在将其旋转,使得第一主成分与 x 轴平行且第二主成分与 y 轴平行。在旋转之前,从数据中减去平均值,使得变换后的数据以零为中心。在 PCA 找到的旋转表示中,两个坐标轴是不相关的,也就是说,对于这种数据表示,除了对角线,相关矩阵全部为零。
我们可以通过仅保留一部分主成分来使用 PCA 进行降维。在这个例子中,我们可以仅保留第一个主成分,正如上图中第三张图所示(左下)。这将数据从二维数据集降为一维数据集。但要注意,我们没有保留原始特征之一,而是找到了最有趣的方向(第一张图中从左上到右下)并保留这一方向,即第一主成分。
最后,我们可以反向旋转并将平均值重新加到数据中。这样会得到上图最后一张图中的数据。这些数据点位于原始特征空间中,但我们仅保留了第一主成分中包含的信息。这种变换有时用于去除数据中的噪声影响,或者将主成分中保留的那部分信息可视化。
1.1、将 PCA 应用于 cancer 数据集并可视化
PCA 最常见的应用之一就是将高维数据集可视化。对于有两个以上特征的数据,很难绘制散点图。对于 Iris
(鸢尾花)数据集,我们可以创建散点图矩阵,通过展示特征所有可能的两两组合来展示数据的局部图像。但如果我们想要查看乳腺癌数据集,即便用散点图矩阵也很困难。这个数据集包含 30 个特征,这就导致需要绘制 30 * 14 = 420 张散点图!我们永远不可能仔细观察所有这些图像,更不用说试图理解它们了。
不过我们可以使用一种更简单的可视化方法——对每个特征分别计算两个类别(良性肿瘤和恶性肿瘤)的直方图。但是,这种图无法向我们展示变量之间的相互作用以及这种相互作用与类别之间的关系。利用 PCA,我们可以获取到主要的相互作用,并得到稍为完整的图像。我们可以找到前两个主成分,并在这个新的二维空间中用散点图将数据可视化。
在应用 PCA 之前,我们利用 StandardScaler
缩放数据,使每个特征的方差均为 1:
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
scaler = StandardScaler()
scaler.fit(cancer.data)
X_scaled = scaler.transform(cancer.data)
学习并应用 PCA
变换与应用预处理变换一样简单。我们将 PCA 对象实例化,调用 fit
方法找到主成分,然后调用 transform
来旋转并降维。默认情况下,PCA
仅旋转(并移动)数据,但保留所有的主成分。为了降低数据的维度,我们需要在创建 PCA
对象时指定想要保留的主成分个数:
from sklearn.decomposition import PCA
# 保留数据的前两个主成分
pca = PCA(n_components=2)
# 对乳腺癌数据拟合PCA模型
pca.fit(X_scaled)
# 将数据变换到前两个主成分的方向上
X_pca = pca.transform(X_scaled)
print("Original shape: {}".format(str(X_scaled.shape)))
# Original shape: (569, 30)
print("Reduced shape: {}".format(str(X_pca.shape)))
# Reduced shape: (569, 2)
现在我们可以对前两个主成分作图:
# 对第一个和第二个主成分作图,按类别着色
plt.figure(figsize=(8, 8))
mglearn.discrete_scatter(X_pca[:, 0], X_pca[:, 1], cancer.target)
plt.legend(cancer.target_names, loc="best")
plt.gca().set_aspect("equal")
plt.xlabel("First principal component")
plt.ylabel("Second principal component")
重要的是要注意,PCA
是一种无监督方法,在寻找旋转方向时没有用到任何类别信息。它只是观察数据中的相关性。对于这里所示的散点图,我们绘制了第一主成分与第二主成分的关系,然后利用类别信息对数据点进行着色。你可以看到,在这个二维空间中两个类别被很好地分离。这让我们相信,即使是线性分类器(在这个空间中学习一条直线)也可以在区分这个两个类别时表现得相当不错。我们还可以看到,恶性点比良性点更加分散。
PCA
的一个缺点在于,通常不容易对图中的两个轴做出解释。主成分对应于原始数据中的方向,所以它们是原始特征的组合。但这些组合往往非常复杂,这一点我们很快就会看到。在拟合过程中,主成分被保存在 PCA
对象的 components_
属性中:
print("PCA component shape: {}".format(pca.components_.shape))
# PCA component shape: (2, 30)
components_
中的每一行对应于一个主成分,它们按重要性排序(第一主成分排在首位,以此类推)。列对应于 PCA 的原始特征属性,在本例中即为 “mean radius”、“mean texture” 等。我们来看一下 components_
的内容:
print("PCA components:\n{}".format(pca.components_))
'''
PCA components:
[[ 0.21890244 0.10372458 0.22753729 0.22099499 0.14258969 0.23928535
0.25840048 0.26085376 0.13816696 0.06436335 0.20597878 0.01742803
0.21132592 0.20286964 0.01453145 0.17039345 0.15358979 0.1834174
0.04249842 0.10256832 0.22799663 0.10446933 0.23663968 0.22487053
0.12795256 0.21009588 0.22876753 0.25088597 0.12290456 0.13178394]
[-0.23385713 -0.05970609 -0.21518136 -0.23107671 0.18611302 0.15189161
0.06016536 -0.0347675 0.19034877 0.36657547 -0.10555215 0.08997968
-0.08945723 -0.15229263 0.20443045 0.2327159 0.19720728 0.13032156
0.183848 0.28009203 -0.21986638 -0.0454673 -0.19987843 -0.21935186
0.17230435 0.14359317 0.09796411 -0.00825724 0.14188335 0.27533947]]
'''
我们还可以用热图将系数可视化,这可能更容易理解:
plt.matshow(pca.components_, cmap='viridis')
plt.yticks([0, 1], ["First component", "Second component"])
plt.colorbar()
plt.xticks(range(len(cancer.feature_names)),
cancer.feature_names, rotation=60, ha='left')
plt.xlabel("Feature")
plt.ylabel("Principal components")
你可以看到,在第一个主成分中,所有特征的符号相同(均为正,但前面我们提到过,箭头指向哪个方向无关紧要)。这意味着在所有特征之间存在普遍的相关性。如果一个测量值较大的话,其他的测量值可能也较大。第二个主成分的符号有正有负,而且两个主成分都包含所有 30 个特征。这种所有特征的混合使得解释图 3-6 中的坐标轴变得十分困难。
1.2、特征提取的特征脸
前面提到过,PCA
的另一个应用是特征提取。特征提取背后的思想是,可以找到一种数据表示,比给定的原始表示更适合于分析。特征提取很有用,它的一个很好的应用实例就是图像。图像由像素组成,通常存储为红绿蓝(RGB
)强度。图像中的对象通常由上千个像素组成,它们只有放在一起才有意义。
我们将给出用 PCA
对图像做特征提取的一个简单应用,即处理 Wild
数据集 Labeled Faces
(标记人脸)中的人脸图像。这一数据集包含从互联网下载的名人脸部图像,它包含从 21 世纪初开始的政治家、歌手、演员和运动员的人脸图像。我们使用这些图像的灰度版本,并将它们按比例缩小以加快处理速度。你可以在下图中看到其中一些图像:
from sklearn.datasets import fetch_lfw_people
people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[