数据挖掘比赛通用框架

简介:

0?wx_fmt=jpeg

 ◆ ◆ 

前言


Kaggle上有篇名为「Approaching (Almost) Any Machine Learning Problem」的博客(点击底部阅读原文),作者是Kaggle比赛的专业户,博客是他参加Kaggle比赛的经验总结。在进入正题前随便扯几句:


本文并非原博客的翻译版,而是90%的原创,是在原博客基础上融合自己的经验,重写了大部分章节和代码。所以当你看到本文跟原博客差别很大时,请不要怀疑人生 ;-P

原博客题目直译过来是『解决(几乎)任一机器学习问题的方法』,但原博客内容更偏数据挖掘之『术』而非机器学习之『道』,因为讲解了很多实际操作的trick和代码,所以我给本文取名为『数据挖掘比赛通用框架』。为简化描述,后续用ML指代机器学习DM指代数据挖掘

本文可以看做是一篇科普性质的文章,内容简单基础,关键在于结合实际实践这些想法,所谓 practice makes perfect.

本人连续多天利用数个晚上写成此文,请尊重原创,转载请注明。也希望本文能给各位带来收获,如有疏漏,欢迎后台留言积极指正,先行谢过


◆ ◆ 

背景


DM流程通常分两个阶段


Step1. 数据清洗,数据格式调整

Step2. 特征构建,模型选择,效果评估


Step1.是整个流程中最耗时的,这点想必大家早有耳闻,DM界有句名言 garbage in ,garbage out ,可见清洗数据非常重要。从我的经验看,这部分工作跟实际处理的业务问题关系很大,比较dirty,也没有统一流程,所以本文重点放在Step2.

◆ ◆ 

前期准备


1、数据变换


先把原始数据通过一定变换,变成通用的多列数据类型,作为ML模型的输入,也就是上面的Step1。用X代表样本及其特征集合,y代表样本标签集合,整个流程如下:



0?wx_fmt=png


2、问题分类

根据标签y的不同,可以把DM问题分为以下几类:

  • 二分类问题(这种问题在工业界最为常见,比如广告点击率预估、推荐系统购买行为预测),此时y只有一维,取值只有两个(比如0-1),每个样本有唯一的标签。比如预测广告是否会被用户点击;用户是否会购买某种商品

  • 多分类问题(比如微博用户情感分析、用户对理财产品偏好性分析),通常此时y有多维,每维代表一个类标签,取值只有两个(比如0-1),每个样本有唯一的标签;当然,y也可以只有一维,取值有多个,每个值代表一个类标签。比如通过微博分析出用户情感属于喜怒哀乐等哪类;将理财产品的用户群体分为偏好型/温和型/厌恶型

  • 多标签问题(比如音乐的标签划分),y有多维,跟多分类的区别在于,样本可以同时属于多个标签。作为一枚钢琴爱好者,这里以钢琴作品举例,假设标签集合为{独奏,协奏,浪漫主义,印象主义},最爱之一的德彪西「月光」无疑属于{独奏,印象主义},朗总成名作柴一则可归为{协奏,浪漫主义},云迪家喻户晓的肖邦夜曲是{独奏,浪漫主义},而中国特色的「保卫黄河」可归为{协奏}

  • 单回归问题(比如股价预测),y只有一维,取值为连续值。比如预测阿里明天的股价

  • 多回归问题(比如天气预测),y有多维,取值连续。比如预测明天的气温、空气湿度、降雨量

3、评价指标

预测结果的好坏需要用一些指标来衡量,通常不同类型的DM问题有不同的评价指标。对于二分类问题,很多时候类别本身不均衡(比如正样本很多负样本极少),所以我们通常用AUC值——即ROC曲线下的面积——来评价二分类结果;在多分类或者多标签问题中,我们通常选取评价指标为交叉熵(cross-entropy)或者log损失(log loss);对于回归问题,则可以选用MSE(mean square error)

4、 工具

