Press "Enter" to skip to content

数据分析之特征创造——降维算法

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

前言

 

我们通过前面的两篇文章给大家介绍了特征工程的三种方法——​​Filter过滤法​​​、​ ​Embeded嵌入法以及Wrapper包装法​ ​​,本文给大家介绍特征的创造。其实特征选择只是特征工程中的第一步。真正的高手,往往使用特征创造或特征提取来寻找高级特征。在​ ​Kaggle​ ​之类的算法竞赛中,很多高分团队都是在高级特征上做文章,而这是比调参和特征选择更难的,提升算法表现的高深方法。接下来和大家一起探讨特征创造的相关算法——降维算法。

 

降维算法

 

大多数的降维算法都属于特征创造范畴,这里我们主要探讨的就是​ ​sklearn​ ​中的一些降维算法,在学习这些降维算法之前,首先给大家对比一下特征提取、特征选择与特征之间的区别与联系,具体相关内容如下:

 

 

1、 sklearn中的降维算法

 

​sklearn​ ​​中降维算法都被包括在模块​ ​decomposition​ ​中,这个模块本质是一个矩阵分解模块。在过去的几年中,如果要讨论算法进步的先锋,矩阵分解可以说是独树一帜。矩阵分解可以说在降维,深度学习,聚类分析,数据预处理,低纬度特征学习,推荐系统,大数据分析等领域。SVD和主成分分析PCA都属于矩阵分解算法中的入门算法,都是通过分解特征矩阵来进行降维,它们也是我们接下来要讲解的重点。首先给出一张相应的表将其方法进行说明:

 

 

在降维过程中,我们会减少特征的数量,这意味着删除数据,数据量变少则表示模型可以获取的信息会变少,模型的表现可能会因此受影响。同时,在高维数据中,必然有一些特征是不带有有效的信息的(比如噪音),或者有一些特征带有的信息和其他一些特征是重复的(比如一些特征可能会线性相关)。我们希望能够找出⼀种办法来帮助我们衡量特征上所带的信息量,让我们在降维的过程中, 能够既减少特征的数量,而保留一部分有效信息 ——将那些带有重复信息的特征合并,并删除那些带无效信息的特征等等——逐渐创造出能够代表原特征矩阵大部分信息的,特征更少的,新特征矩阵。

 

在上面的特征选择中,我们讲了重要的特征选择方法:方差过滤。如果一个特征的方差很小,则意味着这个特征上很可能有大量取值都相同(比如90%都是1,只有10%是0,甚至100%是1),那这这个特征的取值对样本而言就没有区分度,这种特征就不带有有效信息。从方差的这种应用就可以推断出,如果一个特征的方差很大,则说明这个特征上带有大量的信息。因此,在降维中, PCA使用的信息量衡量指标,就是样本方差,又称可解释性方差,方差越大,特征所带的信息量越多 。具体的表达式如下:

 

 

在这个公式中,其中Var代表一个特征的方差,n代表样本量,x i 代表一个特征中的每个样本取值,xhat代表这一列样本的均值。

 

2、降维的原理

 

我们首先看降维在​ ​sklearn​ ​中的源码为:

 

class sklearn.decomposition.PCA (n_components=None, copy=True, whiten=False,
svd_solver=’auto’, tol=0.0, iterated_power=’auto’, random_state=None)

 

PCA作为矩阵分解算法的核心算法,其实没有太多参数,但是每个参数的意义和运用都很难,因为几乎每个参数都涉及到高深的数学原理。为了参数的运用和意义变得越来越明朗,我们来看一组简单的二维数据的降维:

 

 

我们现在有一组简单的数据,有特征x 1 和x 2 ,三个样本数据的坐标点分别为(1,1),(2,2),(3,3)。我们可以让x 1 和x 2 分别作为两个特征向量,很轻松地用一个二维平面来描述这组数据。这组数据现在每个特征的均值都为2,方差则等于:

 

 

每个特征的数据一模一样,因此方差也都为1,数据的方差总和是2。现在我们的目标是:只用一个特征向量来描述这组数据,即将二维数据降为一维数据,并且尽可能地保留信息量,即让数据的总方差尽量靠近2。于是,我们将原本的直⻆坐标系逆时针旋转45°,形成了新的特征向量x 1* 和x 2 *组成的新平面,在这个新平面中,三个样本数据的坐标点可以表示( √2,0 ),( 2√2,0 ),(3 √2,0 )。这里可以注意到的是:x 2 *上的数值此时都变成了0,因此x 2 *明显不带有任何有效信息了(此时x 2 *的方差为0了)。此时,x 1 *特征上的数据均值是 2√2,而方差则可表示成:

 

 

