用scikit-learn 來演繹隨機森林方法

yxli 8年前發布 | 29K 次閱讀 算法 scikit-learn

來自: http://datartisan.com/article/detail/85.html

在之前的一篇文章中,我們討論了如何將隨機森林模型轉成一個「白箱子」,就像預測變量可以由一組擁有不同特征自變量的來解釋。

我對此有不少需求,但不幸的是,大多數隨機森林算法包(包括 scikit-learn)并沒有給出樹的預測路徑。因此sklearn的應用需要一個補丁來展現這些路徑。幸運的是,cong 0.17 dev,scikit-learn 補充了兩個附加的api,使得一些問題更加方便。獲得葉子node_id,并將所有中間值存儲在決策樹中所有節點,不僅葉節點。通過結合這些,我們有可能可以提取每個單獨預測的預測路徑,以及通過檢查路徑來分解它們。

廢話少說, 代碼托管在github,你可以通過 pip install treeinterpreter 來獲取。

使用treeinterpreter來分解隨機森林

首先我們將使用一個簡單的數據集,來訓練隨機森林模型。在對測試集的進行預測的同時我們將對預測值進行分解。

from treeinterpreter import treeinterpreter as tifrom sklearn.tree import DecisionTreeRegressorfrom sklearn.ensemble import RandomForestRegressorimport numpy as npfrom sklearn.datasets import load_boston
boston = load_boston()
rf = RandomForestRegressor()
rf.fit(boston.data[:300], boston.target[:300])
RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=None,
           max_features='auto', max_leaf_nodes=None, min_samples_leaf=1,
           min_samples_split=2, min_weight_fraction_leaf=0.0,
           n_estimators=10, n_jobs=1, oob_score=False, random_state=None,
           verbose=0, warm_start=False)

任意選擇兩個可以產生不同價格模型的數據點。

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.27]
Instance 1 prediction: [ 22.03]

/Users/donganlan/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py:386: DeprecationWarning: Passing 1d arrays as data is deprecated in 0.17 and willraise ValueError in 0.19. Reshape your data either using X.reshape(-1, 1) if your data has a single feature or X.reshape(1, -1) if it contains a single sample.
  DeprecationWarning)
/Users/donganlan/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py:386: DeprecationWarning: Passing 1d arrays as data is deprecated in 0.17 and willraise ValueError in 0.19. Reshape your data either using X.reshape(-1, 1) if your data has a single feature or X.reshape(1, -1) if it contains a single sample.
  DeprecationWarning)

對于這兩個數據點,隨機森林給出了差異很大的預測值。為什么呢?我們現在可以將預測值分解成有偏差項(就是訓練集的均值)和個體差異,并探究哪些特征導致了差異,并且占了多少。

我們可以簡單的使用treeinterpreter中 predict 方法來處理模型和數據。

prediction, bias, contributions = ti.predict(rf, instances)#Printint out the results: for i in range(len(instances)):
    print "Instance", i
    print "Bias (trainset mean)", bias[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.8759666667
Feature contributions:
RM 4.25
TAX -1.26
LSTAT 0.71
PTRATIO 0.22
DIS 0.15
B -0.14
AGE 0.12
CRIM 0.12
RAD 0.11
ZN 0.1
NOX -0.1
INDUS 0.06
CHAS 0.06
--------------------
Instance 1
Bias (trainset mean) 25.8759666667
Feature contributions:
RM -5.81
LSTAT 1.66
CRIM 0.26
NOX -0.21
TAX -0.15
DIS 0.13
B 0.11
PTRATIO 0.07
INDUS 0.07
RAD 0.05
ZN -0.02
AGE -0.01
CHAS 0.0
--------------------

各個特征的貢獻度按照絕對值從大到小排序。我們可以從 Instance 0中(預測值較高)可以看到,大多數正效應來自RM.LSTAT和PTRATIO。在Instance 1中(預測值較低),RM實際上對預測值有著很大的負影響,而且這個影響并沒有被其他正效應所補償,因此低于數據集的均值。

但是這個分解真的是對的么?這很容易檢查:偏差項和各個特征的貢獻值加起來需要等于預測值。

print predictionprint bias + np.sum(contributions, axis=1)
[ 30.27  22.03]
[ 30.27  22.03]

對更多的數據集進行對比

當對比兩個數據集時,這個方法將會很有用。例如

  • 理解導致兩個預測值不同的真實原因,究竟是什么導致了房價在兩個社區的預測值不同 。

  • 調試模型或者數據,理解為什么新數據集的平均預測值與舊數據集所得到的結果不同。

舉個例子,我們將剩下的房屋價格數據分成兩個部分,分別計算它們的平均估計價格。

ds1 = boston.data[300:400]
ds2 = boston.data[400:]print np.mean(rf.predict(ds1))print np.mean(rf.predict(ds2))
22.3327
18.8858490566

我們可以看到兩個數據集的預測值是不一樣的。現在來看看造成這種差異的原因:哪些特征導致了這種差異,它們分別有多大的影響。

prediction1, bias1, contributions1 = ti.predict(rf, ds1)
prediction2, bias2, contributions2 = ti.predict(rf, ds2)#We can now calculate the mean contribution of each feature to the difference. 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.4468509434
3.4468509434

最后,我們將兩個數據集中各個特征的貢獻打印出來,這些數的總和正好等于與預測均值的差異。

for c, feature in sorted(zip(totalc1 - totalc2, 
                             boston.feature_names), reverse=True):
    print feature, round(c, 2)
LSTAT 2.23
CRIM 0.56
RM 0.45
NOX 0.28
B 0.1
ZN 0.03
PTRATIO 0.03
RAD 0.03
INDUS -0.0
CHAS -0.0
TAX -0.01
AGE -0.05
DIS -0.18

分類樹 和 森林

完全相同的方法可以用于分類樹,其中可以得到各個特征對于估計類別的貢獻大小。我們可以用iris數據集做一個例子。

from sklearn.ensemble import RandomForestClassifierfrom 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])
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=4, max_features='auto', max_leaf_nodes=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
            oob_score=False, random_state=None, verbose=0,
            warm_start=False)

對單個例子進行預測

instance = iris.data[idx][100:101]print rf.predict_proba(instance)
[[ 0.  0.  1.]]
prediction, bias, contributions = ti.predict(rf, instance)print "Prediction", predictionprint "Bias (trainset prior)", biasprint "Feature contributions:"for c, feature in zip(contributions[0], 
                             iris.feature_names):
    print feature, c
Prediction [[ 0.  0.  1.]]
Bias (trainset prior) [[ 0.33  0.32  0.35]]
Feature contributions:
sepal length (cm) [-0.04014815 -0.00237543  0.04252358]
sepal width (cm) [ 0.  0.  0.]
petal length (cm) [-0.13585185 -0.13180675  0.2676586 ]
petal width (cm) [-0.154      -0.18581782  0.33981782]

我們可以看到,對預測值是第二類影響力最大的是花瓣的長度和寬度,它們對更新之前的結果有最大影響。

總結

對隨機森林預測值的說明其實是很簡單的,與線性模型難度相同。通過使用treeinterpreter (pip install treeinterpreter),簡單的幾行代碼就可以解決問題。

翻譯:lan

來源: http://blog.datadive.net/random-forest-interpretation-with-scikit-learn/

 本文由用戶 yxli 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!