3.3 大数据时代的数据降维

数据降维就是降低数据的维度数量,数据降维是维数归约的一个重要课题。

3.3.1 需要数据降维的情况

数据降维可以降低模型的计算量并减少模型运行时间、降低噪音变量信息对于模型结果的影响、便于通过可视化方式展示归约后的维度信息并减少数据存储空间。因此,大多数情况下,当我们面临高维数据时,都需要对数据做降维处理。是否进行降维主要考虑以下方面:

1)维度数量。降维的基本前提是高维,假如模型只有几个维度,那就不一定需要降维,具体取决于维度本身的重要性、共线性以及其他排除关系,而不是出于高维的考虑。

2)建模输出是否必须保留原始维度。某些场景下,我们需要完整保留参与建模的原始维度并在最终建模输出时能够得以分析、解释和应用,这种情况下不能进行转换方式降维,只能选择特征筛选的方式降维。

3)对模型的计算效率与建模时效性有要求。当面临高维数据建模时,数据模型的消耗将呈几何倍数增长,这种增长带来的结果便是运算效率慢、耗时长。如果对建模时间和时效性有要求,那么降维几乎是必要步骤。

4)是否要保留完整数据特征。数据降维的基本出发点是在尽量(或最大化)保留原始数据特征的前提下,降低参与建模的维度数。在降维过程中,无论未被表示出来的特征是噪音还是正常分布,这部分信息都无法参与建模。如果某些场景下需要所有数据集的完整特征,那么通常不选择降维。

提示

数据降维只是处理高维数据的思路和方法之一,除此之外,国内外学者也在研究其他高维数据建模的方法。以高维数据聚类为例,除了可以通过降维来应对之外,其他思路还包括基于超图的聚类、基于子空间的聚类、联合聚类等,这些都是非降维的方法。

3.3.2 基于特征选择的降维

基于特征选择的降维指的是根据一定规则和经验,直接选取原有维度的一部分参与到后续的计算和建模过程,用选择的维度代替所有维度,整个过程不产生新的维度。

基于特征选择的降维方法有4种思路:

❑ 经验法:根据业务专家或数据专家的以往经验、实际数据情况、业务理解程度等进行综合考虑。业务经验依靠的是业务背景,从众多维度特征中选择对结果影响较大的特征;而数据专家则依靠的是数据工作经验,基于数据的基本特征以及对后期数据处理和建模的影响来选择或排除维度,例如去掉缺失值较多的特征。

❑ 测算法:通过不断测试多种维度选择参与计算,通过结果来反复验证和调整并最终找到最佳特征方案。

❑ 基于统计分析的方法:通过相关性分析不同维度间的线性相关性,在相关性高的维度中进行人工去除或筛选;或者通过计算不同维度间的互信息量,找到具有较高互信息量的特征集,然后把其中的一个特征去除或留下。

❑ 机器学习算法:通过机器学习算法得到不同特征的特征值或权重,然后再根据权重来选择较大的特征。图3-2所示是通过CART决策树模型得到不同变量的重要程度,然后根据实际权重值进行选择。

图3-2 变量重要性得分

这种数据降维方法的好处是,在保留了原有维度特征的基础上进行降维,既能满足后续数据处理和建模需求,又能保留维度原本的业务含义,以便于业务理解和应用。对于业务分析型的应用而言,模型的可理解性、可解释性和可应用性在很多时候,其优先级要高于模型本身的准确率、效率等技术类指标。要知道,如果没有业务的理解和支持,再好的数据、模型和算法都无法落地。

例如通过决策树得到的特征规则,可以作为选择用户样本的基础条件,而这些特征规则便是基于输入的维度产生。假如我们在决策树之前将原有维度用表达式(例如PCA的主成分)方法进行转换,那么即使得到了决策树规则,也无法直接应用于业务。

3.3.3 基于维度转换的降维

基于维度转换的降维是按照一定的数学变换方法,把给定的一组相关变量(维度)通过数学模型将高维空间的数据点映射到低维度空间中,然后利用映射后变量的特征来表示原有变量的总体特征。这种方式是一种产生新维度的过程,转换后的维度并非原有维度的本体,而是其综合多个维度转换或映射后的表达式。