x 2 *上的数据均值为0,方差也为0。

 

此时,我们根据信息含量的排序,取信息含量最大的一个特征,因为我们想要的是一维数据。所以我们可以将x 2 *删除,同时也删除图中的x 2 *特征向量,剩下的x 1 *就代表了曾经需要两个特征来代表的三个样本点。通过旋转原有特征向量组成的坐标轴来找到新特征向量和新坐标平面,我们将三个样本点的信息压缩到了一条直线上,实现了二维变一维,并且尽量保留原始数据的信息。一个成功的降维,就实现了。其实从上面降维的这个过程我们不难发现:有以下几个重要的步骤:

 

 

在步骤3当中, 我们用来找出n个新特征向量,让数据能够被压缩到少数特征上并且总信息量不损失太多的技术就是矩阵分解。 PCA和SVD是两种不同的降维算法,但他们都遵从上面的过程来实现降维,只是两种算法中矩阵分解的方法不同,信息量的衡量指标不同罢了。PCA使用方差作为信息量的衡量指标,并且特征值分解来找出空间V。降维时,它会通过一系列数学的神秘操作(比如说,产生协方差矩阵 )将特征矩阵X分解为以下三个矩阵,其中Q和Q -1 是辅助的矩阵,Σ是一个对⻆矩阵(即除了对⻆线上有值,其他位置都是0的矩阵),其对⻆线上的元素就是方差。降维完成之后,PCA找到的每个新特征向量就叫做“主成分”,而被丢弃的特征向量被认为信息量很少,这些信息很可能就是噪音。

 

 

而SVD使用奇异值分解来找出空间V,其中Σ也是一个对⻆矩阵,不过它对⻆线上的元素是奇异值,这也是SVD中用来衡量特征上的信息量的指标。U和V^{T}分别是左奇异矩阵和右奇异矩阵,也都是辅助矩阵。

 

 

在数学原理中,无论是PCA和SVD都需要遍历所有的特征和样本来计算信息量指标。并且在矩阵分解的过程之中,会产声比原来的特征矩阵更大的矩阵,比如原数据的结构是(m,n),在矩阵分解中为了找出最佳新特征空间V,可能需要产生(n,n),(m,m)大小的矩阵,还需要产生协方差矩阵去计算更多的信息。而现在无论是Python还是R,或者其他的任何语言,在大型矩阵运算上都不是特别擅长,无论代码如何简化,我们不可避免地要等待计算机去完成这个非常庞大的数学计算过程。因此,降维算法的计算量很大,运行比较缓慢,但无论如何,它们的功能无可替代,它们依然是机器学习领域的宠儿。

 

其实通过上面的介绍不难发现:PCA和特征选择技术都是特征工程的一部分;那幺读者看到这里肯用有一个疑问:它们有什幺不同呢?其实特征选择是从已存在的特征中选取携带信息最多的,选完之后的特征依然具有可解释性,我们依然知道这个特征在原数据的哪个位置,代表着原数据上的什幺含义。而PCA,是将已存在的特征进行压缩,降维完毕后的特征不是原本的特征矩阵中的任何一个特征,而是通过某些方式组合起来的新特征。通常来说, 在新的特征矩阵生成之前,我们无法知晓PCA都建立了怎样的新特征向量,新特征矩阵生成之后也不具有可读性 ,我们⽆法判断新特征矩阵的特征是从原数据中的什幺特征组合而来,新特征虽然带有原始数据的信息,却已经不是原数据上代表着的含义了。以PCA为代表的降维算法因此是特征创造(​ ​feature creation​ ​​,或​ ​feature construction​ ​)的一种。可想而知,PCA一般不适用于探索特征和标签之间的关系的模型(如线性回归),因为无法解释的新特征和标签之间的关系不具有意义。在线性回归模型中,我们使用特征选择。

 

3、重要参数​ ​n_components​

 

​n_components​ ​​是我们降维后需要的维度,即降维后需要保留的特征数量,降维流程中第⼆步⾥需要确认的k值,一般输入​ ​[0, min(X.shape)]​ ​​范围中的整数。一说到K,大家可能都会想到,类似于KNN中的K和随机森林中的​ ​n_estimators​ ​​,这是一个需要我们人为去确认的超参数,并且我们设定的数字会影响到模型的表现。如果留下的特征太多,就达不到降维的效果,如果留下的特征太少,那新特征向量可能⽆法容纳原始数据集中的⼤部分信息,因此,​ ​n_components​ ​既不能太大也不能太小。那我们应该怎幺选取合适的呢:其实我们可以先从降维的目标说起:如果我们希望可视化一组数据来观察数据分布,我们往往将数据降到三维以下,很多时候是二维,即​ ​n_components​ ​的取值为2。

 

