本文为数盟 特约作者投稿,欢迎转载,请注明出处“数盟社区”和作者
博主简介:段石石,1号店精准化推荐算法工程师,主要负责1号店用户画像构建,喜欢钻研点Machine Learning的黑科技,对Deep Learning感兴趣,喜欢玩kaggle、看9神,对数据和Machine Learning有兴趣咱们可以一起聊聊,个人博客: hacker.duanshishi.com
Spark构建分类器
在本章中,我们会了解基本的分类器以及在Spark如何使用,以及一套如何对model进行评价、调参。MLlib在这一块还是比较强大的,但是对比sklearn无论是算法种类以及配套功能还是有很大的差距。不过,据传spark最近正在修改ml,参考sklearn中的pipeline框架,将所有对数据的操作写成一个管道,在model的选择、调参、评估将更加方便,像sklearn一样,下面是一些Kaggle比赛当中的一些代码,用一个Pipeline把数据流的所有操作集合在一起,这样就很方便地进行调参。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
clf
=
pipeline
.
Pipeline
(
[
(
'union'
,
FeatureUnion
(
transformer_list
=
[
(
'cst'
,
cust_regression_vals
(
)
)
,
(
'txt1'
,
pipeline
.
Pipeline
(
[
(
's1'
,
cust_txt_col
(
key
=
'search_term'
)
)
,
(
'tfidf1'
,
tfidf
)
]
)
)
,
(
'txt2'
,
pipeline
.
Pipeline
(
[
(
's2'
,
cust_txt_col
(
key
=
'product_title'
)
)
,
(
'tfidf2'
,
tfidf
)
,
(
'tsvd2'
,
tsvd
)
]
)
)
,
(
'txt3'
,
pipeline
.
Pipeline
(
[
(
's3'
,
cust_txt_col
(
key
=
'product_description'
)
)
,
(
'tfidf3'
,
tfidf
)
,
(
'tsvd3'
,
tsvd
)
]
)
)
,
(
'txt4'
,
pipeline
.
Pipeline
(
[
(
's4'
,
cust_txt_col
(
key
=
'brand'
)
)
,
(
'tfidf4'
,
tfidf
)
,
(
'tsvd4'
,
tsvd
)
]
)
)
]
,
transformer_weights
=
{
'cst'
:
1.0
,
'txt1'
:
0.5
,
'txt2'
:
0.25
,
'txt3'
:
0.0
,
'txt4'
:
0.5
}
,
n_jobs
=
1
)
)
,
(
'xgbr'
,
xgbr
)
]
)
下面我们将分为以下几部分来聊下Spark MLlib中的分类器模块:
了解MLlib中支持的基本的分类器算法
利用Spark从原始数据当中提取特征
利用MLlib训练各种有代表性的模型
使用训练好的模型对数据进行预测
使用标准的评估手段对分类器模型来进行评估
使用一些数据处理的方法来提升model性能
探索在Spark MLlib如何进行Hyperparameter tuning,以及使用CV,来选择对应最优参数
MLlib中支持的分类器算法
Linear models
线性模型,顾名思义,在空间定一条直线来分割数据,从而来对数据进行判断,基本的model:
其中,y是目标变量,w是model的权重向量,x是输入的特征向量。这里我们可以变化f来更改model。 f确定后,一般会对应的decost函数。然后,我们在权重向量的参数空间寻优,找到cost函数值最小的一组最优参数,常用的cost函数包括logistic loss(logistic regression)、hinge loss(Linear Support Vector)以及最常见的Zero-one loss:
Logistic regression
在Logistic Regression中,f就是所谓的sigmoid函数:
Linear Support Vector Machines
在线性支持向量机中,f就是一个对等函数(?这里其实我也不知道为啥是这个名字),也就是本身:
在Linear Support Vector Machines中,我们使用的cost函数为hinge loss :
Logistic Regression和Support Vector Machines的分割线示意图:
Naive Bayes Model
Naive Bayes要求特征质检条件独立,是一种实际当中应用很多的分类方法 特征之间的属于类变量的概率相互独立,然后计算所有类变量,选择概率最大的那个C即是我们分给的类别。 一个简单的二值分类器的结果:
Decision trees
决策树的基本原理就是通过某些metrics选出最重要的属性node来对数据进行分割,然后依次进行分割,决策树是一个很流行的算法,也是一种很容易过拟合的算法,为了减少过拟合的产生,有其他ensemble的高级版,如Random Forest、GBDT,用来增强决策树算法的性能和鲁棒性 一个简单的决策树
从原始数据中提取合适的特征
在Supervised Learning中,提供LabeledPoint数据类型,
case
class
LabeledPoint
(
label
:
Double
,
features
:
Vector
)
从Kaggle StumbleUpon evergreen Dataset提取features
# 去掉train中的header信息
!
sed
1d
.
.
/
data
/
evergreen_classification
/
train
.
tsv
>
.
.
/
data
/
evergreen_classification
/
train_noheader
.
tsv
# 读入数据,以\t分割
rawData
=
sc
.
textFile
(
'../data/evergreen_classification/train_noheader.tsv'
)
records
=
rawData
.
map
(
lambda
x
:
x
.
split
(
'\t'
)
)
records
.
take
(
4
)
数据内容如图: 取其中有用字段,并做初步处理(将?取代为0.0)
1
2
3
4
5
6
7
8
9
10
11
12
13
from
pyspark
.
mllib
.
regression
import
LabeledPoint
from
pyspark
.
mllib
.
linalg
import
Vectors
trimmed
=
records
.
map
(
lambda
x
:
[
xx
.
replace
(
'\\'
,
' '
)
for
xx
in
x
]
)
# data.first()
label
=
trimmed
.
map
(
lambda
x
:
x
[
-
1
]
)
# label.take(5)
# features = trimmed.map(lambda x: x[4:-1]).map(lambda x: [ 0.0 if x=='?' else float(xx.replace("\"","")) for xx in x])
# data = LabeledPoint(label,Vectors.dense(features))
# data = trimmed.map(lambda x:(x[-1],x[4:-1])).map(lambda (x,y): (x,[ 0.0 if yy =='?' else float(yy.replace("\"","")) for yy in y])).map(LabeledPoint(label,features))
# ?号时,文本里面存的是"?"
data
=
trimmed
.
map
(
lambda
x
:
(
x
[
-
1
]
,
x
[
4
:
-
1
]
)
)
.
map
(
lambda
(
x
,
y
)
:
(
x
.
replace
(
"\""
,
""
)
,
[
0.0
if
yy
==
'\"?\"'
else
yy
.
replace
(
"\""
,
""
)
for
yy
in
y
]
)
)
.
map
(
lambda
(
x
,
y
)
:
(
int
(
x
)
,
[
float
(
yy
)
for
yy
in
y
]
)
)
.
map
(
lambda
(
x
,
y
)
:
LabeledPoint
(
x
,
Vectors
.
dense
(
y
)
)
)
# features.take(5)
data
.
take
(
5
)
这里有一个小的细节就是里面存的是”123”而非123,在做处理时需要注意,这里代码写的比较粗糙,就先这样看看,后面再做类似处理的时候回先把这些”“处理掉,scala的代码中没有出现问题,具体不知道为什么,不过这个是小问题,注意下就可以了,这里就生成了后面做分类的数据结构LabeledPoint,很简单是不是。 下面,我们意义处理下nbData,为后面做Naive Bayes的数据,因为NB中是不允许存在负数的,这个很好理解,概率是不存在负的,对吧,但是数据当中有些,这里我们先不看具体意义,直接和书上一样,把负数做0.0处理,实际当中可能需要具体了解数据库,或者可能会对原先的数据进行一个概率统计才能用相关的Naive Bayes的算法。
# naive bayes要求feature为非负features
nbdata
=
trimmed
.
map
(
lambda
x
:
(
x
[
-
1
]
,
x
[
4
:
-
1
]
)
)
.
map
(
lambda
(
x
,
y
)
:
(
int
(
x
.
replace
(
"\""
,
""
)
)
,
[
0.0
if
yy
==
'\"?\"'
else
float
(
yy
.
replace
(
"\""
,
""
)
)
for
yy
in
y
]
)
)
.
map
(
lambda
(
x
,
y
)
:
(
x
,
[
0.0
if
yy
<
0
else
yy
for