Contact me:

Blog : cugtyt.github.io/blog/i

Email: cugtyt#qq.com


譯自

Intuitive Interpretation of Random Forest?

medium.com

1. How important are our features ?

在sklearn隨機森林中使用model.feature_importances來研究重要特徵是很常見的。重要的特徵意味著與因變數更密切相關的特徵,並且對因變數的變化貢獻更多。我們通常儘可能多地為隨機森林模型提供特徵,並讓演算法返回它發現對預測最有用的特徵列表。但仔細選擇正確的特徵可以使我們的目標預測更準確。

計算feature_importances的想法很簡單,也很有效。步驟:

  1. 訓練隨機森林模型(假設有正確的超參數)
  2. 確定模型的預測分數(稱為基準分數)
  3. 模型預測p次,其中p是特徵數,每次隨機打亂第i列特徵
  4. 將p次分數與基準分數進行比較。如果隨機打亂某些列會損害分數,這意味著模型在沒有該特徵的情況下會很糟糕
  5. 刪除不影響基準分數的特徵,並使用刪除後的特徵子集重新訓練模型

用於計算特徵重要性的代碼,下面的代碼將為所有特徵提供{feature,importance}的字典:

# defining rmse as scoring criteria (any other criteria can be used in a similar manner)
def score(x1,x2):
return metrics.mean_squared_error(x1,x2)

# defining feature importance function based on above logic
def feat_imp(m, x, y, small_good = True):
"""
m: random forest model
x: matrix of independent variables
y: output variable
small__good: True if smaller prediction score is better
"""
score_list = {}
score_list[『original』] = score(m.predict(x.values), y)
imp = {}
for i in range(len(x.columns)):
rand_idx = np.random.permutation(len(x)) # randomization
new_coli = x.values[rand_idx, i]
new_x = x.copy()
new_x[x.columns[i]] = new_coli
score_list[x.columns[i]] = score(m.predict(new_x.values), y)
imp[x.columns[i]] = score_list[『original』] — score_list[x.columns[i]] # comparison with benchmark
if small_good:
return sorted(imp.items(), key=lambda x: x[1])
else: return sorted(imp.items(), key=lambda x: x[1], reverse=True)

輸出:

importance = feat_imp(ens, X_train[cols], y_train); importance

[(YearMade, -0.21947050888595573),
(Coupler_System, -0.21318328275792894),
(ProductSize, -0.18353291714217482),
(saleYear, -0.045706193607739254),
(Enclosure, -0.041566508577359523),
(MachineID, -0.01399141076436905),
(MachineHoursCurrentMeter, -1.9246700722952426e-05)]

In above output, YearMade increases prediction RMSE most if it gets
shuffled (proxy to getting removed from model).
So it must be most important feature.

