微信号:bigdata_ny

介绍:分享大数据、机器学习和数学等相关的内容.

可视化图表让机器学习“biu”的一样简单:特征分析

2016-06-28 07:55 侠天

--视觉诊断让你对机器学习了如指掌。


写在之前:此篇文章在InfoQ首发。


Python和high level的机器学习/深度学习库,比如Scikit-learn,TensorFlow,NLTK,PyBrain,Theano和MLPY让机器学习走进“大众”(开发社区)视野。随着这些工具的开源,现在有了越来越多的机器学习从业者。与此同时,机器学习的份额并没有增加。预测工具正在成为各行各业(从商业,艺术,和工程到教育,法律和国防)的决策驱动。


当我们使用几行Python代码来示例和拟合一个模型时,如何才能确保我们的预测结果是可信的和健壮的?

 
           
  1. from sklearn.linear_model import LinearRegression

  2. model = LogisticRegression()

  3. model.fit(X,y)

  4. model.predict(X)


选择什么样的初始模型?使用哪些特征?哪些特征需要归一化?如何来甄别一些问题(比如,局部极小值和过拟合)?从弱模型可以得到优化模型吗?


为了帮助我们解决以上问题,让我们来看一下以下4个二维数组,为每个生成预测模型:

 
           
  1. import numpy as np

  2. i   = np.array([

  3.    [10.0, 8.0, 13.0, 9.0, 11.0, 14.0, 6.0, 4.0, 12.0, 7.0, 5.0],

  4.    [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]

  5. ])

  6. ii  = np.array([

  7.    [10.0, 8.0, 13.0, 9.0, 11.0, 14.0, 6.0, 4.0, 12.0, 7.0, 5.0],

  8.    [9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]

  9. ])

  10. iii = np.array([

  11.    [10.0, 8.0, 13.0, 9.0, 11.0, 14.0, 6.0, 4.0, 12.0, 7.0, 5.0],

  12.    [7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73]

  13. ])

  14. iv  = np.array([

  15.    [8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 19.0, 8.0, 8.0, 8.0],

  16.    [6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89]

  17. ])


我们应该使用哪种模型来拟合数据呢?首先,为每个数组计算统计属性:平均值、方差、相关系数以及线性回归的斜率和截距。

 
           
  1. from scipy import stats

  2. def get_stats(twoDarray):

  3.    print(np.mean(twoDarray[0]))

  4.    print(np.mean(twoDarray[1]))

  5.    print(np.var(twoDarray[0]))

  6.    print(np.var(twoDarray[1]))

  7.    print(np.corrcoef(twoDarray[0],twoDarray[1]))

  8.    print(stats.linregress(twoDarray[0],twoDarray[1]))

  9. for data in (i, ii, iii, iv):

  10.    get_stats(data)


当你运行上面的代码,你会发现四组数组中有相同的描述统计属性。这可能导致我们决定为每个数组使用单个模型(比如,sklearn.linear_model.LinearRegression)。但是,如果我们把数据集绘制成图表,我们将看到跟我们想象的不一样:

 
           
  1. def make_plot(a, b, c, d):

  2.    fig, ((axa, axb), (axc, axd)) =  plt.subplots(2, 2, sharex='col', sharey='row')

  3.    for arr, ax in ((a, axa), (b, axb), (c, axc), (d, axd)):

  4.        x = arr[0]

  5.        y = arr[1]

  6.        ax.scatter(x, y, c='g')

  7.        m,b = np.polyfit(x, y, 1)

  8.        X = np.linspace(ax.get_xlim()[0], ax.get_xlim()[1], 100)

  9.        ax.plot(X, m*X+b, '-')

  10.    plt.show()

  11. make_plot(i, ii, iii, iv)


更重要的是,一个简单的线性回归模型并不能对这个四个数组都满足。我们能看到 i 和 iii 是线性关系,但是它们的回归线又是那么的不同。在 ii 的图表中,我们看到变量是相关的,但又不是线性相关的,也不是完全的正太分布。并且,图表 iii 和 iv 都包含比较明显的异常点,严重地影响到相关系数。


统计学家弗朗西斯·安斯库姆(Francis Anscombe)于1973年构造出安斯库姆四重奏(Anscombe’s quartet),它包含四组基本的统计特性一致的数据,目的是用来说明在分析数据前先绘制图表的重要性,以及离群值对统计的影响之大。所以,有时数据集的可视化对机器学习至关重要。在数据科学中,视觉诊断是一种强大的但常常又被低估的工具。可视化不应该在数据管道的结尾处。当我们直接在原始数据集中看不到什么规律时,绘制图表可以帮助我们找到模型/模式。静态输出结果和表格数据不能使得模型/模式显现的地方,人类视觉分析能够洞察,并能获得健壮的程序和更好的数据产品。


