PCA(主成分分析)(Principal Component Analysis)是一种常用的数据分析方法。PCA 主要是通过线性变换(分解特征矩阵)将我们拿到的具有高维度的原始数据在数据预处理阶段(数据清洗)进行降维,也可以说是高维数据在低维空间上的投影。
在降维过程中,我们会减少特征的数量,这就需要我们删除数据。而数据量变少则会导致模型可以获取的信息会变少,模型的表现可能会因此受影响。同时,在高维数据中,必然有一些特征是不带有有效的信息的(比如噪音),或者有一些特征带有的信息和其他一些特征是重复的(比如一些特征可能会线性相关)。我们希望能够找出一种办法来帮助我们衡量特征上所带的信息量,让我们在降维的过程中,能够即减少特征的数量,又保留大部分有效信息——将那些带有重复信息的特征合并,并删除那些带无效信息的特征等等——逐渐创造出能够代表原特征矩阵大部分信息,特征更少的新的特征矩阵。
在降维中,PCA使用的信息量衡量指标,就是样本方差(可解释性方差),方差越大,特征所带的信息量越多。特征方差计算公式如下:
其中Var代表一个特征的方差,n代表样本量, x i xi xi代表一个特征中的每个样本取值, x − x^- x−代表这一列样本的均值。式中的n-1是为了得到样本方差的无偏估计。
sklearn是机器学习中一个常用的Python第三方模块,在进行机器学习任务时,只需要简单的调用sklearn里的模块就可以实现大多数机器学习任务。
机器学习任务通常包括分类(Classification)和回归(Regression),常用的分类器包括SVM、KNN、贝叶斯、线性回归、逻辑回归、决策树、随机森林、xgboost、GBDT、boosting、神经网络NN。
常见的降维方法包括TF-IDF、主题模型LDA、主成分分析PCA等等
class sklearn.decomposition.PCA (n_components=None, copy=True, whiten=False, svd_solver=’auto’, tol=0.0,
iterated_power=’auto’, random_state=None)
PCA作为矩阵分解算法的核心算法,参数不多,但是每个参数的意义和运用都很难,因为几乎每个参数都涉及到高深的数学原理。
假设我们现在有一组简单的数据,有特征x1和x2,三个样本数据的坐标点分别为(1,1),(2,2),(3,3)。我们可以让x1和x2分别作为两个特征向量,很轻松地用一个二维平面来描述这组数据。这组数据现在每个特征的均值都为2,方差总和也为2。
现在我们的任务是需要只用一个特征向量来描述这组数据,即将二维数据降为一维数据,并且尽可能地保留信息量,即让数据的总方差尽量靠近2。于是,我们将原本的直角坐标系逆时针旋转45°,形成了新的特征向量x1和x2组成的新平面。
在这个新平面中,三个样本数据的坐标点可以表示为 ( 2 , 0 ) , ( 2 2 , 0 ) , ( 3 2 , 0 ) (\sqrt{2},0),(2\sqrt{2},0),(3\sqrt{2},0) (2,0),(22,0),(32,0)。可以注意到,x2上的数值此时都变成了0,因此x2明显不带有任何有效信息了(此时x2的方差也为0了)。此时,x2特征上的数据均值是 2 2 2\sqrt{2} 22,而方差降维后也为2。x1上的数据均值为0,方差为1.此时,我们根据信息含量的排序,取信息含量最大的一个特征,因为我们想要的是一维数据。所以我们可以将x2
删除,同时也删除图中的x2特征向量,剩下的x1就代表了曾经需要两个特征来代表的三个样本点。通过旋转原有特征向量组成的坐标轴来找到新特征向量和新坐标平面,我们将三个样本点的信息压缩到了一条直线上,实现了二维变一维,并且尽量保留原始数据的信息。
在这个降维过程中,有几步非常重要:
二维特征矩阵 | N维特征矩阵 |
---|---|
1.找出原数据的2个特征对应的直角坐标系,本质是找出2个特征值构成的二维平面 | 1.找出原始的n个特征向量构成的n维空间 |
2.决定降维后的特征数量:1 | 2.决定降维后的特征数量:k |
3.旋转,找出一个新坐标系本质是找出2个新的特征向量,以及它们构成的新二维平面 | 3.通过变换,找出n个新的特征量及它们构成的新n维空间 |
4.找出数据点在新坐标及坐标轴上的坐标 | 4.找出原始数据在新特征空间V中的n个新特征向量上对应的值 |
5.选取方差最大的特征向量,删掉没有被选中的特征向量,降维完成 | 5选取前k个信息量最大的特征,删掉没有被选中的特征,将n维空间V降为k维,降维完成 |
在步骤3当中,我们用来找出n个新特征向量,让数据能够被压缩到少数特征上并且总信息量不损失太多的技术就是矩阵分解。PCA和SVD是两种不同的降维算法,但他们都遵从上面的过程来实现降维,只是两种算法中矩阵分解的方法不同,信息量的衡量指标不同罢了。PCA使用方差作为信息量的衡量指标,并且特征值分解来找出空间V。降维时,它会通过一系列数学的运算(比如说,产生协方差矩阵 1 n X X T \frac{1}{n}XX^{T} n1XXT)将特征矩阵X分解为以下三个矩阵,其中 Q Q Q和 Q − 1 Q^{-1} Q−1是辅助的矩阵,Σ是一个对角矩阵(即除了对角线上有值,其他位置都是0的矩阵),其对角线上的元素就是方差。降维完成之后,PCA找到的每个新特征向量就叫做“主成分”,而被丢弃的特征向量被认为信息量很少,这些信息很可能就是噪音。
无论是PCA和SVD都需要遍历所有的特征和样本来计算信息量指标。并且在矩阵分解的过程之中,会产生比原来的特征矩阵更大的矩阵,比如原数据的结构是(m,n),在矩阵分解中为了找出最佳新特征空间V,可能需要产生(n,n),(m,m)大小的矩阵,还需要产生协方差矩阵去计算更多的信息。无论代码如何简化,我们不可避免地要等待计算机去完成这个非常庞大的数学计算过程。因此,降维算法的计算量很大,运行比较缓慢,但它们的功能无可替代。
而降维算法,是将已存在的特征进行压缩,降维完成后的特征不是原本的特征矩阵中的任何一个特征,而是通过某些方式组合起来的新特征。通常来说,在新的特征矩阵生成之前,我们无法知晓降维算法建立了怎样的新特征向量,新特征矩阵生成之后也不具有可读性,我们无法判断新特征矩阵的特征是从原数据中的什么特征组合而来,新特征虽然带有原始数据的信息,却已经不是原数据上代表着的含义了。
降维算法是特征创造(feature creation)的一种。PCA一般不适用于探索特征和标签之间的关系的模型(如线性回归),它无法解释的新特征和标签之间的关系。在线性回归模型中,一般使用特征选择。
n_components是我们降维后需要的维度,即降维后需要保留的特征数量,降维流程中第二步里需要确认的k值,一般输入[0, min(X.shape)]范围中的整数。
一说到K,大家可能都会想到,类似于KNN中的K和随机森林中的n_estimators,这是一个需要我们人为去确认的超参数,并且我们设定的数字会影响到模型的表现。如果留下的特征太多,就达不到降维的效果,如果留下的特征太少,那新特征向量可能无法容纳原始数据集中的大部分信息,因此,n_components既不能太大也不能太小。这主要取决于降维的目标,如果我们希望可视化一组数据来观察数据分布,我们往往将数据降到三维以下,很多时候是二维,即n_components的取值为2。
代码如下:
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA, pca
import pandas as pd
# 提取数据集
iris = load_iris() # 实例化
y = iris.target
X = iris.data
# 调用PCA建模 降至2维,此时K=2
x_dr = PCA(2).fit_transform(X) # 也可以用下面三句代替
# pca = PCA(n_components=2)
# pca = pca.fit(X)
# x_dr = pca.transform(X)
print(X.shape)
print('X的数据表或特征矩阵:')
print(pd.DataFrame(X))
print('采用布尔索引:') # 采用布尔索引
print(x_dr[y == 0, 0])
print('iris中的对象名为:', iris.target_names)
# 要展示三中分类的分布,需要对三种鸢尾花分别绘图
# 可以写成三行代码,也可以写成for循环
"""
plt.figure()
plt.scatter(X_dr[y==0, 0], X_dr[y==0, 1], c="red", label=iris.target_names[0])
plt.scatter(X_dr[y==1, 0], X_dr[y==1, 1], c="black", label=iris.target_names[1])
plt.scatter(X_dr[y==2, 0], X_dr[y==2, 1], c="orange", label=iris.target_names[2])
plt.legend()
plt.title('PCA of IRIS dataset')
plt.show()
"""
colors = ['red', 'black', 'orange']
# 可视化
plt.figure()
for i in [0, 1, 2]:
plt.scatter(x_dr[y == i, 0],
x_dr[y == i, 1],
alpha=.7,
c=colors[i],
label=iris.target_names[i])
plt.legend()
plt.title('PCA of IRIS dataset') # PCA of IRIS dataset
plt.show()
运行结果如下:
三种鸢尾花类的分布如下图所示:
如图所示这是一个明显分簇的分布,并且每个簇之间的分布相对比较明显,数据很容易分类。可以断定KNN,随机森林,神经网络,朴素贝叶斯,Adaboost这些分类器在鸢尾花数据集上,未调整的时候都可以有很好的准确率。
代码如下:
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
# 提取数据集
iris = load_iris() # 实例化
y = iris.target
X = iris.data
# 调用PCA建模 降至2维,此时K=2
pca = PCA(n_components=2)
pca = pca.fit(X)
x_dr = pca.transform(X)
a = pca.explained_variance_ # 属性explained_variance,查看降维后每个新特征向量上所带的信息量大小(可解释性方差的大小)
print('新特征向量上所带的信息量:', a)
b = pca.explained_variance_ratio_ # 属性explained_variance_ratio,查看降维后每个新特征向量所占的信息量占原始数据总信息量的百分比,又叫做可解释方差贡献率
print('可解释方差贡献率:', b)
c = pca.explained_variance_ratio_.sum() # 新特征表所带的信息总量
print('新特征表所带的信息总量为:', c)
print('由此可见:大部分信息都被有效地集中在了第一个特征上了!')
当参数components中不填写任何值,则默认返回min(X.shape)个特征,一般来说,样本量都会大于特征数目,所以什么都不填就相当于转换了新特征空间,但没有减少特征的个数。一般来说,不会使用这种输入方式。我们却可以使用这种输入方式来画出累计可解释方差贡献率曲线,以此选择最好的n_components的整数取值。累积可解释方差贡献率曲线是一条以降维后保留的特征个数为横坐标,降维后新特征矩阵捕捉到的可解释方差贡献率为纵坐标的曲线,能够帮助我们决定n_components最好的取值(哪个的可解释方差贡献率大就以该特征个数作为K值)。
代码如下:
import numpy as np
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
# 提取数据集
iris = load_iris() # 实例化
y = iris.target
X = iris.data
pca_line = PCA().fit(X)
plt.plot([1, 2, 3, 4], np.cumsum(pca_line.explained_variance_ratio_))
plt.xticks([1, 2, 3, 4]) # 限制坐标轴显示为整数
plt.xlabel("number of components after dimension reduction")
plt.ylabel("cumulative explained variance")
plt.show()