主成分分析(PCA)

使用样本进行模型训练时,特征个数太多会增加模型训练的复杂性。所以,我们希望数据集既能特征个数较少,又蕴含信息较多的信息。

虽然我们已经拿到数据集,并且数据集的维度可能较大,我们也可以通过一些方法来降低数据的维度。它的思想是:

在数据集中,特征之间是有一定的相关关系的;
当两个特征之间有一定相关关系时,可以解释为这两个特征表达的信息有一定的重叠。

PCA 就是将重复的特征(关系紧密)删去多余,建立尽可能少的新特征,使得这些新特征是两两不相关的,而且这些新特征则会尽可能多的保留原有的信息。

注意:PCA 是删除原有关系紧密的特征,产生新的不相关的特征。

你可能会想到,我们删除多余特征,保留一部分特征不就可以吗?举个例子:我们有 A、B、C 三个特征,特征之间具有一定的相关性,即:我们认为 3 个特征中包含了重叠、冗余的信息。 PCA 将新产生两个新的特征 D、E,然后将 A、B、C 中包含的部分信息融合到 D 特征中,部分信息融合到 E 特征中,并且 D 和 E 是不具有相关性。这样既能去除数据中的重叠、冗余信息,又能够实现数据维度的降低。

在这里也要清楚,当数据的维度降低时,势必会丢失部分原始数据信息。只要这部分信息不影响到我们的模型训练,我们是乐意这么做的。原始的特征在新特征组成的数据集中不会再存在。

1. 协方差矩阵

我们在前面提到 PCA 会根据特征之间的相关性来去除信息冗余,实现数据降维。在这里,通过协方差矩阵就能表现特征之间的相关性,方差和协方差的计算公式如下:

方差表示衡量特征的离散程度。方差是协方差的特殊情况,当 x 和 y 是同一个变量时,即:方差。当 x 和 y 非同一个变量时,即:协方差。

当 x 和 y 都大于自身的期望值时,协方差的值为正值,反之则为负值。协方差为 0 的两个变量,可以理解为不相关的两个变量。

协方差矩阵则为多个变量中,两两变量的协方差组成的矩阵。下面使用 Python 计算多个变量的方差和协方差矩阵:

import numpy as np
import pandas as pd


def cov():

    np.random.seed(0)
    data = np.random.randint(1, 10, [3, 4])

    dataset = pd.DataFrame(data,
                         columns=['特征%d' % (index + 1) for index in range(4)],
                         index=['样本%d' % (index + 1) for index in range(3)])
    print(dataset, end='\n\n')

    # pandas 计算协方差矩阵
    print('协方差矩阵:\n', dataset.cov().round(2))
    print()

    # numpy 计算协方差矩阵
    print('协方差矩阵:\n', np.cov(dataset.T))
    print()

    # 计算协方差矩阵
    data = data - data.mean(axis=0)
    data_cov = (data.T @ data) / (data.shape[0] - 1)
    print('协方差矩阵:\n', data_cov)


if __name__ == '__main__':
    cov()

程序执行结果:

     特征1  特征2  特征3  特征4
样本1    6    1    4    4
样本2    8    4    6    3
样本3    5    8    7    9

协方差矩阵:
       特征1    特征2   特征3    特征4
特征1  2.33  -2.17 -0.33  -4.17
特征2 -2.17  12.33  5.17   9.33
特征3 -0.33   5.17  2.33   3.17
特征4 -4.17   9.33  3.17  10.33

协方差矩阵:
 [[ 2.33333333 -2.16666667 -0.33333333 -4.16666667]
 [-2.16666667 12.33333333  5.16666667  9.33333333]
 [-0.33333333  5.16666667  2.33333333  3.16666667]
 [-4.16666667  9.33333333  3.16666667 10.33333333]]

协方差矩阵:
 [[ 2.33333333 -2.16666667 -0.33333333 -4.16666667]
 [-2.16666667 12.33333333  5.16666667  9.33333333]
 [-0.33333333  5.16666667  2.33333333  3.16666667]
 [-4.16666667  9.33333333  3.16666667 10.33333333]]

2. 主成分

为了能够去除特征之间的相关关系,我们需要将样本投射到新的坐标系空间中,如下图所示:

上图中,左侧的图找那个的点表示 4 个样本点,横纵轴表示每个样本的 2 个特征。从图上可以看到,2 个特征之间呈现的是很强的线性相关关系。为了去除这个线性关系,我们找到了一个新的坐标系(如右图所示),并将原来的样本投射到新坐标中,此时,我们可以看到样本基本都投射到 X 轴上,投射到 Y 轴时方差为 0(几乎没啥信息),也就是说在新的坐标系中,原来的 2 个特征只需要用 1 个特征就能表示出所有的信息。我们把 x 轴叫做主成分,也可以把 y 轴叫做第二主成分。

如果原来我们的数据有 100 维,我们想将其降到 2 维,就只需要找到一个坐标系将原数据分别投射到第一主成分、第二主成分得到的 2 两个维度的数据就可以作为去除线性相关性之后的保留下来的信息,如果再增加几个主成分保留的信息能够更多一些。比如:想增加到 3 维,则找到第三个与 x 和 y 正交的轴,将其投射到该轴就会得到一个新的特征。

为了能够极大的降低数据的维度,并且能够尽可能多的保留原始数据的信息。我们希望找到的前 N 个主成分都能够极大的保留的数据的信息(也就是说,找到的轴使得原始数据投射到该轴时能够方差最大,方差越大我们认为该特征保留的信息越多,反之,方差越小则说明该特征保留的信息就越少)。

所以,只要能够找到原始数据分布中,方差最大的一个方向作为主成分,次大的作为第二成分,次次大的作为第三个主成分…. 以此类推,我们就可以实现将原始数据降维到指定维度。

前面提到的协方差矩阵就是为了找到这些个主成分。我们通过对协方差矩阵进行特征值分解会得到特征值和特征向量,特征值最大对应的特征向量,就是第一主成分,以此类推。 然后将原始数据依次投射到各个主成分上,就会得到一个由 M(M < N) 个新特征组成的降维后的数据,并且该数据去除数据中的相关性,并尽可能多的保留了原始数据的信息。

3. PCA API 使用

scikit-learn 中的 PCA API 的 n_components 参数可以指定降维的维度,也可以指定一个小数,保留多少信息量,这个信息量就是根据 explained_variance_ratio_ 来衡量。

from sklearn.decomposition import PCA


if __name__ == '__main__':
    # 数据包含 4 个特征
    data = [[2, 8, 4, 5], [6, 3, 0, 8], [5, 4, 9, 1]]
    # 实例化 PCA 对象,并指定维度
    model = PCA(n_components=2)
    # 数据降维
    decomposition_data = model.fit_transform(data)
    print('降维后数据:\n', decomposition_data)

    # 打印主成分
    print('主成分:\n', model.components_)
    # 新特征的方差
    print('特征方差:\n', model.explained_variance_)
    # 打印特征的方差比率
    print('方差比率:\n', model.explained_variance_ratio_)

程序执行结果:

降维后数据:
 [[ 1.28620952e-15  3.82970843e+00]
 [ 5.74456265e+00 -1.91485422e+00]
 [-5.74456265e+00 -1.91485422e+00]]
主成分:
 [[ 0.08703883 -0.08703883 -0.78334945  0.6092718 ]
 [-0.6092718   0.78334945 -0.08703883  0.08703883]]
特征方差:
 [33. 11.]
方差比率:
 [0.75 0.25]
未经允许不得转载:一亩三分地 » 主成分分析(PCA)
评论 (0)

9 + 9 =