我跟原博客作者一样,提倡使用python解决DM问题,因为python的第三方库非常齐全,以下是常见的、用于DM问题的python库:

  • pandas: 仿照了R语言的数据结构、数据操作,一般用来做数据预处理,特征工程,其DataFrame数据格式用起来相当便利

  • scikit-learn: 家喻户晓的ML库,各种聚类、分类、回归模型等,也可以用来做预处理

  • xgboost: 陈天奇大神的杰作,改进了传统的GBDT模型,在底层用一些trick加速模型训练,非常值得一试,可以取代其他ML库里的GBDT系列模型 (很早就听说过这个碉堡的库,但一直没有上手实践,实在汗颜…后面我会结合GBDT做特征工程,实践下效果,发布到公众号)

  • keras: 神经网络相关的库,可以选择基于tensorflow或theano,赶脚很强大,我也是刚接触

  • matplotlib: 作图必备,语言风格跟MATLAB很像,很好上手

  • tpdm: 我没听过,原作者提到的,感兴趣的童鞋可以了解下

5、开发环境

这里我补充说一下python开发环境和上面几个库的安装方法。首先我跟原作者一样,因为追求自(装)由(逼),所以不用python IDE(比如Anaconda, Pycharm),当然,装IDE可能省很多事情,个人建议安装Pycharm。然后我自己的python开发环境(纯属个人习惯,仅供参考):

  • windows: notepad++及其插件nppexec/explorer,结合我昨天发布的『一个神奇的脚本,一键运行各类程序』,里面的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位的库大多没有官方版本,具体安装方式见我之前写过的一篇文章『在Windows下安装64位Python及数据挖掘相关库』(后续我会完善该文,但只发送给指定分组,具体见文末Bonus)。大多数库的安装都类似,但xgboost稍微复杂些,不能直接pip install,而是要装VS来编译其中相关文件,再安装,遇到问题可以微信我。另外tensroflow目前没有windows版本

  • mac[推荐]: 最新的python2.7一般都自带pip,所以装好python后,直接在terminal中 pip install 相关库就可以了,注意库的依赖关系,一般先安装numpy,scipy,matplotlib,再装其他库

  • linux[推荐]: 基本跟mac类似

◆ ◆ 

DM问题框架


终于到了最核心的部分,原作者总结了一个他参加各类DM比赛常用的ML流程图,真是一图胜千言


0?wx_fmt=png


这里我擅自补充一下,这张图看着眼花缭乱,其实就两点,这两点也是DM比赛中最核心的

两点

特征工程(包括各种离散化、组合、选择)

模型选择、模型融合(即ensemble)


能把这两点做好,实属不易,但其实在工业界,特征工程和模型融合是否需要做到极致,是要看具体问题的。有些业务的数据维度本身就很稀少,并不足以支撑庞大的特征体系;有些业务需要很强的可解释性(比如金融领域),于是很多模型不能直接用;有些业务则要系统的实时性和稳定性,过于复杂的ensemble虽然能提升一点指标,但也许得不偿失。

上图当中的粉色部分是最常用的一些步骤,简单梳理一下:先确定DM问题的类型,然后对数据集划分,接着对常见的数值变量和类别变量做相应处理,可以进行特征选择,最后选择合适的模型做预测,评估模型并输出结果。下面将详细展开。

1. 问题定义

首先搞清楚要解决的问题属于哪一类,结合上节所讲,我们一般通过观察y标签类来定义DM问题的类型。

2. 数据集划分

在明确了问题的分类后,我们将对数据集划分成训练集(Training Data)和验证集(Validation Data)(补充:很多时候还要划分出测试集(Test Data),先用训练集验证集的交叉验证来寻找模型的最优超参数,模型调优完毕后,最终用测试集来评估模型最终效果,具体参考我之前在公众号发布的『新手数据挖掘中的几个常见误区』第二节)。划分方式如下:
640?wx_fmt=png

这里我用自己本地的一个小数据集(名为toy_data.txt)做展示,获取方式见文末Bonus,加载以上小数据集的代码如下:

import pandas as pd
df = pd.read_csv("toy_data.txt",sep = "\t")
df.head()

