0x0B 菩提决策树,姻缘算法求

摘要:要找到有情人,通常是一个漫长的过程。在scikit-learn中,如何模拟人类的决策过程?决策树的目的是构建一颗最优的二叉分割树,因此从根节点往下,每一个结点的条件选择,都是为了让树结构最简洁。

0x0B.jpg
00 缘起

姻缘天注定,一段婚姻一段缘,是报恩报怨,还是讨债还债,在一开始,谁都不知道。但可以确定一点,今生定会在一起,无论是长相厮守,还是短暂快乐。

佛曰:和有情人,做快乐事,别问是劫还是缘。

要找到有情人,通常是一个漫长的过程,也许会经历很多场相亲活动。时间久了,对相亲也有更多的认识,并且积累和记录一些数据,如下表:

序号 身高 房子 车子 长相 工作 约否
1 1.80 6.5 fact
2 1.62 5.5 IT
3 1.71 8.5 bank
4 1.58 6.3 bank
5 1.68 5.1 IT 不约
6 1.63 5.3 bank 不约
7 1.78 4.5 IT 不约
8 1.64 7.8 fact 不约
9 1.65 6.6 bank 约吗?

前面8条数据为过往的相亲记录,最后字段为是否愿意继续约会的记录。如前4条数据,会愿意继续约会,后面四条,直接pass。将这八条记录作为训练集,第9条是一条新数据,作为预测,决断是否要继续约会呢?

对数据进行一些处理,将其中的离散变量(类别变量)进行替换,保存成csv格式如下表。1表示有,0表示无,目标值的-1表示没有意义,待预测。对于属性“工作”列,使用字典:{'IT': 0, 'bank': 1, 'fact':2}进行映射。

height,house,car,handsome,job,is_date
1.80,1,0,6.5,2,1
1.62,1,0,5.5,0,1
1.71,0,1,8.5,1,1
1.58,1,1,6.3,1,1
1.68,0,1,5.1,0,0
1.63,1,0,5.3,1,0
1.78,0,0,4.5,0,0
1.64,0,0,7.8,2,0
1.65,0,1,6.6,0,-1

01 scikit-learn模拟

知识星球.jpeg

程序员喜欢用代码说话,喜欢用代码模拟,那么在scikit-learn中,如何模拟人类的决策过程呢?

# sklearn_dt.py
import pandas as pd
from sklearn.tree import DecisionTreeClassifier,export_graphviz

df = pd.read_csv('sklearn_data.csv')
train,test = df.query("is_date != -1"), df.query("is_date == -1")
y_train, X_train = train['is_date'],train.drop(['is_date'], axis=1)
X_test = test.drop(['is_date'], axis=1)

model = DecisionTreeClassifier(criterion='gini')
model.fit(X_train, y_train)
print model.predict(X_test)

上面代码,使用pandas加载数据集,然后选择出训练集与测试集,倒数第三行构建了一个决策树模型,全部使用默认参数,最后对测试数据进行预测。

需要注意一个参数,criterion='gini',这是决策树构建的核心算法,参数“gini”指示了程序使用基尼不纯度来进行构建决策树。对于分类问题,scikit-learn还支持使用参数值为"entropy",表示使用熵的信息增益来构建决策树。对于回归问题,scikit-learn使用mse(均方误差)来构建。

多次运行上面的程序,程序会在某些时刻输出0,某些时间输出1。

02 信息增益,基尼不纯度

上面虽然只是用一行代码便构建了一个决策树,但实际上这一行代码后面,做了很多事情,也就是决策树构建的过程。

由前面的图片可以看出,决策树是一颗二叉树,左右两个分支,中间一个条件判断,条件满足走左分支,条件不满足,走右分支。一层层往下,因此可以用递归过程来构建。唯一的问题,在当前的结点,应该选择哪个条件来进行分割。

决策树的目的是构建一颗最优的二叉分割树,因此从根节点往下,每一个结点的条件选择,都是为了让树结构最简洁。换一句简单的说,就是尽量的找出能使当前数据集尽量分开的条件。

专业术语来说,是使得数据集的不纯度越来小,即纯度越来越大,使得数据尽可能的分开;不纯度也可以理解成数据集的混乱程度(熵),数据越混乱,不纯度越高,熵越大,也即不确定性越大。比如数据集里面,约会和不约会的概率都是0.5,那么表示不确定性很多,即不纯度很大,纯度很小。

我们的目的,就是找到一个条件进行分割,使得这种不确定性减小,如长相小于等于5.4的数据,一定不约,其不纯度为0,表示完全纯净了,都是不约,没有不确定性。

决策树常用的算法有ID3,C4.5,C5.0,CART,其中前面三种,都是用的熵的信息增益(率)来表示。最后一个CART(Classification And Regression Tree),即分类回归树,Scikit-learn实现的便是这种算法,从名字知道,既能用于分类,也用于回归问题,其中分类问题可以使用基尼(Gini)不纯度和熵的信息增益,回归问题使用了方差降低(Variance Reduction,同mse的均方误差)的算法。

