机器学习DAY6:主成分分析和PCA降维(完)

发布于:2025-02-11 ⋅ 阅读:(78) ⋅ 点赞:(0)

前言:

无监督学习是机器学习中的一类重要算法。与监督学习算法相比,无监督学习算法不需要对输入数据进行标记,即不需要给出标签或类别。此外,无监督学习算法还可以在没有辅助的情况下能够学习数据的内在关系。

由于数据不需要手动标记标签,这可以使许多无监督算法能够处理大量的数据,从而节省了大量的人力成本。但是,这也给其带来一个问题,因为无监督学习算法在学习时没有使用标签数据,所以一般情况下很难直观地去评估无监督学习算法的质量。

知识点
  • PCA 降维

主成分分析 PCA 

在无监督学习中最常见的任务之一是降维,也就是减少输入数据的维数。为什么要降维呢?主要有一下几个原因:首先,降维可能有助于数据可视化,因为人是无法理解高维数据的,通常只能看明白二维或三维数据的可视化图像。其次,降维可以有效的解决维度灾难的问题,改善模型的训练效果。此外,降维还可以进行数据压缩,提升模型训练效率。 

目前,主成分分析是最简单,最直观,最常用的降维方法之一,其工作原理主要是将的数据投影到一个正交特征子空间中。

                                        

为了更好的理解 PCA 的工作原理,一起来看上面这幅图。试想一下,如果将坐标系中的数据点投影到 x 轴(投影过后的数据即为 x 轴上的一堆数据点),或者以同样的方法投影到 y 轴,哪一个更容易区分呢? 

很显然是 x 轴。因为数据投影到 x 轴之后相比于 y 轴更分散,也就是数据样本的方差更大,从而也更容易区分。回到上图,如果要在坐标系中找到一根轴,使得投影到这根轴后的数据的方差更大,也就是说更分散,哪一根轴最好呢?显然,就是图中所标的椭圆轴方向。 

PCA 的思想就是这样。如果将图中的数据都投影到椭圆轴上,则数据也就从二维降为了一维。 

来看看这个过程的数学公式: 

为了将数据的维数从 n 维降到 k 维,我们按照散度(也就是数据的分散程度)降低的顺序对轴列表进行排序,然后取出前 k 项。 

现在开始计算原始数据 n 维的散度值和协方差。根据协方差矩阵的定义,两个特征列的协方差矩阵计算公式如下: 

上式中的 μi​ 表示第 i 个特征的期望值。这里需要注意的是,协方差都是对称的,而且向量与其自身的协方差就等于其散度。(这里说的“散度”一般指的是“方差(variance)”。即cov(Xi,Xi)=var(Xi))

当协方差为正时,说明 X 和 Y 是正相关关系;协方差为负时,说明 X 和 Y 是负相关关系;协方差为 0 时,说明 X 和 Y 是相互独立。

PS: 协方差(Covariance)用来衡量 “两个特征是否会一起偏离各自平均值” 的趋势。

  • 如果协方差是正的:当 Xi​ 比平均值大时,Xj​ 往往也比它的平均值大;或者当 Xi 比平均值小时,Xj 也比它的平均值小。这说明它们在同方向上“同涨同跌”,有正相关关系。
  • 如果协方差是负的:当 Xi 比平均值大时,Xj​ 往往比平均值小;或相反。这说明它们“此涨彼落”,存在负相关关系。
  • 如果协方差接近 0:代表两者在数值上没有显著的“同步偏离”倾向,可能互不相关或相关性不强(至少在线性角度)。

举个小例子:

  • Xi​:一个人每天的咖啡摄入量(相对于均值的偏离)。
  • Xj:同一个人每天的熬夜时长(相对于均值的偏离)。
    如果它们协方差为正,表示“咖啡喝得多的日子,我也更容易熬夜更久”。

假定 X 是观测矩阵,则协方差矩阵如下: 

快速回顾一下矩阵知识:假定一个矩阵 M 的特征向量为 wi​ 以及对应的特征向量为 λi​ ,则它们会满足下面公式: 

一个样本 X 的协方差矩阵可以表示为其转置矩阵 XT与 X 的乘积(中心化后的)。根据  Rayleigh_quotient ,样本 XX 的最大方差位于协方差矩阵的最大特征值对应的特征向量上。也就是说想要保留一个矩阵的最大信息,我们只需要保留该矩阵的最大特征值所对应的特征向量所组成的矩阵即可,这个过程就是降维了。