(1)、鸢尾花数据集的可视化案例

 

该案例的数据集我们直接是从网上登录获取,通过代码获取,没有必要保存到本地,当然这个数据集是特别典型的鸢尾花数据集。我们首先通过PCA建立数据模型,然后获取新的矩阵,具体实现如下:

 

#导⼊模块和包
from sklearn.datasets import load_iris #导⼊sklearn中内置的鸢尾花数据集
from sklearn.decomposition import PCA #导⼊PCA降维算法
import matplotlib.pyplot as plt #绘图包
#提取数据集
iris = load_iris()
y = iris.target
X = iris.data
#调⽤PCA建模
pca = PCA(n_components=2) #实例化
pca = pca.fit(X) #拟合模型
X_dr = pca.transform(X) #获取新矩阵
print(X_dr[y == 0, 0])

 

我们通过调用PCA拟合模型,其新矩阵的运行结果如下:

 

 

为了更加直观地看出其模型,我们通过plot工具将其模型通过代码展示出来,具体实现如下:

 

#导⼊模块和包
from sklearn.datasets import load_iris #导⼊sklearn中内置的鸢尾花数据集
from sklearn.decomposition import PCA #导⼊PCA降维算法
import matplotlib.pyplot as plt #绘图包
#提取数据集
iris = load_iris()
y = iris.target
X = iris.data
#调⽤PCA建模
pca = PCA(n_components=2) #实例化
pca = pca.fit(X) #拟合模型
X_dr = pca.transform(X) #获取新矩阵
# print(X_dr[y == 0, 0])
X_dr[y == 0, 0]
#要展示三中分类的分布,需要对三种鸢尾花分别绘图
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()

 

我们模型的具体分布如下:

 

 

当然,我们不难发现代码其实也可以用遍历,这种方法比较繁琐的,我们稍微改一下代码,具体如下:

 

#导⼊模块和包
from sklearn.datasets import load_iris #导⼊sklearn中内置的鸢尾花数据集
from sklearn.decomposition import PCA #导⼊PCA降维算法
import matplotlib.pyplot as plt #绘图包
#提取数据集
iris = load_iris()
y = iris.target
X = iris.data
#调⽤PCA建模
pca = PCA(n_components=2) #实例化
pca = pca.fit(X) #拟合模型
X_dr = pca.transform(X) #获取新矩阵
# print(X_dr[y == 0, 0])
X_dr[y == 0, 0]
#要展示三中分类的分布,需要对三种鸢尾花分别绘图
colors = ['red', 'black', 'orange']
iris.target_names
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')
plt.show()

 

当然效果仍然是和上面的模型分布是一致的,具体如下:

 

 

从上面的分布图我们不难发现:明显这是一个分簇的分布,并且每个簇之间的分布相对比较明显,也许​ ​versicolor​ ​​和​ ​virginia​ ​​这两种花之间会有一些分类错误,但​ ​setosa​ ​肯定不会被分错。这样的数据很容易分类,可以遇见,KNN,随机森林,神经网络,朴素贝叶斯,Adaboost这些分类器在鸢尾花数据集上,未调整的时候都可以有95%上下的准确率。

 

我们稍微探索该降维后的数据,我们通过代码模拟一下这个过程,具体实现如下:

 

#导⼊模块和包
from sklearn.datasets import load_iris #导⼊sklearn中内置的鸢尾花数据集
from sklearn.decomposition import PCA #导⼊PCA降维算法
import matplotlib.pyplot as plt #绘图包
#提取数据集
iris = load_iris()
y = iris.target
X = iris.data
#调⽤PCA建模
pca = PCA(n_components=2) #实例化
pca = pca.fit(X) #拟合模型
X_dr = pca.transform(X) #获取新矩阵
# print(X_dr[y == 0, 0])
X_dr[y == 0, 0]
#要展示三中分类的分布,需要对三种鸢尾花分别绘图
colors = ['red', 'black', 'orange']
print(iris.target_names)
#属性explained_variance_,
# 查看降维后每个新特征向量上所带的信息量⼤⼩(可解释性⽅差的⼤⼩)
print(pca.explained_variance_)
#属性explained_variance_ratio,
#查看降维后每个新特征向量所占的信息量占原始数据总信息量的百分⽐
#⼜叫做可解释⽅差贡献率
print(pca.explained_variance_ratio_)
#⼤部分信息都被有效地集中在了第⼀个特征上
print(pca.explained_variance_ratio_.sum())

 