在机器学习中,许多因素(比如,杂乱的数据,过度训练数据集,过度调优,维度灾难((curse of dimensionality)[备注一]等)会引起问题。视觉诊断可以辨别出“损毁”的模型和正常预测的模型。在系列文章《视觉诊断让你对机器学习了如指掌》中,将向你展示可视化工具是如何在机器学习过程的几个关键阶段(特征工程,模型选择,参数调优)提供帮助的?如何有效利用Scikit-Learn库和Matplotlib库(包括但不限于Pandas,Bokeh和 Seaborn)。


样本数据集

为了在不同的领域讲解可视化方法,这里将使用不同的数据集,来自于UCI机器学习仓库

  • 从light,humidity,CO2等特征中预测房屋入住

  • 从sex,education,marital status,age和payment history几个特征中预测信用卡默认支付

  • 从混凝土生产年限和材料来预测混凝土抗压强度


下面给出简单的Python脚本,使用Python的requests模块去UCI获取所有三个数据集:

 
           
  1. import os

  2. import zipfile

  3. import requests

  4. OCCUPANCY = ('http://bit.ly/ddl-occupancy-dataset', 'occupancy.zip')

  5. CREDIT    = ('http://bit.ly/ddl-credit-dataset', 'credit.xls')

  6. CONCRETE  = ('http://bit.ly/ddl-concrete-data', 'concrete.xls')

  7. def download_data(url, name, path='data'):

  8.    if not os.path.exists(path):

  9.        os.mkdir(path)

  10.    response = requests.get(url)

  11.    with open(os.path.join(path, name), 'w') as f:

  12.        f.write(response.content)

  13. def download_all(path='data'):

  14.    for href, name in (OCCUPANCY, CREDIT, CONCRETE):

  15.        download_data(href, name, path)

  16.    # Extract the occupancy zip data

  17.    z = zipfile.ZipFile(os.path.join(path, 'occupancy.zip'))

  18.    z.extractall(os.path.join(path, 'occupancy'))

  19. path='data'

  20. download_all(path)


运行脚本后,你会在当前工作目录下发现一个名为data文件夹,包含两个XLS文件(Excel),一个zip文件和一个包含房间入住数据的未压缩文件夹。


特征分析和选择

特征选择是机器学习的关键。对于三个样本数据集来说比较简单,因为这些数据集在上传到UCI仓库之前就已经做好了部分特征选择。但是当我们自己做机器学习时,还是必须使用统计和其它方法(比如,和领域专家交流,可视化分析)结合的方式来做特征选择。在现实情况中,我们期待可能只有几个属性需要预测而不用管其它的属性。我们也期待有些属性是冗余的(比如,两种属性的线性组合)。


在特征选择这步,我们的目标是能够找到合适的最小特征集合来达到最好的预测值。为什么呢?首先,减小特征的数量能够降低模型的复杂度,相应的也减小偏差。第二,低维度的数据集消耗更少的计算时间。最后,实践证明,基于更小的变量数据的模型更容易解释。统计方法,比如,平均值和方差,在特征拆解中是非常有用第一步。获得数据后,我们导入pandas模块,加载数据进入data frame,粗略扫一眼:

 
           
  1. import pandas as pd

  2. # Load the room occupancy dataset

  3. occupancy = os.path.join('data','occupancy_data','datatraining.txt')

  4. occupancy = pd.read_csv(occupancy, sep=',')

  5. occupancy.columns = [

  6.    'date', 'temp', 'humid', 'light', 'co2', 'hratio', 'occupied'

  7. ]

  8. # View the occupancy details

  9. print(occupancy.head())

  10. print(occupancy.describe())

  11. # Load the credit card default dataset

  12. credit = os.path.join('data','credit.xls')

  13. credit = pd.read_excel(credit, header=1)

  14. credit.columns = [

  15.    'id', 'limit', 'sex', 'edu', 'married', 'age', 'apr_delay', 'may_delay',

  16.    'jun_delay', 'jul_delay', 'aug_delay', 'sep_delay', 'apr_bill', 'may_bill',

  17.    'jun_bill', 'jul_bill', 'aug_bill', 'sep_bill', 'apr_pay', 'may_pay', 'jun_pay',

  18.    'jul_pay', 'aug_pay', 'sep_pay', 'default'

  19. ]

  20. # View the credit details

  21. print(credit.head())

  22. print(credit.describe())

  23. # Load the concrete compression data set

  24. concrete   = pd.read_excel(os.path.join('data','concrete.xls'))

  25. concrete.columns = [

  26.    'cement', 'slag', 'ash', 'water', 'splast',

  27.    'coarse', 'fine', 'age', 'strength'

  28. ]

  29. # View the concrete details

  30. print(concrete.head())

  31. print(concrete.describe())


从上面.describe() 语句的输出结果可以开始对三个数据集的不同有个大概的认识。例如,对于房间入住数据集,light和CO2释放的标准差比temperature和humidity的标准差多两个数量级。这意味着可能有必要进行归一化处理。在信用卡默认支付的数据集中,打标签(label,0代表信用卡持有者没有默认支付;1代表有默认支付)数据的分布不均衡,这意味着分类可能不平衡。


然而,如果你仅仅只是基于描述的表格来选择特征,而没有相关领域专家的指导,这个数据预测将是非常艰难的。一般在这种情况下,有过预测模型训练经验的人员经常会可视化数据集,这样他们可以清晰地看到不同特征向量的行为。下面我们将用一些常规的方法来可视化前面的三种数据集:

  • 箱线图(Boxplot/violinplot);

  • 直方图(histogram);

  • 散点图矩阵(scatter plot matrice/splom);

  • 径向坐标可视化(radviz);

  • 平行坐标图(parallel coordinate);

  • 双标图(jointplot)


我们将绘制图表,找出特征信号(比如,模式,可分性,特征和目标之间的关系,不同特征间的关系等等)和波动(比如,噪声量,数据分布等)。


箱线图

箱线图可以看出数据的集中趋势、分布和异常点。

 
           
  1. import seaborn as sns

  2. import matplotlib.pyplot as plt

  3. sns.set_style('whitegrid')

  4. def box_viz(df):

  5.    ax = sns.boxplot(df)

  6.    plt.xticks(rotation=60)

  7.    plt.show()

  8. box_viz(concrete)


在上面的例子中,混凝土数据集的每个特征作为x轴,对于每个特征,我们得到了可视化的数据行为。箱线图包含数据的最大和最小四分位,箱子中间的黑线代表中指中位数,边缘线代表最大值和最小值(异常点除外),菱形代表异常点。在混凝土的数据集的箱线图中,我们可以看出大部分特征都是相似的尺度,除了“coarse” 和 “fine”。这意味着我们在开始模型训练之前进行特征的标准化预处理。


Violinplot提供了的传统箱线图,除了提供前面的信息,也反映相对密度估计,这对判断特征的可分性很有用。violin的两边显示了分类变量的分布,这对二分类特有用。使用sns.violinplot代替sns.boxplot即可绘制Violinplot图。


直方图

直方图显示根据每个特征的组距值放入不同的直条,并根据每个直条的频率来计算直条值。下面绘制信用卡默认支付数据集的年龄特征的直方图,代码如下:

 
           
  1. def hist_viz(df,feature):

  2.    ax = sns.distplot(df[feature])

  3.    plt.xlabel(feature)

  4.    plt.show()

  5. hist_viz(credit,'age') # We need to specify a feature vector


从上面的直方图可以看出大部分代表性的人都在40岁以下。


散点图矩阵

散点图矩阵是非常值得推荐的特征分析工具。我们在散点图中把所有特征配对(每两两特征组合画在一个矩阵中)绘制,对角一般留白或者用来显示核密度估计、直方图或者特征标注。散点图矩阵可以检查两两不同特征之间的关系。我们从散点图矩阵中找出协方差,线性关系、二次关系或者指数关系,同方差或者异方差(代表特征之间分散的程度)。下面混凝土数据集的散点图矩阵,我们可以发现 strength 和 cement 两特征间是异方差。


注意到Seaborn功能可以绘制散点图矩阵,调用sns.pairplot即可:

 
           
  1. def splom_viz(df, labels=None):

  2.    ax = sns.pairplot(df, hue=labels, diag_kind='kde', size=2)

  3.    plt.show()

  4. splom_viz(concrete)



径向坐标可视化(radviz)

径向坐标可视化是基于弹簧张力最小化算法。它把数据集的特征映射成二维目标空间单位圆中的一个点,点的位置由系在点上的特征决定。把实例投入圆的中心,特征会朝圆中此实例位置(实例对应的归一化数值)“拉”实例。


截至目前,径向坐标可视化在Seaborn中并未实现,所以我们结合Pandas的radviz函数和Seaborn的sns.color_palette来绘制:

 
           
  1. from pandas.tools.plotting import radviz

  2. def rad_viz(df,labels):

  3.    fig = radviz(df, labels, color=sns.color_palette())

  4.    plt.show()

  5. rad_viz(occupancy.ix[:,1:],'occupied') # Specify which column contains the labels


从上面房间入住数据集的径向坐标可视化,我们能看到被标注为入住和空缺的房间有些明显的分离。并且,它显示出temperature是可预测的特征之一,因为绿色的点(空缺房间)被拉向圆中的temperature。


平行坐标图

平行坐标图,类似于radviz图,是可视化数据集聚类的方法。数据点表示为可连接的线段,x轴的单位没有实际意义,每个竖直线代表一个属性。连接线段的一个集合代表一个实例。紧挨着的点聚成一类,相同颜色的线意味着好的分散性。


跟径向坐标可视化一样,我们使用Pandas函数parallel_coordinates来绘制:

 
           
  1. from pandas.tools.plotting import parallel_coordinates

  2. def pcoord_viz(df, labels):

  3.    fig = parallel_coordinates(df, labels, color=sns.color_palette())

  4.    plt.show()

  5. pcoord_viz(occupancy.ix[:,1:],'occupied') # Specify which column contains the labels


随着数据集维度的增加,即使对于专家,特征分析的挑战也会变大。坦率地讲,没有多少工具可以处理高维数据集。在Python的实现中,径向坐标可视化和radviz图对高维数据集都不是特别好扩展。


双标图(jointplot)

一般来讲,维度数目必须通过技术(比如,层次聚合,降维(例如,PCA和LDA)和维度裁剪)来减少。对于维度裁剪,可以使用散点图矩阵生成小倍数的特征。另外一种可行的办法是用双标图来检测每两两特征间的相关性。


在下面的双标图,我们检测到每个人四月份第一笔支付和九月最后一笔支付的关系。

 
           
  1. def joint_viz(feat1,feat2,df):

  2.    ax = sns.jointplot(feat1, feat2, data=df, kind='reg', size=5)

  3.    plt.xticks(rotation=60)

  4.    plt.show()

  5. joint_viz('apr_bill','sep_bill',credit)


结论

特征分析湿机器学习的关键步骤,随着潜在特征数量的增加,特征分析的复杂性也显著提高。但是,通过本文展示出特征选择也不是那么神秘。统计工具(比如,相关系数)和LASSO(下篇文章将会阐述)对鉴别最大预测特征的最小集合是非常有用的工具。利用像箱线图、直方图和散点图矩阵的工具和统计方法一起来可视化分析特征。可视化特征帮助我们洞察数据,这对开始机器学习是非常有用的。


通过对上面房间入住数据集、信用卡支付数据集和混凝土数据集的可视化,我们确定了什么是要预测的?什么特征能够用来做预测?通过特征分析的实践过程,可视化工具引导我们选择正确的机器学习算法。在本系列文章的第二部分,将继续以三种数据集来讨论可视化如何来促进模型选择的过程。 

参考:

 [1]: https://en.wikipedia.org/wiki/Anscombe%27s_quartet

  [2]: http://archive.ics.uci.edu/ml/

  [3]: https://stanford.edu/~mwaskom/software/seaborn/generated/seaborn.violinplot.html

  [4]: https://en.wikipedia.org/wiki/Homoscedasticity

  [5]: https://en.wikipedia.org/wiki/Heteroscedasticity

  [6]: http://blog.districtdatalabs.com/visual-diagnostics-for-more-informed-machine-learning-part-1

备注: 
一. 维度灾难(curse of dimensionality):这一概念是由贝尔曼(Bellman)在1961年首先提出,用来描述以下事实:许多在低维空间表现很好的算法,当输入数据是高维度时,计算就变得不可行。但在机器学习领域的意义:随着样本维度(即特征数目)的增长,正确泛化的难度会以指数级增加,究其原因是同等规模的训练集只能覆盖越来越少的输入空间比例。


侠天,专注于大数据、机器学习和数学相关的内容,并有个人公众号:bigdata_ny分享相关技术文章。

若发现以上文章有任何不妥,请联系我。




 
神机喵算 更多文章 流式处理架构的“瓶颈”:数据访问(上) Tmux简明教程 【程序开发必经之路】简明 Vim 攻略 【Spark 2.0系列】: Catalog和自定义Optimizer 【Spark 2.0系列】: Spark Session API和Dataset API
猜您喜欢 IT互联网公司为庆祝反法西斯战争举行的特色阅兵式,好看又好玩 拆轮子系列:拆 Okio Open-Falcon发布新版本了 UI设计的狂暴之路(PS篇)---利用PS帮王宝强搜集离婚证据 年轻人,你毕竟太年轻,太天真