使用scikit确定每个要素对特定类预测的贡献

use*_*827 16 python scikit-learn

我正在使用scikit额外的树分类器:

model = ExtraTreesClassifier(n_estimators=10000, n_jobs=-1, random_state=0)
Run Code Online (Sandbox Code Playgroud)

一旦模型被拟合并用于预测类,我想找出每个特征对特定类预测的贡献.我如何在scikit中学习呢?是否可以使用额外的树分类器或我是否需要使用其他模型?

Ulf*_*lak 15

更新

今天对ML的了解比我2.5年前更加了解,我现在说这种方法只适用于高度线性的决策问题.如果你不小心将它应用于非线性问题,你将遇到麻烦.

示例:想象一个功能,既没有非常大的值也没有非常小的值预测类,但是在某个中间间隔中的值确实如此.这可能是饮水以预测脱水.但是水的摄入可能与盐的摄入相互作用,因为多吃盐可以增加水的摄入量.现在,您可以在两个非线性要素之间进行交互.决策边界蜿蜒在您的特征空间周围,以模拟这种非线性,并仅询问其中一个特征影响脱水风险的程度根本无知.这不是正确的问题.

替代方案:您可以提出的另一个更有意义的问题是:如果我没有这些信息(如果我遗漏了这个功能),我对给定标签的预测会受到多大影响?要做到这一点,您只需省略一项功能,训练模型并查看每个班级的精确度和召回率.它仍然告知特征重要性,但它没有对线性进行假设.

以下是旧答案.


我在前一段时间里遇到了类似的问题并在Cross Validated上发布了相同的问题.简短的回答是没有实现sklearn所有你想要的东西.

但是,您要实现的目标非常简单,可以通过将每个类的每个特征拆分的平均标准化平均值与相应的model._feature_importances数组元素相乘来完成.您可以编写一个简单的函数来标准化数据集,计算跨类预测的每个特征拆分的平均值,并与model._feature_importances数组进行元素相乘.绝对结果值越大,特征对其预测类越重要,更好的是,符号将告诉您它是重要的小值还是大值.

这是一个超级简单的实现,它采用数据矩阵X,预测列表Y和一系列要素重要性,并输出描述每个要素对每个类的重要性的JSON.

def class_feature_importance(X, Y, feature_importances):
    N, M = X.shape
    X = scale(X)

    out = {}
    for c in set(Y):
        out[c] = dict(
            zip(range(N), np.mean(X[Y==c, :], axis=0)*feature_importances)
        )

    return out
Run Code Online (Sandbox Code Playgroud)

例:

import numpy as np
import json
from sklearn.preprocessing import scale

X = np.array([[ 2,  2,  2,  0,  3, -1],
              [ 2,  1,  2, -1,  2,  1],
              [ 0, -3,  0,  1, -2,  0],
              [-1, -1,  1,  1, -1, -1],
              [-1,  0,  0,  2, -3,  1],
              [ 2,  2,  2,  0,  3,  0]], dtype=float)

Y = np.array([0, 0, 1, 1, 1, 0])
feature_importances = np.array([0.1, 0.2, 0.3, 0.2, 0.1, 0.1])
#feature_importances = model._feature_importances

result = class_feature_importance(X, Y, feature_importances)

print json.dumps(result,indent=4)

{
    "0": {
        "0": 0.097014250014533204, 
        "1": 0.16932975630904751, 
        "2": 0.27854300726557774, 
        "3": -0.17407765595569782, 
        "4": 0.0961523947640823, 
        "5": 0.0
    }, 
    "1": {
        "0": -0.097014250014533177, 
        "1": -0.16932975630904754, 
        "2": -0.27854300726557779, 
        "3": 0.17407765595569782, 
        "4": -0.0961523947640823, 
        "5": 0.0
    }
}
Run Code Online (Sandbox Code Playgroud)

第一级键result是类标签,第二级键是列索引,即特征索引.回想一下,大的绝对值对应于重要性,并且符号告诉您它是小的(可能是负的)还是重要的大值.


tho*_*olf 6

这篇论文 “Why Should I Trust You?”: Explaining the Predictions of Any Classifier是在这个问题之后9天提交的,提供了一个通用解决这个问题的算法!:-)

简而言之,它被称为“本地可解释模型不可知解释”的 LIME,它通过围绕您想要理解的预测拟合一个更简单的本地模型来工作。

更重要的是,他们制作了一个 python 实现(https://github.com/marcotcr/lime),其中包含有关如何将它与 sklearn 一起使用的非常详细的示例。例如,这个是关于文本数据的两类随机森林问题,而这个是关于连续和分类特征的。它们都可以通过 github 上的 README 找到。

作者在 2016 年在这个领域非常有成效,所以如果你喜欢阅读论文,这里是一个初学者:


Kev*_*vin 5

这是从文档修改

from sklearn import datasets
from sklearn.ensemble import ExtraTreesClassifier

iris = datasets.load_iris()  #sample data
X, y = iris.data, iris.target

model = ExtraTreesClassifier(n_estimators=10000, n_jobs=-1, random_state=0)
model.fit_transform(X,y) # fit the dataset to your model
Run Code Online (Sandbox Code Playgroud)

我想feature_importances_你正在寻找:

In [13]: model.feature_importances_
Out[13]: array([ 0.09523045,  0.05767901,  0.40150422,  0.44558631])
Run Code Online (Sandbox Code Playgroud)

编辑

也许我误解了第一次(赏金前),对不起,这可能更符合您的要求.有一个叫做python的库treeinterpreter可以产生我认为你正在寻找的信息.你必须使用基本DecisionTreeClassifer(或回归).在此博客文章中,您可以在每个实例的预测中离散地访问要素贡献:

from sklearn import datasets
from sklearn.cross_validation import train_test_split
from sklearn.tree import DecisionTreeClassifier

from treeinterpreter import treeinterpreter as ti

iris = datasets.load_iris()  #sample data
X, y = iris.data, iris.target
#split into training and test 
X_train, X_test, y_train, y_test = train_test_split( 
    X, y, test_size=0.33, random_state=0)

# fit the model on the training set
model = DecisionTreeClassifier(random_state=0)
model.fit(X_train,y_train)
Run Code Online (Sandbox Code Playgroud)

X_test为了说明的目的,我将迭代每个示例,这几乎完全模仿上面的博客文章:

for test_sample in range(len(X_test)):
    prediction, bias, contributions = ti.predict(model, X_test[test_sample].reshape(1,4))
    print "Class Prediction", prediction
    print "Bias (trainset prior)", bias

    # now extract contributions for each instance
    for c, feature in zip(contributions[0], iris.feature_names):
        print feature, c

    print '\n'
Run Code Online (Sandbox Code Playgroud)

循环的第一次迭代产生:

Class Prediction [[ 0.  0.  1.]]
Bias (trainset prior) [[ 0.34  0.31  0.35]]
sepal length (cm) [ 0.  0.  0.]
sepal width (cm) [ 0.  0.  0.]
petal length (cm) [ 0.         -0.43939394  0.43939394]
petal width (cm) [-0.34        0.12939394  0.21060606]
Run Code Online (Sandbox Code Playgroud)

解释这个输出,似乎花瓣长度和花瓣宽度是预测第三类(对于第一个样本)最重要的贡献者.希望这可以帮助.