例如,假设原始数据集中有10个维度,分别是tenure、cardmon、lncardmon、cardten、lncardten、wiremon、lnwiremon、wireten、lnwireten、hourstv,现在用主成分分析进行降维,降维后选择具有显著性的前3个主成分作为示例:

        方程式用于 主成分-1
            0.006831 * tenure +
            0.007453 * cardmon +
            0.1861 * lncardmon +
            0.0001897 * cardten +
            0.1338 * lncardten +
            + -4.767
        方程式用于 主成分-2
            -0.4433 * lnwiremon +
            -0.0001222 * wireten +
            -0.1354 * lnwireten +
            0.008099 * hourstv +
            + -0.272
        方程式用于 主成分-3
            -0.01809 * tenure +
            0.0124 * cardmon +
            0.00002565 * wireten +
            -0.1644 * lnwireten +
            0.03984 * hourstv +
            + -4.076

上述转换结果就是主成分分析后提取的3个能基本代表原始10个维度的新“维度”。通过上述结果发现主成分(也就是新的“维度”)是一个多元一次方程,其含义无法直接体现和理解。但对于不关注每个维度业务含义的系统级应用和集成来讲是没有关系的,因为后续算法不需要知道每个变量的具体业务含义。

为了更形象地解释映射关系,现在使用一个序列图来表示整个映射过程。图3-3所示为原始数据集在经过顺时针旋转(映射变换)后的可视化特征变化过程。从图3-3中所示①到⑧的序列中可以看出,原始数据的分布呈现明显的曲线分布特征,而旋转后的数据样本则呈现直线分布特征。

图3-3 数据维度特征转换过程

通过数据维度变换进行降维是非常重要的降维方法,这种降维方法分为线性降维和非线性降维两种,其中常用的代表算法包括独立成分分析(ICA)、主成分分析(PCA)、因子分析(Factor Analysis, FA)、线性判别分析(LDA,又称Fisher线性判别FLD)、局部线性嵌入(LLE)、核主成分分析(Kernel PCA)等。

以下介绍PCA的主要适用场景:

非监督式的数据集。它是一种非监督式的降维方法,因此适用于不带有标签的数据集;而对于带有标签的数据集则可以采用LDA。

根据方差自主控制特征数量。最大的主成分的数量会小于或等于特征的数量,这意味着,PCA也可以输出完全相同数量的特征,具体取决于选择特征中解释的方差比例。

更少的正则化处理。选择较多的主成分将导致较少的平滑,因为我们将能够保留更多的数据特征,从而减少正则化。

数据量较大的数据集。数据量大包括数据记录多和数据维度多两种情况,PCA对大型数据集的处理效率较高。

数据分布是位于相同平面上(非曲面),数据中存在线性结构。

提示

在数据挖掘或机器学习中,经常会把数据分为训练集和测试集,也有可能包括应用集,多份数据需要单独降维还是把数据集合起来进行整体降维?对于线性方法(例如PCA)而言,它旨在寻找一个高维空间到低维空间的映射矩阵或映射关系,当映射矩阵找到后便可直接将其应用到其他数据集进行降维(通俗点理解就是直接套用矩阵公式得到降维结果),因此,这种降维方式下可以单独降维;而非线性方法(例如LLE)则需要在保持某种局部结构的条件下实现数据的整体降维,此时需要所有的数据集合到一起然后才能实现数据降维。

3.3.4 代码实操:Python数据降维

本示例中,将分别使用sklearn的DecisionTreeClassifier来判断变量重要性并选择变量,通过PCA进行维度转换。数据源文件data1.txt位于“附件-chapter3”中,默认程序的工作目录为“附件-chapter3”(如果不是,请切换到该目录下,否则会报错“IOError: File data1.txt does not exist”)。完整代码如下:

        import numpy as np
        from sklearn.tree import DecisionTreeClassifier
        from sklearn.decomposition import PCA
        # 读取数据文件
        data = np.loadtxt('data1.txt')  # 读取文本数据文件
        x = data[:, :-1]  # 获得输入的x
        y = data[:, -1]  # 获得目标变量y
        print (x[0], y[0])  # 打印输出x和y的第一条记录
        # 使用sklearn的DecisionTreeClassifier判断变量重要性
        model_tree = DecisionTreeClassifier(random_state=0)  # 建立分类决策树模型对象
        model_tree.fit(x, y)  # 将数据集的维度和目标变量输入模型
        feature_importance = model_tree.feature_importances_  # 获得所有变量的重要性得分
        print (feature_importance)  # 打印输出
        # 使用sklearn的PCA进行维度转换
        model_pca = PCA()  # 建立PCA模型对象
        model_pca.fit(x)  # 将数据集输入模型
        model_pca.transform(x)  # 对数据集进行转换映射
        components = model_pca.components_  # 获得转换后的所有主成分
        components_var = model_pca.explained_variance_  # 获得各主成分的方差
        components_var_ratio = model_pca.explained_variance_ratio_  # 获得各主成分的方差占比
        print (components[:2])  # 打印输出前2个主成分
        print (components_var[:2])  # 打印输出前2个主成分的方差
        print (components_var_ratio)  # 打印输出所有主成分的方差占比