运行结果:
640?wx_fmt=png

最后一个字段Label就是我们要预测的y,在我的数据集里取值0或1,所以是一个二分类问题。

对于分类问题,要根据标签来划分数据集,比如每种标签采样多少,这样才能保证训练集跟验证集的样本标签分布接近,另外采样方式也不限于随机采样,可以根据实际业务问题选择合适的采样方式。这里我们可以借助scikit-learn来实现分层的K折交叉验证,代码如下

X = df.ix[:,0:-1]
y = df.ix[:,-1]
from sklearn.cross_validation import StratifiedKFold
kf = StratifiedKFold(y,3) # 三折交叉验证

用以下代码验证一下训练集和验证集中的正样本的占比:

idx_train, idx_valid = next(iter(kf))
print float(sum(y[idx_train]))/len(idx_train)
print float(sum(y[idx_valid]))/len(idx_valid)

结果为0.69713 0.69565,两者非常接近。
注意,不太推荐使用
iter(kf),这里只是为了展示标签分布,具体我会在本文第五节『实战』中介绍如何高效地使用交叉验证。

如果是回归问题,则不存在分类问题中类别标签分布不均的情况,所以我们只需采用普通的K折交叉验证即可:

from sklearn.cross_validation import KFold
kf = KFold()

3. 特征工程

毫不夸张地说,特征工程是DM重要的一环,也是决定DM比赛的关键因素。纵观DM比赛,几年间已由追求模型是否fancy转向无尽的特征工程,主要得益于越来越标准化的ML模型,以及更好的计算能力。

特征工程可以做的很复杂很庞大,但受限于本人目前的水平,这里只结合原博客内容讲解一些最基本(也是最经典)的处理方法。

3.1. 处理类别变量

类别变量(categorial data)是一种常见的变量,在我之前写的『新手数据挖掘中的几个常见误区』 一文的第三节中讨论过为何要对类别变量编码

toy_data当中,字段Continent, Country, Product, Brand, TreeID, Industry, Saler都可以看做是类别变量。处理类别变量一般是先标签化,然后再二值化编码。标签化的目的是将字段的原始值(如字符串、不连续的数字等)转换成连续的整数值,再对整数值二值化编码,如果原始值是整数,则直接二值化即可

我们拿toy_data前几个样本的Continent字段举例,对其进行编码:

mapper = skp.DataFrameMapper([
('Continent', sklearn.preprocessing.LabelBinarizer()),
])
tempX = df[['Continent']].head()
print tempX
print mapper.fit_transform(tempX.copy())

运行结果如下

640?wx_fmt=png

可以看到,原来的一列Continent字段变成了三列,分别代表[ 'AM', 'EP', 'LA' ],取值1表明是,取值0表明否。这就是常说的one-hot编码。如果类别变量的取值是整数,则直接用sklearn.preprocessing.OneHotEncoder()即可,把上面代码中LabelBinarizer()替换掉

注意我们必须将对训练集上的变换原封不动的作用到测试集,而不能重新对测试集的数据做变换(详见我之前写的『新手数据挖掘中的几个常见误区』第一节)。

3.2. 处理数值变量

一般而言,数值变量不用做太多处理,只需做正规化(normalization)和标准化(standardization)即可,分别对应scikit-learn中的NormalizerStandardScaler。不过对于稀疏变量,在做标准化的时候要注意,选择不去均值。

其实数值型变量最好也进行离散化,离散手段从基本的等距离散法、按分隔点人为指定,到聚类、输入树模型等,手段很多,在此不详细展开,我会在后续文章中提及。

3.3. 处理文本变量

文本在实际问题中很常见,比如用户评论、新闻摘要、视频弹幕等等。我们用的toy_data不包含文本变量,所以这里我参考了scikit-learn的文档,一个小的corpus作为我们的训练数据集。

corpus = [
'This is the first document.',
'This is the second second document.',
'And the third one.',
'Is this the first document?',
]

