用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/