维基上面有各种算法的数学公式,对分类问题,都是基于各个类别的概率的简单计算。在构建树的时候,算法会尝试在当前数据集的所有特征上进行切分,找到概率计算出来的最优切分,并将当前条件做为切分点。

以上面的数据为例,使用gini不纯度进行分割,最开始数据集的不纯度为0.5(根结点的impurity=0.5),在尝试了所有将数据切分一分为二(比如,切分按是否有房切分,长相大于7划分,长相小于5划分)的条件后,发现handsome<=5.4的划分,是最优的划分。

因此,决策树的构建过程,主要为两个步骤,一是数据二叉划分,不同的实现方法,有不同的数据划分,对离散变量,通常是按集合的方法,将数据集划分成两类,比如{'bank', 'IT', 'fact'}这个集合,通常会划分成{'bank'}, {'IT','fact'};{'IT'},{'bank','fact'};{'bank','IT'},{'fact'}这三个。而对于连续型数据,{3.8, 4.5, 7.8}这样的集体,则会按两个数的平均值进行划分。

数据分割完后,使用一种度量方法,来计算当前结点应该选择哪一个条件进行最优切分,选中的条件,即为当前的决策。

03 决策之树,决策过程

从上面的算法中,可以简单理解为,决策树就是找到当前最优的条件进行将数据一分为二,最优的条件即为当前的决策。

使用图表,来分析具体的决策过程,在scikit-learn程序中,添加如下代码:

export_graphviz(model.tree_,
                out_file='tree.dot',
                feature_names=df.columns,
                max_depth=None,
                # 下面几个参数,需要使用最新的scikit-learn 0.17版本才能用
                class_names=["is_date:no", "is_date:yes"],
                rounded=True,
                filled=True,
            )

对生成的决策树图片的说明:

  • 第一行为决策条件(非叶子结点),比如根节点handsome<=5.4,条件为真走左边,为假走右边。
  • impurity表示当前数据集的基尼不纯度
  • samples表示当前数据集的样本数
  • value表示当前数据集各个类别(按类别顺序从小到大排序,第0类,第1类……)的数量,如果value中的值相等,当前节点没有着色,为白色。
  • class表示当前数据集中的多数类别,如果value中各个值相等,则应该是按顺序取值。

运行上面的代码,会输出一个tree.dot的文件,再使用如下的命令,可以生成一个图片(开篇图片左边部分):

$ sudo apt-get install graphviz   # 需要安装程序
$ dot -Tpng tree.dot -o tree.png

分析图片的数据,总结出决策规则如下:

  • 长相小于等于5.4的,一定不约;
  • 不满足前面条件,但:有房子的,一定约;
  • 不满足前面条件,但:有车,约,没有车的,不约;


    知识星球.jpeg

    对待测数据,使用上面的规则:

  • 长相大于5.4,不满足规则1,继续下一条;
  • 没有房子,不满足规则2,继续下一条规则;
  • 有车,符合第3条规则,约。

04 spark模拟

上面的决策过程,换成简单的程序思维来表达,就是if-else的条件判断,使用spark的实现的决策树,可以打印出来if-else的条件决策过程:

# spark_dt.py
from pprint import pprint
from pyspark import SparkContext
from pyspark.mllib.tree import DecisionTree
from pyspark.mllib.regression import LabeledPoint

sc = SparkContext()
data = sc.textFile('spark_data.csv').map(lambda x: x.split(',')).map(lambda x: (float(x[0]), int(x[1]), int(x[2]), float(x[3]), int(x[4]), int(x[5])))
train = data.filter(lambda x: x[5]!=-1).map(lambda v: LabeledPoint(v[-1], v[:-1]))
test = data.filter(lambda x: x[5]==-1)#.map(lambda v: LabeledPoint(v[-1], v[:-1]))
model = DecisionTree.trainClassifier(train,
                                     numClasses=2,
                                     categoricalFeaturesInfo={1:2, 2:2, 4:3},
                                     impurity='gini',
                                     maxDepth=5,
                                 )

print 'The predict is:', model.predict(test).collect()
print 'The Decision tree is:', model.toDebugString()

通过上面的model.toDebugString(),打印出决策的过程,见开篇图片右边部分。

05 结尾

上面演示了两种不同的决策树实现,scikit-learn和spark,在演示数据上,输出可能会不同,因为各自的实现是有差别的。主要在于对离散变量(工作属性)的处理和数据的分割上面。

决策树还会涉及剪枝的问题,完全生成的决策树会伴随着数据的噪声导致过拟合,实际应用通常使用随机森林来防止过拟合。关于随机森林,请持续关注。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容