corpus有四句话,可以看做是四个样本。接下来我们先用一个简单的方法处理文本变量——统计corpus中每个词出现次数,代码如下:

from sklearn.feature_extraction.text import CountVectorizer
vectorizer1 = CountVectorizer(min_df=1)
temp1 = vectorizer1.fit_transform(corpus)
print vectorizer1.get_feature_names()
print temp1.toarray() # temp1是sparse类型, 转换成ndarray方便查看

运行结果:

640?wx_fmt=png

第一行是corpus中所有词,下面的ndarray每行代表该词在该样本中出现次数,比如第2行第6列的2代表second这个词在第二句话中出现了2次。一般我们不会直接用这个结果,而是会将每行归一化之类。

这种处理方式简单粗暴,没有考虑词与词之间的关系。我们改进一下这个方法,除了考虑单个词之外,还考虑corpus中成对出现的词(类似NLP里n-gram的 bi-gram,具体请自行Google),代码如下

vectorizer2 = CountVectorizer(ngram_range=(1, 2))
temp2 = vectorizer2.fit_transform(corpus)
print vectorizer2.get_feature_names()
print temp2.toarray()

运行结果:

640?wx_fmt=png

然而,这还不够,像a is this这类的助词、介词等,词频将非常高(在NLP中又叫停止词 stop word),所以需要减小他们的权重。一种做法是,不再简单统计该词在文档中出现的词频,而且还要统计 出现该词的文档的占比,这在NLP中叫tfidf。说的有点绕,具体到我们的例子中可以写成如下表达式:

某单词x的tfidf = x在一个样本中出现的次数/出现x的文档占比

分子即tf,分母即1/idf,有时需要用log sqrt之类的函数作用在tf或者 1/idf上,以减弱某项的影响。同样,我们可以:

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer3 = TfidfVectorizer(ngram_range=(1, 2))
temp3 = vectorizer3.fit_transform(corpus)
print vectorizer3.get_feature_names()
print temp3.toarray()

运行结果:

640?wx_fmt=png

仔细观察不难发现,is this这类的停止词在变换后数值变小了。

!!注意!!跟处理类别变量、数值变量一样,我们在处理文本变量时,必须将训练集上的变换方式原封不动地作用到验证集或测试集上,而不能重新对验证集或者测试集做变换。比如在得到上面的vectorizer3后,我们将其作用在一个新的样本 ['a new sentence']上,代码如下

print vectorizer1.transform(['a new sentence']).toarray()

我们可以看到,结果是 [[0 0 0 0 0 0 0 0 0]],因为这个样本里的三个词从未出现在训练集corpus中,这是正确的结果!

为了方便将变换作用在未来的测试集,我们可以先把vectorizer3pickle保存到本地,用的时候再load,保存方式如下:

import cPickle as pickle
pickle.dump(vectorizer3, open('vectorizer3.pkl','w'))

用的时候再 vectorizer = pickle.load(open('vectorizer3.pkl','r'))即可。

3.4. 特征融合

ToDo 区别对待稠密特征和稀疏特征,

3.4. 特征降维

ToDo PCA等

3.5. 特征选择

ToDo

4. 模型选择和模型融合

ToDo

原文发布时间为:2016-08-16

本文来自云栖社区合作伙伴“大数据文摘”,了解相关信息可以关注“BigDataDigest”微信公众号

相关文章
|
机器学习/深度学习 人工智能 运维
【数据挖掘】数据挖掘简介 ( 6 个常用功能 | 数据挖掘结果判断 | 数据挖掘学习框架 | 数据挖掘分类 )
【数据挖掘】数据挖掘简介 ( 6 个常用功能 | 数据挖掘结果判断 | 数据挖掘学习框架 | 数据挖掘分类 )
659 0
|
数据挖掘 Python
Crawler之Scrapy:数据挖掘必备的scrapy框架之最完整爬取网页内容攻略
Crawler之Scrapy:数据挖掘必备的scrapy框架之最完整爬取网页内容攻略
Crawler之Scrapy:数据挖掘必备的scrapy框架之最完整爬取网页内容攻略