网站首页 > 技术文章 正文
在以前的一篇博文里,我讨论过如何将随机森林算法转化为一个“白盒”,这样每次预测就能被分解为各项特征的贡献和,即
我多次想找相关的代码。然而,绝大多数的随机森林算法库(包括scikit-learn)不暴露预测过程的树路径(tree paths)。sklearn的实现方法需要一个额外补丁来暴露。庆幸的是,scikit-learn自0.17版起在API中添加了两项功能,使得这个过程相对而言比较容易理解:获取用于预测的所有叶子节点的ID,并存储所有决策树的所有节点的中间值,而不仅仅只存叶子节点的。结合这两步,就可以获取每次独立预测的预测路径,同时根据查看路径来分解预测过程。
代码已经放在github上了,也可以用 pip install treeinterpreter进行安装。
注意:需要用到仍在开发中的scikit-learn 0.17,你在下面的链接中能找到安装方法http://scikit-learn.org/stable/install.html#install-bleeding-edge。
用treeinterpreter分解随机森林预测
我们选一个简单的数据集,训练一个随机森林模型,并用测试集进行预测,然后分解预测过程。
from treeinterpreter import treeinterpreter as ti from sklearn.tree import DecisionTreeRegressor from sklearn.ensemble import RandomForestRegressor import numpy as np from sklearn.datasets import load_boston boston = load_boston rf = RandomForestRegressor rf.fit(boston.data[:300], boston.target[:300])
我们随机挑选两个预测价格不相同的样本。
instances = boston.data[[300, 309]] print "Instance 0 prediction:", rf.predict(instances[0]) print "Instance 1 prediction:", rf.predict(instances[1])
Instance 0 prediction: [ 30.76]
Instance 1 prediction: [ 22.41]
随机森林模型对它们的预测结果迥然不同。这是为什么呢?我们接下来就把预测结果分为偏置项(也就是训练集的平均结果)和单个特征贡献值,以便于观察究竟哪些特征项造成了差异,差异程度有多大。
我们直接调用tree interpreter的predict方法,向其传入模型和数据作为参数。
prediction, bias, contributions = ti.predict(rf, instances)
打印出这些结果:
for i in range(len(instances)): print "Instance", i print "Bias (trainset mean)", biases[i] print "Feature contributions:" for c, feature in sorted(zip(contributions[i], boston.feature_names), key=lambda x: -abs(x[0])): print feature, round(c, 2) print "-"*20
Instance 0
Bias (trainset mean) 25.2849333333
Feature contributions:
RM 2.73
LSTAT 1.71
PTRATIO 1.27
ZN 1.04
DIS -0.7
B -0.39
TAX -0.19
CRIM -0.13
RAD 0.11
INDUS 0.06
AGE -0.02
NOX -0.01
CHAS 0.0
--------------------
Instance 1
Bias (trainset mean) 25.2849333333
Feature contributions:
RM -4.88
LSTAT 2.38
DIS 0.32
AGE -0.28
TAX -0.23
CRIM 0.16
PTRATIO 0.15
B -0.15
INDUS -0.14
CHAS -0.1
ZN -0.05
NOX -0.05
RAD -0.02
特征贡献值按照其绝对值从大到小排序。我们观察到第一个样本的预测结果较高,正贡献值主要来自RM、LSTAT和PTRATIO特征。第二个样本的预测值则低得多,因为RM特征实际上有很大的负面影响,它不会被其它特征的正面影响所抵消,因此使得预测值要低于数据集的平均水平。
分解的结果真的对吗?很容易检验:偏置和特征贡献值相加应该等于预测值:
print prediction print biases + np.sum(contributions, axis=1)
[ 30.76 22.41]
[ 30.76 22.41]
注意,在把贡献值相加时,我们需要对浮点数进行处理,所以经过四舍五入处理后的值可能略有不同。
比较两个数据集
这个方法的用武之地之一就是比较两个数据集。例如:
- 理解造成两个数据集预测值差异的真正原因,比如是什么因素导致相邻两幢房屋的预测价值差异。
- 调试模型和数据,例如解释为什么新数据的平均预测值和旧数据的不一样。
还是上面这个例子,我们把房价数据的测试集再一分为二,分别计算它们的平均预测价值。
ds1 = boston.data[300:400] ds2 = boston.data[400:] print np.mean(rf.predict(ds1)) print np.mean(rf.predict(ds2))
22.1912
18.4773584906
我们发现两个数据集的平均预测价值完全不同。现在我们就能细分导致差异的因素:究竟哪些特征项造成了差异,差异程度有多大。
prediction1, bias1, contributions1 = ti.predict(rf, ds1) prediction2, bias2, contributions2 = ti.predict(rf, ds2)
我们再来计算每一维特征的平均贡献程度。
totalc1 = np.mean(contributions1, axis=0) totalc2 = np.mean(contributions2, axis=0)
由于两个数据集的偏置项都一样(因为模型的训练集都一样),平均预测价值的差异只能来自于特征的贡献值。换句话说,特征贡献差异的总和应该与平均预测的差异相等,我们很容易验证
print np.sum(totalc1 - totalc2) print np.mean(prediction1) - np.mean(prediction2)
3.71384150943
3.71384150943
最后,我们把每一维特征贡献的差异之和显示出来,正好就是平均预测值的差异。
for c, feature in sorted(zip(totalc1 - totalc2, boston.feature_names), reverse=True): print feature, round(c, 2)
LSTAT 2.8
CRIM 0.5
RM 0.5
PTRATIO 0.09
AGE 0.08
NOX 0.03
B 0.01
CHAS -0.01
ZN -0.02
RAD -0.03
INDUS -0.03
TAX -0.08
DIS -0.14
分类树和森林
同样的方法也能用于分类树,查看特征对某个类别的预测概率值的影响力。
我们在iris数据集上做演示。
from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import load_iris iris = load_iris rf = RandomForestClassifier(max_depth = 4) idx = range(len(iris.target)) np.random.shuffle(idx) rf.fit(iris.data[idx][:100], iris.target[idx][:100])
接着用一个独立样本做预测。
instance = iris.data[idx][100:101] print rf.predict_proba(instance)
拆分每一维特征的贡献值:
prediction, bias, contributions = ti.predict(rf, instance) print "Prediction", prediction print "Bias (trainset prior)", bias print "Feature contributions:" for c, feature in zip(contributions[0], iris.feature_names): print feature, c
Prediction [[ 0. 0.9 0.1]]
Bias (trainset prior) [[ 0.36 0.262 0.378]]
Feature contributions:
sepal length (cm) [-0.1228614 0.07971035 0.04315104]
sepal width (cm) [ 0. -0.01352012 0.01352012]
petal length (cm) [-0.11716058 0.24709886 -0.12993828]
petal width (cm) [-0.11997802 0.32471091 -0.20473289]
我们看到对第二类预测能力最强的特征是花瓣长度和宽度,它们极大提高了预测的概率值。
总结
让随机森林算法的预测结果具有解释性也很容易,几乎达到了线性模型的解释能力。有了treeinterpreter,这个步骤只需几行代码就能搞定。
原文地址:Random forest interpretation with scikit-learn(译者/赵屹华 校检/刘帝伟、朱正贵、李子健 责编/周建丁)
赵屹华,计算广告工程师@搜狗,前生物医学工程师,关注推荐算法、机器学习领域。
- 上一篇: 异常检测汇总 异常检测 gan
- 下一篇: 深度学习:利用神经网络在少量数据情况下预测房价走势
猜你喜欢
- 2024-09-25 果断收藏!python数据分析入门学习笔记(下)
- 2024-09-25 「机器学习」支持向量机分类 支持向量机 知乎
- 2024-09-25 数据可视化之箱线图详细介绍 箱线图绘制步骤
- 2024-09-25 简单的统计学:如何用Python计算扑克概率
- 2024-09-25 Python进行数据预处理 python如何做数据处理
- 2024-09-25 Distribution is all you need:这里有12种做ML不可不知的分布
- 2024-09-25 如何使用 Qdrant DB 创建基于向量的电影推荐系统?
- 2024-09-25 如何可视化卷积网络分类图像时关注的焦点
- 2024-09-25 感知机:教程,实现和可视示例 感知机定义
- 2024-09-25 数据处理中的“归一化”到底是什么?Talk is cheap,show me the code
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)