BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

可视化方法对机器学习至关重要(系列)之一

| 作者 Rebecca Bilbro 关注 1 他的粉丝 ,译者 侠天 关注 5 他的粉丝 发布于 2016年7月12日. 估计阅读时间: 27 分钟 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。

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

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

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

from sklearn.linear_model import LinearRegression
model = LogisticRegression()
model.fit(X,y)
model.predict(X)

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

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

import numpy as np

i   = np.array([
    [10.0, 8.0, 13.0, 9.0, 11.0, 14.0, 6.0, 4.0, 12.0, 7.0, 5.0],
    [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]
])

ii  = np.array([
    [10.0, 8.0, 13.0, 9.0, 11.0, 14.0, 6.0, 4.0, 12.0, 7.0, 5.0],
    [9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]
])

iii = np.array([
    [10.0, 8.0, 13.0, 9.0, 11.0, 14.0, 6.0, 4.0, 12.0, 7.0, 5.0],
    [7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73]
])

iv  = np.array([
    [8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 19.0, 8.0, 8.0, 8.0],
    [6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89]
])

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

from scipy import stats

def get_stats(twoDarray):
    print(np.mean(twoDarray[0]))
    print(np.mean(twoDarray[1]))
    print(np.var(twoDarray[0]))
    print(np.var(twoDarray[1]))
    print(np.corrcoef(twoDarray[0],twoDarray[1]))
    print(stats.linregress(twoDarray[0],twoDarray[1]))


for data in (i, ii, iii, iv):
    get_stats(data)

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

def make_plot(a, b, c, d):
    fig, ((axa, axb), (axc, axd)) =  plt.subplots(2, 2, sharex='col', sharey='row')
    for arr, ax in ((a, axa), (b, axb), (c, axc), (d, axd)):
        x = arr[0]
        y = arr[1]

        ax.scatter(x, y, c='g')
        m,b = np.polyfit(x, y, 1)
        X = np.linspace(ax.get_xlim()[0], ax.get_xlim()[1], 100)
        ax.plot(X, m*X+b, '-')
    plt.show()

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机器学习仓库

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

import os
import zipfile
import requests

OCCUPANCY = ('http://bit.ly/ddl-occupancy-dataset', 'occupancy.zip')
CREDIT    = ('http://bit.ly/ddl-credit-dataset', 'credit.xls')
CONCRETE  = ('http://bit.ly/ddl-concrete-data', 'concrete.xls')

def download_data(url, name, path='data'):
    if not os.path.exists(path):
        os.mkdir(path)

    response = requests.get(url)
    with open(os.path.join(path, name), 'w') as f:
        f.write(response.content)


def download_all(path='data'):
    for href, name in (OCCUPANCY, CREDIT, CONCRETE):
        download_data(href, name, path)

    # Extract the occupancy zip data
    z = zipfile.ZipFile(os.path.join(path, 'occupancy.zip'))
    z.extractall(os.path.join(path, 'occupancy'))

path='data'
download_all(path)

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

特征分析和选择

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

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

import pandas as pd

# Load the room occupancy dataset
occupancy = os.path.join('data','occupancy_data','datatraining.txt')
occupancy = pd.read_csv(occupancy, sep=',')
occupancy.columns = [
    'date', 'temp', 'humid', 'light', 'co2', 'hratio', 'occupied'
]

# View the occupancy details
print(occupancy.head())
print(occupancy.describe())

# Load the credit card default dataset
credit = os.path.join('data','credit.xls')
credit = pd.read_excel(credit, header=1)
credit.columns = [
    'id', 'limit', 'sex', 'edu', 'married', 'age', 'apr_delay', 'may_delay',
    'jun_delay', 'jul_delay', 'aug_delay', 'sep_delay', 'apr_bill', 'may_bill',
    'jun_bill', 'jul_bill', 'aug_bill', 'sep_bill', 'apr_pay', 'may_pay', 'jun_pay',
    'jul_pay', 'aug_pay', 'sep_pay', 'default'
]

# View the credit details
print(credit.head())
print(credit.describe())

# Load the concrete compression data set
concrete   = pd.read_excel(os.path.join('data','concrete.xls'))
concrete.columns = [
    'cement', 'slag', 'ash', 'water', 'splast',
    'coarse', 'fine', 'age', 'strength'
]

# View the concrete details
print(concrete.head())
print(concrete.describe())

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

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

  • 箱线图(Boxplot/violinplot);
  • 直方图(histogram);
  • 散点图矩阵(scatter plot matrice/splom);
  • 径向坐标可视化(radviz);
  • 平行坐标图(parallel coordinate);
  • 双标图(jointplot)

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

箱线图

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

import seaborn as sns
import matplotlib.pyplot as plt

sns.set_style('whitegrid')

def box_viz(df):
    ax = sns.boxplot(df)
    plt.xticks(rotation=60)
    plt.show()

box_viz(concrete)

(点击放大图像)

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

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

直方图

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

def hist_viz(df,feature):
    ax = sns.distplot(df[feature])
    plt.xlabel(feature)
    plt.show()

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

(点击放大图像)

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

散点图矩阵

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

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

def splom_viz(df, labels=None):
    ax = sns.pairplot(df, hue=labels, diag_kind='kde', size=2)
    plt.show()

splom_viz(concrete)

(点击放大图像)

径向坐标可视化(radviz)

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

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

from pandas.tools.plotting import radviz

def rad_viz(df,labels):
    fig = radviz(df, labels, color=sns.color_palette())
    plt.show()

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

(点击放大图像)

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

平行坐标图

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

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

from pandas.tools.plotting import parallel_coordinates

def pcoord_viz(df, labels):
    fig = parallel_coordinates(df, labels, color=sns.color_palette())
    plt.show()

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

(点击放大图像)

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

双标图(jointplot)

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

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

def joint_viz(feat1,feat2,df):
    ax = sns.jointplot(feat1, feat2, data=df, kind='reg', size=5)
    plt.xticks(rotation=60)
    plt.show()

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

(点击放大图像)

结论

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

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

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

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

查看英文原文:Visual Diagnostics for More Informed Machine Learning: Part 1

备注:

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

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT