–视觉诊断让你对机器学习了如指掌。
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 机器学习仓库:
- 从 light,humidity,CO2 等特征中预测房屋入住;
- 从 sex,education,marital status,age 和 payment history 几个特征中预测信用卡默认支付;
- 从混凝土生产年限和材料来预测混凝土抗压强度。
下面给出简单的 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 年首先提出,用来描述以下事实:许多在低维空间表现很好的算法,当输入数据是高维度时,计算就变得不可行。但在机器学习领域的意义:随着样本维度(即特征数目)的增长,正确泛化的难度会以指数级增加,究其原因是同等规模的训练集只能覆盖越来越少的输入空间比例。
评论