因此,从数据中保留的主要成分就是与矩阵的顶部 k 最大特征值对应的特征向量。

莺尾花数据集

上面主要讲述了主成分分析方法的原理,现在通过实例来加深理解。首先导入所有实验所用到的基本模块。

from mpl_toolkits.mplot3d import Axes3D
from sklearn import datasets
from sklearn import decomposition
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
sns.set(style='white')
%matplotlib inline
%config InlineBackend.figure_format = 'retina'

通过 scikit-learn 提供的数据集接口导入莺尾花数据集。

iris = datasets.load_iris()
X = iris.data
y = iris.target

为了直观地查看数据的分布,使用三维图画出莺尾花的数据分布图。

fig = plt.figure(1, figsize=(6, 5))
plt.clf()
ax = Axes3D(fig, rect=[0, 0, .95, 1], elev=48, azim=134)

plt.cla()

for name, label in [('Setosa', 0), ('Versicolour', 1), ('Virginica', 2)]:
    ax.text3D(X[y == label, 0].mean(),
              X[y == label, 1].mean() + 1.5,
              X[y == label, 2].mean(), name,
              horizontalalignment='center',
              bbox=dict(alpha=.5, edgecolor='w', facecolor='w'))
# 改变标签的顺序,让其与数据匹配
y_clr = np.choose(y, [1, 2, 0]).astype(float)
ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=y_clr,
           cmap=plt.cm.nipy_spectral)

ax.w_xaxis.set_ticklabels([])
ax.w_yaxis.set_ticklabels([])
ax.w_zaxis.set_ticklabels([])

现在让我们看看使用 PCA 是怎么样提高模型的识别性能的。同样先导入实验所用到的模块。

from sklearn.tree import DecisionTreeClassifier  # 导入决策树模型、
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score  # 识别准确率计算函数

莺尾花数据是一个相对容易区分的数据。因此,为了使实验结果对比明显。选用简单的决策树模型来对莺尾花数据进行分类。

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3,
                                                    stratify=y,
                                                    random_state=42)

# 决策树的深度设置为 2
clf = DecisionTreeClassifier(max_depth=2, random_state=42)
clf.fit(X_train, y_train)
preds = clf.predict_proba(X_test)
print('Accuracy: {:.5f}'.format(accuracy_score(y_test,
                                               preds.argmax(axis=1))))

 Accuracy: 0.88889

从上面的结果可知。在不对数据进行处理的情况下,使用决策树模型对莺尾花数据进行分类的准确率为 0.88889。

现在使用 PCA 将莺尾花数据的维度降低到 2 维,然后画出降维后的数据分布图。

# 使用从 sklearn 提供的 PCA 接口,降数据降到 2 维
pca = decomposition.PCA(n_components=2)
X_centered = X - X.mean(axis=0)
pca.fit(X_centered)
X_pca = pca.transform(X_centered)

# 可视化 PCA 降维后的结果
plt.plot(X_pca[y == 0, 0], X_pca[y == 0, 1], 'bo', label='Setosa')
plt.plot(X_pca[y == 1, 0], X_pca[y == 1, 1], 'go', label='Versicolour')
plt.plot(X_pca[y == 2, 0], X_pca[y == 2, 1], 'ro', label='Virginica')
plt.legend(loc=0)

                               

同样的方法,将降维后的莺尾花数据输入到决策树模型中。

# 训练集合测试集同时使用 PCA 进行降维
X_train, X_test, y_train, y_test = train_test_split(X_pca, y, test_size=.3,
                                                    stratify=y,
                                                    random_state=42)

clf = DecisionTreeClassifier(max_depth=2, random_state=42)
clf.fit(X_train, y_train)
preds = clf.predict_proba(X_test)
print('Accuracy: {:.5f}'.format(accuracy_score(y_test,
                                               preds.argmax(axis=1))))

Accuracy: 0.91111 

从上面的结果可知,对数据进行 PCA 降维之后,决策树模型的识别准确率提升到了 0.91111。这说明了 PCA 确实可以有效改善大部分机器学习算法的准确性和计算效率。

那么降维后的每个主成分都来自于原始数据的哪些维度呢?让我们通过查看每个维度的方差百分比来解释这个问题。

for i, component in enumerate(pca.components_):
    print("{} component: {}% of initial variance".format(i + 1,
                                                         round(100 * pca.explained_variance_ratio_[i], 2)))
    print(" + ".join("%.3f x %s" % (value, name)
                     for value, name in zip(component,
                                            iris.feature_names)))

1 component: 92.46% of initial variance

0.361 x sepal length (cm) + -0.085 x sepal width (cm) + 0.857 x petal length (cm) + 0.358 x petal width (cm)

2 component: 5.31% of initial variance

0.657 x sepal length (cm) + 0.730 x sepal width (cm) + -0.173 x petal length (cm) + -0.075 x petal width (cm) 

从上面结果可知,我们将 4 维的数据降为了 2 维数据。在降维后的数据中,第一维(也就是第一个成分)主要由原始数据的 0.361 x sepal length (cm) + -0.085 x sepal width (cm) + 0.857 x petal length (cm) + 0.358 x petal width (cm) 组成。

手写数字数据集

在上面的例子中,莺尾花的原始数据只有 4 个维度。为了验证 PCA 在其他高维数据同样可行。接下来,使用之前实验所接触到的手写数字体数据集再完成一个示例练习。先导入手写数字数据集。

digits = datasets.load_digits()
X = digits.data
y = digits.target

数据集中的每个手写数字都是由 8×8 矩阵表示,每个像素值的表示颜色强度。获取数据集的前 10 个数字。并对这 10 个手写数字体数据进行可视化。

plt.figure(figsize=(14, 5))
for i in range(10):
    plt.subplot(2, 5, i + 1)
    plt.imshow(X[i, :].reshape([8, 8]), cmap='gray')

手写数字体数据集的维度是 8×8 维的,即 64 维。只有将其降维减少到 2 维,才能对其进行可视化,以便查看其分布状态。

pca = decomposition.PCA(n_components=2)
X_reduced = pca.fit_transform(X)

print('Projecting %d-dimensional data to 2D' % X.shape[1])

plt.figure(figsize=(6, 5))
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y,
            edgecolor='none', alpha=0.7, s=40,
            cmap=plt.cm.get_cmap('nipy_spectral', 10))
plt.colorbar()
plt.title('MNIST. PCA projection')

                                

在上图中,总共包含十种颜色,每一种颜色对应一个类别标签。

除了 PCA 之外,t-SNE 也是一种常用的降维算法。相比于 PCA,t-SNE 不具有线性约束。下面使用 t-SNE 来对手写数字体数据进行降维,并对降维后的数据进行可视化。

from sklearn.manifold import TSNE  # 导入 t-SNE

tsne = TSNE(random_state=17)
X_tsne = tsne.fit_transform(X)

plt.figure(figsize=(6, 5))
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y,
            edgecolor='none', alpha=0.7, s=40,
            cmap=plt.cm.get_cmap('nipy_spectral', 10))
plt.colorbar()
plt.title('MNIST. t-SNE projection')

从上面的图中可以看出,t-SNE 相比于 PCA,降维效果要好很多。但是 t-SNE 也有一个缺点,就是其运行时间要大大超过 PCA。在实际使用中,可以根据具体任务需求来选择。

                         

在对手写数据集进行降维时,如果要保留原始数据的 90% 散度,应该将数据降到多少维呢?先来画出主成分与其所保留的原始数据散度的关系。

pca = decomposition.PCA().fit(X)

plt.figure(figsize=(6, 4))
plt.plot(np.cumsum(pca.explained_variance_ratio_), color='k', lw=2)
plt.xlabel('Number of components')
plt.ylabel('Total explained variance')
plt.xlim(0, 63)
plt.yticks(np.arange(0, 1.1, 0.1))
plt.axvline(21, c='b')
plt.axhline(0.9, c='r')
plt.show()

从上图中可以看出,要想保留原始数据 90% 的信息。需要保留 21 个主要组成成分。因此,需要将维度从 64 个特征减少到 21 个。 

                            

总结

本文主要围绕无监督学习算法进行的,主要涉及到 PCA 降维方法,并会使用 PCA 来对数据进行降维。 


网站公告

今日签到

点亮在社区的每一天
去签到