(above results correspond to data taken from a Kaggle competition.
Here is the link - https://www.kaggle.com/c/bluebook-for-bulldozers)

2. How confident are we about our predictions ?

通常,當企業想要預測某些事物時,他們的最終目標是降低成本或提高利潤。在做出重大商業決策之前,企業有興趣估計做出決定的風險。但是,如果預測結果沒有置信區間,那麼不僅沒有降低風險,還可能會無意中將業務暴露出更大的風險。

當我們使用線性模型(基於分佈假設的模型)時,相對容易找到我們預測的置信水平。但是當談到隨機森林的置信區間時,相對而言就不那麼直接了。

我想,任何參加線性回歸課程的人都看過這張圖片:

為了找到最佳線性模型,我們尋找能平衡偏差-方差的模型。該圖很好地說明瞭預測中偏差和方差的定義。如果高偏差低方差(第3個),那麼一直遠離靶心擊中飛鏢。相反,如果低偏差高方差(第2個),擊中就非常不一致。現在,把這個例子類比於在現實生活中捕捉信用欺詐。如果信用公司有類似於第二個的預測模型,那這個公司可能在大多數情況下不會捕獲到欺詐,即使平均模型預測是正確的。

需要注意的是,我們不僅僅應該檢查平均預測,還應該檢查預測點的置信水平。

How to do that in random forest ?

隨機森林由多個決策樹組成(由n_estimators給出)。每棵樹單獨預測新數據,隨機森林吐出這些樹的平均預測。置信水平的想法只是為了看到來自不同樹的預測對於新觀察而言的變化情況。為了進一步分析,可以針對具有最大變化的觀測尋找一些模式(類似於2011年對應的預測具有較大的變化)。

基於樹的變化情況來分析預測置信水平:

#Pseudo code:
def pred_ci(model, x_val, percentile = 95, n_pnt):

"""
x_val = validation input
percentile = required confidence level
model = random forest model
"""

allTree_preds = np.stack([t.predict(x_val) for t in model.estimators_], axis = 0)

err_down = np.percentile(allTree_preds, (100 - percentile) / 2.0 ,axis=0)
err_up = np.percentile(allTree_preds, 100- (100 - percentile) / 2.0 ,axis=0)

ci = err_up - err_down
yhat = model.predict(x_val)
y = y_val

df = pd.DataFrame()
df[down] = err_down
df[up] = err_up
df[y] = y
df[yhat] = yhat
df[deviation] = (df[up] - df[down])/df[yhat]
df.reset_index(inplace=True)
df_sorted = df.iloc[np.argsort(df[deviation])[::-1]]
return df_sorted

輸出:

從這個輸出中可以看出,我們對index=14的觀察預測最不自信。

3. What is the prediction path? (Tree Interpreter)

如果我們想要分析哪些特徵對整個隨機森林模型很重要,那麼特徵重要性(第1部分)是及其有用的。但是如果我們對一個特定的觀察感興趣,那麼樹解釋器就會發揮作用。

例如,有一個隨機森林模型用來預測:來醫院的患者X是否具有很高的再入院概率?為簡單起見,我們假設只有3個特徵:血壓數據,年齡和性別。現在,如果模型說患者A有80%的再入院概率,我們怎麼知道模型為什麼會認為患者A會再次入院?在這種情況下,樹解釋器給出了該特定患者遵循的預測路徑。這有點像這種情況,因為患者A是65歲的男性,所以模型預測他將再次入院。模型預測再入院的患者B可能是因為B患有高血壓(不是因為年齡或性別)。

基本上,樹解釋器給出了偏差(起始節點處的數據平均值)的排序列表,以及單個節點對預測的貢獻。下圖的決策樹(深度為3)基於波士頓住房價格數據集的模型。它根據中間節點的預測值和導致值發生變化的特徵顯示決策路徑的細分變化。節點的貢獻是該節點處的值與前一節點處值的差異。

下圖給出了對患者A使用樹解釋器的示例輸出。它表示65歲是模型預測再入院概率高於平均值的最高貢獻者。

也可以使用瀑布圖來可視化它。

繪製它使用的是waterfallcharts包,代碼是:

from waterfallcharts import quick_charts as qc
a = [『Bias』, 『Age』, 『Sex』, 『Blood Pressure』]
b = [0.3, 0.6, -0.1, -0.2]
plot = qc.waterfall(a,b, Title= 『Patient A』, y_lab= 『Predicted probability』, x_lab= 『Contributing features (path)』,
net_label = 『Final Prediction』)
plot.show()

術語:

  • 表示節點預測的目標值(觀測樣本落在該節點的的均值)。
  • 貢獻值是當前節點值減去前一節點的值(這是特徵為該路徑提供的貢獻)。
  • 路徑是一些所有特徵分割直到葉節點的組合。

來自treeinterpreter包的函數非常簡單,可以從每個節點獲取貢獻值,包的地址在這裡。

4. How is target variable related with important features? (Partial Dependence Plots)

找到最重要的特徵之後,接下來我們可能感興趣的是:研究目標變數和感興趣特徵之間的直接關係。這類似於線性回歸的模型係數。對於線性回歸而言,我們可以這樣解釋係數:「保持所有其他X(i)不變的情況下,X(j)變化1個單位那麼Y會怎麼變化。」

雖然可以從隨機森林獲取特徵的重要性,但是它們僅相對於X(i)的變化給出Y的相對變化。不能直接將它們解釋為:使其他特徵保持不變的情況下,X(j)的單位變化導致多大的Y變化。

幸運的是,我們有部分依賴圖(PDP),它可以看作線性模型係數的圖形化表示,但也可以擴展到黑盒模型上。我們的想法是將預測的更改限制在某個特定的特徵上。它與X和Y的散點圖是不同的,因為散點圖不能隔離X與Y的直接關係,並且可能受到與X和Y所依賴的其他變數的間接影響。

製作PDP圖的步驟如下:

  1. 訓練隨機森林模型(假設F1 … F4是特徵,Y是目標變數。假設F1是最重要的特徵)。
  2. 我們希望探索Y和F1的直接關係
  3. 用F1(A)代替F1列,並獲取所有觀測值的新預測。獲取預測的均值,稱作基線值。
  4. 對F1(B)… F1(E)重複步驟3,即所有不同於F1的特徵。
  5. PDP的X軸是F1的離散值,Y軸是預測均值與基線值的差值。

下圖是部分依賴圖的例子(在bulldozer上做的)。它顯示了YearMade與SalesPrice的關係。

以下是SalePrice與YearMade的線圖。可以看到散點/線圖可能無法像PDP那樣捕捉到YearMade對SalesPrice的直接影響。

推薦閱讀:

相關文章