分类算法是机器学习中的一个重点,也是人们常说的“有监督的学习”。这是一种利用一系列已知类别的样本来对模型进行训练调整分类器的参数,使其达到所要求性能的过程,也成为监督训练或有教师学习。
#决策树
决策树是分类时第一种常用的方式,这种方式几乎可以无师自通。
下面举一个例子:
假如某大龄女青年在相亲网站上进行海选,因为资源太多而自己精力有限,所以肯定是要进行相亲决策的,如下图所示:
- 相亲决策树:
可以看出,这个人的决策策略为:
-
年龄35以上的直接拉黑,年龄35以下可以考录见面。
-
年收入20万元以上的属于比较有能力的高质量男性,其他条件可以放宽。
-
如果学历为硕士以上,身高不够175cm也可以;如果学历是硕士及以下,那么身高必须在175cm以上。
-
如果年收入在20万元以下,考虑其他因素。
-
如果学历为硕士以上,身高不够180cm也可以;如果学历是硕士及以下,那么身高必须在180cm以上。
本例是根据样本——对该大龄女青年打招呼的男性的情况从树根开始走决策树,最后决定是相亲还是不相亲。
这种决策树的归纳过程是一个“自底而上”的认识过程。
例如她的相亲策略不是由她直接口述得来的,而是她在网站上不断地相亲,通过计算机学习和归纳总结出来的。
况且更多时候我们是没有机会听别人陈述的,更多的是在观察和总结中认识世界,而且这种归纳总结出来的过程可能比陈述的过程更为客观和准确——所谓察其言不如观其行。
下面继续分析上述相亲的例子,由她之前的相亲信息表归纳出她的相亲策略。
- 相亲信息表:
网站ID | 年龄(岁) | 身高(cm) | 年收入(万元) | 学历 | 是否相亲 |
---|---|---|---|---|---|
XXXXXX | 25 | 179 | 15 | 大专 | N |
XXXXXX | 33 | 190 | 19 | 大专 | Y |
XXXXXX | 28 | 180 | 18 | 硕士 | Y |
XXXXXX | 25 | 178 | 18 | 硕士 | Y |
XXXXXX | 46 | 177 | 100 | 硕士 | N |
XXXXXX | 40 | 170 | 70 | 本科 | N |
XXXXXX | 34 | 174 | 20 | 硕士 | Y |
XXXXXX | 36 | 181 | 55 | 本科 | N |
XXXXXX | 35 | 170 | 25 | 硕士 | Y |
XXXXXX | 30 | 180 | 35 | 本科 | Y |
XXXXXX | 28 | 174 | 30 | 本科 | N |
XXXXXX | 29 | 176 | 36 | 本科 | Y |
这里我们考虑一个问题,为什么上图中以年龄与35岁相比作为树根。试想一下,其他的数据项能不能做树根?另外,为什么要以35岁作为树根分裂的条件呢,为什么不是34岁或36岁?对于是否存在一种比较科学或客观的方法能够找到这个描述最简洁的方式,下面引出一个新概念——信息增益。
##信息增益
首先这里引用信息熵的概念。
信息熵是用来描述信息混乱程度或者说确定程度的一个值。混乱程度越高,熵越大;混乱程度越低,熵越小。
整个样本集合的熵如下:
I n f o = − ∑ i = 1 m p i l o g 2 p i \LARGE Info=-\sum_{i=1}^mp_ilog_2p_i Info=−∑i=1mpilog2pi
关于信息熵的具体解释参见:大数据与机器学习 入门篇
在这里,m的数量就是最后分类(决策)的种类,在本例中m=2——要么见面要么不见面。 p i p_i pi是这个决策项产生的概率。本例中有两个决策项,一个是N(不相亲),概率为 5 12 \frac{5}{12} 125,一个是Y(相亲),概率为 7 12 \frac{7}{12} 127,这个概率就是从拿到的完整的相亲记录里得到的结论,信息熵为:
− ( 5 12 l o g 2 5 12 + 7 12 l o g 2 7 12 ) = 0.98 b i t \LARGE -\bigl(\frac{5}{12}log_2\frac{5}{12}+\frac{7}{12}log_2\frac{7}{12}\bigr)=0.98bit −(125log2125+127log2127)=0.98bit
这里的信息熵也叫期望信息。
期望信息的含义就是:我们要得到这些信息,才能消除这个问题的不确定性。即要做出最后决策需要0.98bit的信息。从熵的定义也不难看出,熵越大说明信息的混乱程度越高,需要更多的信息才能做好决策。
而我们现在要挑选的“树根”,挑选的原则就是要尽可能地消除不确定性,最好做出“一刀切”,即一刀下去就把两个类分清楚。
接下来要考虑的事情就是,以哪个字段做树根能够使得消除信息混杂的能力最强。
假设用某一字段A来划分,在这种划分规则下的信息熵为:
I n f o A = − ∑ j = 1 v p j ⋅ I n f o ( A j ) \LARGE Info_A=-\sum_{j=1}^vp_j·Info(A_j) InfoA=−∑j=1vpj⋅Info(Aj)
例如,取A为学历,这里v=3,表示有3组, A j A_j Aj分别表示大专,本科,硕士, p j p_j pj为各种分组产生的概率, I n f o ( A j ) Info(A_j) Info(Aj)是当前分组状态下的期望信息值。具体计算学历的信息熵为:
I n f o A \LARGE Info_A InfoA
= − [ ( 大 专 项 ) + ( 本 科 项 ) + ( 硕 士 项 ) ] \LARGE =-[(大专项)+(本科项)+(硕士项)] =−[(大专项)+(本科项)+(硕士项)]
= − [ ( p 大 专 × 大 专 分 隔 熵 ) + ( p 本 科 × 本 科 分 隔 熵 ) + ( p 硕 士 × 硕 士 分 隔 熵 ) ] \LARGE =-[(p_{大专}×大专分隔熵)+(p_{本科}×本科分隔熵)+(p_{硕士}×硕士分隔熵)] =−[(p大专×大专分隔熵)+(p本科×本科分隔熵)+(p硕士×硕士分隔熵)]
= − [ 2 12 × ( 1 2 ⋅ l o g 2 1 2 + 1 2 ⋅ l o g 2 1 2 ) ] − [ 5 12 × ( 3 5 ⋅ l o g 2 3 5 + 2 5 ⋅ l o g 2 2 5 ) ] − [ 5 12 × ( 4 5 ⋅ l o g 2 4 5 + 1 5 ⋅ l o g 2 1 5 ) ] \LARGE =-[\frac{2}{12}×(\frac{1}{2}·log_2\frac{1}{2}+\frac{1}{2}·log_2\frac{1}{2})]-[\frac{5}{12}×(\frac{3}{5}·log_2\frac{3}{5}+\frac{2}{5}·log_2\frac{2}{5})]-[\frac{5}{12}×(\frac{4}{5}·log_2\frac{4}{5}+\frac{1}{5}·log_2\frac{1}{5})] =−[122×(21⋅log221+21⋅log221)]−[125×(53⋅log253+52⋅log252)]−[125×(54⋅log254+51⋅log251)]
≈ 0.872 \LARGE ≈0.872 ≈0.872
信息增益如下:
G a i n ( 学 历 ) \LARGE Gain(学历) Gain(学历)
= I n f o − I n f o 学 历 \LARGE =Info-Info_{学历} =Info−Info学历
= 0.98 − 0.872 \LARGE =0.98-0.872 =0.98−0.872
= 0.108 b i t \LARGE =0.108bit =0.108bit
这就是用“学历”为根的信息增益,即知道了学历信息后所能消除的不确定性大小。
.
枚举类型字段的期望信息计算方法:
#枚举类型字段的期望信息的计算方法
import math
#学历分类中大专,本科,硕士占比
education=(2.0/12,5.0/12,5.0/12)
#大专分类中相亲占比
junior_college=(1.0/2,1.0/2)
#本科分类中相亲占比
undergraduate=(3.0/5,2.0/5)
#硕士分类中相亲占比
master=(4.0/5,1.0/5)
#学历各分类中相亲占比
date_per=(junior_college,undergraduate,master)
#“相亲”字段划分规则下的熵
def info_date(p):
info=0
for v in p:
info+=v*math.log(v,2)
return info
#使用“学历”字段划分规则下的熵
def infoA():
info=0
for i in range(len(education)):
info+=-education[i]*info_date(date_per[i])
return info
print(infoA())
在归纳决策树的时候,要计算所有字段的信息增益,选取信息增益最大的字段作为决策树根。
##连续型变量
计算一下用“年龄”字段做树根能否得到最大的信息增益。
.
但是“年龄”字段比较麻烦,是一个连续型的字段,不像学历,只有3个枚举值。
这种时候通常是在这个字段上找到一个最佳分裂点,然后一刀切下去,让它的信息增益最大。
例如字段中的年龄值,做一个排序:
(25,25,28,28,29,30,33,34,35,36,40,46)
我们选择相邻值的中间值作为分裂点,从中位点开始,向两边计算,每次切割产生一个信息熵,当从m点分裂的信息熵比从m-1和m+1点的信息熵都小时,我们就认为找到了这个点。
.
连续类型字段的期望信息计算方法:
#连续类型字段的期望信息计算方法
import numpy as np
#年龄
age=[25,25,28,28,29,30,33,34,35,36,40,46]
#是否相亲:1:是,0:否
date=[0,1,1,0,1,1,1,1,1,0,0,0]
splits=[]
for i in range(1,len(age)):
splits.append((age[i]+age[i-1])/2)
infoAs=[]
age=np.array(age)
date=np.array(date)
#“相亲”字段划分规则下的熵
def info_date(p):
info=0
for v in p:
if v==0:
return 0
info+=v*np.math.log(v,2)
return info
def infoA():
info=0
for i in range(len(split_per)):
info+=-split_per[i]*info_date(date_per[i])
return info
for i in range(len(splits)):
#以split为分裂点
split=splits[i]
left=len(age[age<=split])
#左,右分类中的数量占总数的百分比
split_per=(left/len(age),1-left/len(age))
#左边分类中相亲占比
date_left=(len(date[:left][date[:left]==1])/len(date[:left]),1-len(date[:left][date[:left]==1])/len(date[:left]))
#右边分类中相亲占比
date_right=(len(date[left:][date[left:]==1])/len(date[left:]),1-len(date[left:][date[left:]==1])/len(date[left:]))
#左,右各分类中相亲占比
date_per=(date_left,date_right)
infoAs.append(infoA())
for i in range(len(splits)):
print(splits[i],' : ',infoAs[i])
输出结果为:
.
25.0 : 0.9757921620455572
26.5 : 0.9757921620455572
28.0 : 0.9696226686166434
28.5 : 0.9696226686166434
29.5 : 0.979279160376092
31.5 : 0.9591479170272448
33.5 : 0.90804974601998
34.5 : 0.8112781244591329
35.5 : 0.5731533798814652
38.0 : 0.7344090826922439
43.0 : 0.8668552792172535
.
由输出结果可得,选取35.5作为分裂点信息增益最大。
##总结一下
最后归纳总结一下构造整棵树时的思路,应该是遵循下面这样的方式:
-
第一步,找到信息增益最大的字段A和信息增益最大的切分点v(不管是连续类型还是枚举类型)。
-
第二步,决定根节点的字段A和切分点v。
-
第三步,把字段A从所有的待选字段列表中拿走,再从第一步开始找。这时决策虽然已经走了一步了,但是根节点也分裂成了两个分支,那么每一个分支各自又形成一个完整的决策树的选择过程。不同的是:可选的字段不一样了,因为A字段被去掉了;此外,在这个分支上的样本也比原来少了,因为两个分支分隔了整个样本,使得一个部分分支只拥有样本的一部分。
##减枝法
在决策树的构建过程中,还可以使用“减枝法”进行树的修剪,分为“前减枝”和“后减枝”两种方法。
-
前减枝:提前终止树的构造,如只用了两个字段,两层树就已经构造出完整的整个树了,那么就可以提前终止树的构造,保持了树的精简性。
-
后减枝:在决策树完全构造完之后,如建模一共使用了7个字段,全部用上,这样就形成了一个7层的树,如果一个分支下分类已经比较“纯粹”了,就没必要在通过其他条件分支来进行细化,那么整个枝可以直接减掉变成一个叶。
#随机森林
随机森林算法是一种并行性比较好的算法规则。
与决策树归纳的过程类似,随机森林也是一个构造决策树的过程,只不过它不是要构造一棵树,而是构造许多棵树。
在决策树的构造过程中会遇到过拟和欠拟的问题,但是在随机森林算法中,通常在一棵树上是不会追求及其精确的拟合的,相反,希望的是决策树的简洁和计算的快速。
随机森林算法的步骤和原则如下:
- 第一步,随机挑选一个字段构造树的第一层。
- 第二步,随机挑选一个字段构造树的第二层。
- ……
- 第n步,随机挑选一个字段构造树的第n层。
- 在本棵树构造完毕之后,还需要照相同的方式建造m棵决策树。
补充原则如下:
- 每棵树的层级通常都比较浅。
- 每棵树的分类都不能保证分类精度很高。
- 一个样本进行分类时同时对这m棵决策树做分类概率判断。
人们会为一个训练集构造若干棵决策树,通常可能是几十甚至上百棵,具体会根据样本属性的数量和杂乱程度来决定。当有新样本需要进行分类时,同时把这个样本给这几棵树,然后用民主投票的方式来决定新样本应该属于哪个类,哪一类得票多就归为哪一类。
下面把上述例子用随机森林的方式来实现:
#随机森林
from sklearn.ensemble import RandomForestClassifier
#学历:0:大专,1:本科,2:硕士
#年龄,身高,年收入,学历
X=[
[25,179,15,0],
[33,190,19,0],
[28,180,18,2],
[25,178,18,2],
[46,177,100,2],
[40,170,70,1],
[34,174,20,2],
[36,181,55,1],
[35,170,25,2],
[30,180,35,1],
[28,174,30,1],
[29,176,36,1]
]
#是否相亲:0:N,1:Y
y=[0,1,1,1,0,0,1,0,1,1,0,1]
#现在把训练数据和对应的分类放入分类器中进行训练
clf=RandomForestClassifier().fit(X,y)
#预测下面的人是否相亲
p=[[30,185,18,2],[30,170,70,0]]
print(clf.predict(p))
预测接下来的两个人是否安排相亲,分别为:
年龄30,身高185,年收入18万元,硕士学历,结果为安排相亲;
年龄30,身高170,年收入70万元,大专学历,结果为不安排相亲。