上述代码以空行分为4部分。

第一部分导入库。本程序中涉及Numpy和Sklearn中的DecisionTreeClassifier、PCA。

第二部分导入数据文件。通过Numpy的loadtxt方法读取数据文件得到矩阵,然后对矩阵进行切割,得到输入变量集x和目标变量y, xy的第一条数据记录为:

        (array([ 1.88622997,  1.31785876, -0.16480621,  0.56536882, -1.11934542,
                -0.53218995, -0.6843102 ,  1.24149827,  1.00579225,  0.45485041]), 0.0)

第三部分使用Sklearn的DecisionTreeClassifier判断变量重要性。首先建立分类决策树模型对象,在参数中通过random_state来控制随机种子,如果不设置则会导致模型结果有微小差异;然后通过fit方法输入xy进行训练,在训练模型中通过feature_importances_方法得到变量重要性得分,打印输出结果如下:

        [ 0.03331054  0.01513967  0.02199713  0.119727    0.47930312  0.04776297
          0.17111746  0.02585441  0.02012725  0.06566044]

从变量重要性得分看出,第4/5/7三个变量的重要性最高,分别约为0.12、0.48、0.17,三者得分之和约等于77%,这意味着仅仅这3个变量已经具有非常显著的并且足以代表所有变量参与模型计算的能力。因此,可以选择这3个变量参与后续模型计算。

提示

一般情况下,如果选择的变量重要性总得分接近80%,基本上已经可以解释大部分的特征变化了。变量重要性的总得分为1,值越大重要性越高。

第四部分为使用Sklearn的PCA进行维度转换。该部分的实现思路与DecisionTree-Classifier类似,不同之处在于当创建模型对象之后,我们使用fit方法仅输入x进行PCA训练(y不需要转换),然后通过transform进行转换,fit和tranform可以合并为一步fit_transform(x)。但是考虑到PCA通过fit方法得到的映射关系在后续会用到,因此这里单独使用transform实现对数据集的降维转换。在得到PCA训练模型后,通过模型的components_、explained_variance_、explained_variance_ratio_属性分别获取转换后的所有主成分、各主成分的方差和各主成分的方差占比。打印输出结果如下:

1)前2个主成分:

        [[  7.18818316e-03   1.41619205e-02   1.00543847e-02   3.65097575e-01
            6.38944537e-01  -1.95750380e-02  -1.73413378e-01  -3.80829974e-02
            -2.87413113e-03  -6.52829504e-01]
          [  1.01307710e-02  -1.95270201e-04  -2.33689543e-02  -6.12915216e-01
            5.08983971e-01  -2.23429533e-02   6.02958940e-01  -1.49061329e-02
            -1.81362216e-02  -3.41623971e-03]]

2)前2个主成分对应的方差:

        [ 4.22180334  2.20928822]

3)所有主成分的方差占比

        [  3.38339364e-01   1.77054475e-01   8.92753857e-02   8.73655166e-02
            8.23542686e-02   8.03329836e-02   7.38094896e-02   7.14685179e-02
            7.52486951e-33   2.60352734e-33]

上述结果中,所有主成分的方差占比是选择主成分数量的关键,因为PCA降维的基本思想是根据方差占比来选择主成分的数量。通过该结果可以看出,前6项主成分的方差占比之和components_var_ratio[:5].sum()约等于77%,取前6项主成分基本可以作为转换后的主成分参与后续模型计算。

提示

在建立PCA模型时,可通过n_components指定要获得的主成分数量,但通常不建议采用这种方式,原因是无法根据各主成分的方差占比判断到底指定多少个主成分最合适。当然,如果对于主成分或转换后变量的数量有强烈约束,那么直接指定components的方法是行之有效的。

上述过程中,需要考虑的关键点是:

❑ 如何根据变量重要性得分选择原始变量;

❑ 如何通过方差占比选择合适的主成分。

本小节示例中,主要用了几个知识点:

❑ 通过Numpy的loadtxt读取数据文件;

❑ 对Numpy矩阵进行切片;

❑ 通过Sklearn的DecisionTreeClassifier建立决策树模型,并获得变量重要性属性;

❑ 通过使用sklearn的PCA建立PCA模型,并对数据集进行维度转换降维,并从PCA模型中获得有关主成分、方差和方差占比属性。