◆ ◆ ◆
前言
kaggle上有篇名为「approaching (almost) any machine learning problem」的博客(点击底部<code>阅读原文</code>),作者是kaggle比赛的专业户,博客是他参加kaggle比赛的经验总结。在进入正题前随便扯几句:
本文并非原博客的翻译版,而是90%的原创,是在原博客基础上融合自己的经验,重写了大部分章节和代码。所以当你看到本文跟原博客差别很大时,请不要怀疑人生 ;-p 原博客题目直译过来是『解决(几乎)任一机器学习问题的方法』,但原博客内容更偏数据挖掘之『术』而非机器学习之『道』,因为讲解了很多实际操作的trick和代码,所以我给本文取名为『数据挖掘比赛通用框架』。为简化描述,后续用<code>ml</code>指代<code>机器学习</code>,<code>dm</code>指代<code>数据挖掘</code> 本文可以看做是一篇科普性质的文章,内容简单基础,关键在于结合实际实践这些想法,所谓 practice makes perfect. 本人连续多天利用数个晚上写成此文,请尊重原创,转载请注明。也希望本文能给各位带来收获,如有疏漏,欢迎后台留言积极指正,先行谢过
◆ ◆ ◆
背景
dm流程通常分两个阶段
step1. 数据清洗,数据格式调整 step2. 特征构建,模型选择,效果评估
step1.是整个流程中最耗时的,这点想必大家早有耳闻,dm界有句名言 garbage in ,garbage out ,可见清洗数据非常重要。从我的经验看,这部分工作跟实际处理的业务问题关系很大,比较dirty,也没有统一流程,所以本文重点放在step2.
前期准备
1、数据变换
先把原始数据通过一定变换,变成通用的多列数据类型,作为ml模型的输入,也就是上面的step1。用x代表样本及其特征集合,y代表样本标签集合,整个流程如下:
根据标签y的不同,可以把dm问题分为以下几类:
二分类问题(这种问题在工业界最为常见,比如广告点击率预估、推荐系统购买行为预测),此时y只有一维,取值只有两个(比如0-1),每个样本有唯一的标签。比如预测广告是否会被用户点击;用户是否会购买某种商品
多分类问题(比如微博用户情感分析、用户对理财产品偏好性分析),通常此时y有多维,每维代表一个类标签,取值只有两个(比如0-1),每个样本有唯一的标签;当然,y也可以只有一维,取值有多个,每个值代表一个类标签。比如通过微博分析出用户情感属于喜怒哀乐等哪类;将理财产品的用户群体分为偏好型/温和型/厌恶型
多标签问题(比如音乐的标签划分),y有多维,跟多分类的区别在于,样本可以同时属于多个标签。作为一枚钢琴爱好者,这里以钢琴作品举例,假设标签集合为{独奏,协奏,浪漫主义,印象主义},最爱之一的德彪西「月光」无疑属于{独奏,印象主义},朗总成名作柴一则可归为{协奏,浪漫主义},云迪家喻户晓的肖邦夜曲是{独奏,浪漫主义},而中国特色的「保卫黄河」可归为{协奏}
单回归问题(比如股价预测),y只有一维,取值为连续值。比如预测阿里明天的股价
多回归问题(比如天气预测),y有多维,取值连续。比如预测明天的气温、空气湿度、降雨量
预测结果的好坏需要用一些指标来衡量,通常不同类型的dm问题有不同的评价指标。对于二分类问题,很多时候类别本身不均衡(比如正样本很多负样本极少),所以我们通常用auc值——即roc曲线下的面积——来评价二分类结果;在多分类或者多标签问题中,我们通常选取评价指标为交叉熵(cross-entropy)或者log损失(log loss);对于回归问题,则可以选用mse(mean square error)
4、 工具
我跟原博客作者一样,提倡使用<code>python</code>解决dm问题,因为python的第三方库非常齐全,以下是常见的、用于dm问题的python库:
pandas: 仿照了r语言的数据结构、数据操作,一般用来做数据预处理,特征工程,其dataframe数据格式用起来相当便利
scikit-learn: 家喻户晓的ml库,各种聚类、分类、回归模型等,也可以用来做预处理
xgboost: 陈天奇大神的杰作,改进了传统的gbdt模型,在底层用一些trick加速模型训练,非常值得一试,可以取代其他ml库里的gbdt系列模型 (很早就听说过这个碉堡的库,但一直没有上手实践,实在汗颜…后面我会结合gbdt做特征工程,实践下效果,发布到公众号)
keras: 神经网络相关的库,可以选择基于tensorflow或theano,赶脚很强大,我也是刚接触
matplotlib: 作图必备,语言风格跟matlab很像,很好上手
tpdm: 我没听过,原作者提到的,感兴趣的童鞋可以了解下
这里我补充说一下python开发环境和上面几个库的安装方法。首先我跟原作者一样,因为追求自(装)由(逼),所以不用python ide(比如anaconda, pycharm),当然,装ide可能省很多事情,个人建议安装pycharm。然后我自己的python开发环境(纯属个人习惯,仅供参考):
windows: notepad++及其插件nppexec/explorer,结合我昨天发布的<code>『一个神奇的脚本,一键运行各类程序』</code>,里面的nppexec脚本可一键执行python。以及linux风格的shell: git bash (git bash是基于msys的,跟cygwin略有不同)
mac: sublime及其插件package control/anaconda,以及iterm2,或者自带的terminal。(sublime中import某些python库,比如matplotlib/sklearn/tensorflow会出点bug,需要修改下环境变量啥的,遇到相关问题可以微信我,尽量帮你解决)
linux: vim(因为我一般在命令行模式下开发)。如果是界面linux,应该可以有其他选择
另外,jupyter notebook(前身是ipython notebook)是个好东西,可以逐步执行python代码片段,不依赖于平台,可在浏览器中打开,非常适合学习过程中练手。
再说库的安装,首先强烈建议安装64位python2.7,然后针对不同操作系统:
windows[不推荐]: 略蛋疼,64位的库大多没有官方版本,具体安装方式见我之前写过的一篇文章<code>『在windows下安装64位python及数据挖掘相关库』</code>(后续我会完善该文,但只发送给指定分组,具体见文末bonus)。大多数库的安装都类似,但xgboost稍微复杂些,不能直接pip install,而是要装vs来编译其中相关文件,再安装,遇到问题可以微信我。另外tensroflow目前没有windows版本
mac[推荐]: 最新的python2.7一般都自带pip,所以装好python后,直接在terminal中 pip install 相关库就可以了,注意库的依赖关系,一般先安装numpy,scipy,matplotlib,再装其他库
linux[推荐]: 基本跟mac类似
dm问题框架
终于到了最核心的部分,原作者总结了一个他参加各类dm比赛常用的ml流程图,真是一图胜千言
这里我擅自补充一下,这张图看着眼花缭乱,其实就两点,这两点也是dm比赛中最核心的
两点:
特征工程(包括各种离散化、组合、选择) 模型选择、模型融合(即ensemble)
能把这两点做好,实属不易,但其实在工业界,特征工程和模型融合是否需要做到极致,是要看具体问题的。有些业务的数据维度本身就很稀少,并不足以支撑庞大的特征体系;有些业务需要很强的可解释性(比如金融领域),于是很多模型不能直接用;有些业务则要系统的实时性和稳定性,过于复杂的ensemble虽然能提升一点指标,但也许得不偿失。
上图当中的粉色部分是最常用的一些步骤,简单梳理一下:先确定dm问题的类型,然后对数据集划分,接着对常见的数值变量和类别变量做相应处理,可以进行特征选择,最后选择合适的模型做预测,评估模型并输出结果。下面将详细展开。
1. 问题定义
首先搞清楚要解决的问题属于哪一类,结合上节所讲,我们一般通过观察y标签类来定义dm问题的类型。
在明确了问题的分类后,我们将对数据集划分成训练集(training data)和验证集(validation data)(补充:很多时候还要划分出测试集(test data),先用训练集验证集的交叉验证来寻找模型的最优超参数,模型调优完毕后,最终用测试集来评估模型最终效果,具体参考我之前在公众号发布的<code>『新手数据挖掘中的几个常见误区』</code>第二节)。划分方式如下:
这里我用自己本地的一个小数据集(名为<code>toy_data.txt</code>)做展示,获取方式见文末bonus,加载以上小数据集的代码如下:
运行结果:
最后一个字段<code>label</code>就是我们要预测的y,在我的数据集里取值0或1,所以是一个二分类问题。
对于分类问题,要根据标签来划分数据集,比如每种标签采样多少,这样才能保证训练集跟验证集的样本标签分布接近,另外采样方式也不限于随机采样,可以根据实际业务问题选择合适的采样方式。这里我们可以借助scikit-learn来实现分层的k折交叉验证,代码如下
用以下代码验证一下训练集和验证集中的正样本的占比:
结果为<code>0.69713 0.69565</code>,两者非常接近。
注意,不太推荐使用<code>iter(kf)</code>,这里只是为了展示标签分布,具体我会在本文第五节『实战』中介绍如何高效地使用交叉验证。
如果是回归问题,则不存在分类问题中类别标签分布不均的情况,所以我们只需采用普通的k折交叉验证即可:
毫不夸张地说,特征工程是dm重要的一环,也是决定dm比赛的关键因素。纵观dm比赛,几年间已由追求模型是否fancy转向无尽的特征工程,主要得益于越来越标准化的ml模型,以及更好的计算能力。
特征工程可以做的很复杂很庞大,但受限于本人目前的水平,这里只结合原博客内容讲解一些最基本(也是最经典)的处理方法。
类别变量(categorial data)是一种常见的变量,在我之前写的<code>『新手数据挖掘中的几个常见误区』</code> 一文的第三节中讨论过为何要对类别变量编码
在<code>toy_data</code>当中,字段<code>continent, country, product, brand, treeid, industry, saler</code>都可以看做是类别变量。处理类别变量一般是先标签化,然后再二值化编码。标签化的目的是将字段的原始值(如字符串、不连续的数字等)转换成连续的整数值,再对整数值二值化编码,如果原始值是整数,则直接二值化即可
我们拿<code>toy_data</code>前几个样本的<code>continent</code>字段举例,对其进行编码:
运行结果如下
可以看到,原来的一列<code>continent</code>字段变成了三列,分别代表<code>[ 'am', 'ep', 'la' ]</code>,取值1表明是,取值0表明否。这就是常说的one-hot编码。如果类别变量的取值是整数,则直接用<code>sklearn.preprocessing.onehotencoder()</code>即可,把上面代码中<code>labelbinarizer()</code>替换掉
注意我们必须将对训练集上的变换原封不动的作用到测试集,而不能重新对测试集的数据做变换(详见我之前写的<code>『新手数据挖掘中的几个常见误区』</code>第一节)。
一般而言,数值变量不用做太多处理,只需做正规化(normalization)和标准化(standardization)即可,分别对应scikit-learn中的<code>normalizer</code>和<code>standardscaler</code>。不过对于稀疏变量,在做标准化的时候要注意,选择不去均值。
其实数值型变量最好也进行离散化,离散手段从基本的等距离散法、按分隔点人为指定,到聚类、输入树模型等,手段很多,在此不详细展开,我会在后续文章中提及。
文本在实际问题中很常见,比如用户评论、新闻摘要、视频弹幕等等。我们用的<code>toy_data</code>不包含文本变量,所以这里我参考了scikit-learn的文档,一个小的corpus作为我们的训练数据集。
corpus有四句话,可以看做是四个样本。接下来我们先用一个简单的方法处理文本变量——统计corpus中每个词出现次数,代码如下:
第一行是corpus中所有词,下面的ndarray每行代表该词在该样本中出现次数,比如第2行第6列的<code>2</code>代表<code>second</code>这个词在第二句话中出现了2次。一般我们不会直接用这个结果,而是会将每行归一化之类。
这种处理方式简单粗暴,没有考虑词与词之间的关系。我们改进一下这个方法,除了考虑单个词之外,还考虑corpus中成对出现的词(类似nlp里n-gram的 bi-gram,具体请自行google),代码如下
然而,这还不够,像<code>a is this</code>这类的助词、介词等,词频将非常高(在nlp中又叫停止词 stop word),所以需要减小他们的权重。一种做法是,不再简单统计该词在文档中出现的词频,而且还要统计 出现该词的文档的占比,这在nlp中叫tfidf。说的有点绕,具体到我们的例子中可以写成如下表达式:
分子即tf,分母即1/idf,有时需要用<code>log sqrt</code>之类的函数作用在tf或者 1/idf上,以减弱某项的影响。同样,我们可以:
仔细观察不难发现,<code>is this</code>这类的停止词在变换后数值变小了。
!!注意!!跟处理类别变量、数值变量一样,我们在处理文本变量时,必须将训练集上的变换方式原封不动地作用到验证集或测试集上,而不能重新对验证集或者测试集做变换。比如在得到上面的<code>vectorizer3</code>后,我们将其作用在一个新的样本 <code>['a new sentence']</code>上,代码如下
我们可以看到,结果是 <code>[[0 0 0 0 0 0 0 0 0]]</code>,因为这个样本里的三个词从未出现在训练集corpus中,这是正确的结果!
为了方便将变换作用在未来的测试集,我们可以先把<code>vectorizer3</code>用<code>pickle</code>保存到本地,用的时候再load,保存方式如下:
用的时候再 <code>vectorizer = pickle.load(open('vectorizer3.pkl','r'))</code>即可。
todo 区别对待稠密特征和稀疏特征,
todo pca等
todo
原文发布时间为:2016-08-16
本文来自云栖社区合作伙伴“大数据文摘”,了解相关信息可以关注“bigdatadigest”微信公众号