具体将为后的结果如下:

 

 

从上图我们不难发现其做可解释⽅差贡献率竟达到了0.977685206318795,这个已经很理想了。不过,应该我们的参数选择仍然不是最好的,接下来我们介绍​ ​n_components​ ​的最恰当选择的方式。

 

(2)、选择最好的​ ​n_components​

 

当参数​ ​n_components​ ​​中不填写任何值,则默认返回​ ​min(X.shape)​ ​​个特征,一般来说,样本量都会大于特征数目,所以什幺都不填就相当于转换了新特征空间,但没有减少特征的个数。一般来说,不会使用这种输出方式。但我们却可以使用这种输入方式来画出累计可解释方差贡献率曲线,以此选择最好的​ ​n_components​ ​的整数取值。 累积可解释方差贡献率曲线是⼀条以降维后保留的特征个数为横坐标,降维后新特征矩阵捕捉到的可解释方差贡献率为纵坐标的曲线,能够帮助我们决定 ​ ​n_components​ ​ ​最好的取值 。我们通过实验加以说明:

 

#导⼊模块和包
from sklearn.datasets import load_iris #导⼊sklearn中内置的鸢尾花数据集
from sklearn.decomposition import PCA #导⼊PCA降维算法
import matplotlib.pyplot as plt #绘图包
import numpy as np
#提取数据集
iris = load_iris()
y = iris.target
X = iris.data
#调⽤PCA建模
pca = PCA(n_components=2) #实例化
pca = pca.fit(X) #拟合模型
X_dr = pca.transform(X) #获取新矩阵
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 ratio")
plt.show()

 

通过代码实现我们​ ​n_components​ ​取值的曲线图如下:

 

 

我们除了输入整数,​ ​n_components​ ​还有哪些选择呢?之前我们提到过,矩阵分解的理论发展在业界独树一帜,勤奋智慧的数学大神Minka, T.P.在麻省理工学院媒体实验室做研究时找出了 让PCA用最似然估计( ​ ​maximum likelihood estimation​ ​ )自选超参数的⽅法,输如“mle”作为n_components的参数输入,就可以调用这种方法 。我们通过实验进行验证:

 

#导入模块和包
from sklearn.datasets import load_iris #导⼊sklearn中内置的鸢尾花数据集
from sklearn.decomposition import PCA #导⼊PCA降维算法
#提取数据集
iris = load_iris()
y = iris.target
X = iris.data
pca_mle = PCA(n_components="mle")
pca_mle = pca_mle.fit(X)
X_mle = pca_mle.transform(X)
print(X_mle)
print(pca_mle.explained_variance_ratio_.sum())

 

该最后的准确率以及新的矩阵分布如下:

 

 

我们从上图可以看出其准确率达到0.9947878161267247。接着我们输入[0,1]之间的浮点数,并且让参数​ ​svd_solver =='full'​ ​​,表示希望降维后的总解释性方差占比大于​ ​n_components​ ​​指定的百分比,即是说,希望保留百分之多少的信息量。比如说,如果我们希望保留97%的信息量,就可以输入​ ​n_components = 0.97​ ​,PCA会自动选出能够让保留的信息量超过97%的特征数量。具体代码实现如下:

 

#导⼊模块和包
from sklearn.datasets import load_iris #导⼊sklearn中内置的鸢尾花数据集
from sklearn.decomposition import PCA #导⼊PCA降维算法
import matplotlib.pyplot as plt #绘图包
import numpy as np
#提取数据集
iris = load_iris()
y = iris.target
X = iris.data
pca_f = PCA(n_components=0.97,svd_solver="full")
pca_f = pca_f.fit(X)
X_f = pca_f.transform(X)
print(pca_f.explained_variance_ratio_)

 

具体的执行结果如下:

 

 

总结

 

本文给大家主要介绍了降维算法中的一些方法,这些方法是特别使用的,也是我们在特征选择中经常用到的方法之一。至此,我们的特征工程已经全部介绍完毕。最后给大家留个作业: 通过本文用到的PCA降维去对上文中手写数字识别案例进行一个改动,看看其模型的准确率为多少。再与其他模型做一个对比。 只有这样我们才能真的掌握这些基本的知识。希望大家能够认真的完成。最后希望大家学有所成。

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。