通过随机森林的例子解释特征重要性
https://www.toutiao.com/a6657142683347190284/
?
2019-02-12 23:53:35
在許多(業務)案例中,同樣重要的是不僅要有一個準確的機器學習模型,還要有一個可解釋的機器學習模型。通常,除了想知道我們的機器學習模型的房價預測是什么之外,我們還想知道為什么它是這么高/低,以及哪些特征在確定預測時最重要。另一個例子是預測客戶流失 - 擁有一個能夠成功預測哪些客戶容易流失的機器學習模型是非常好的,但確定哪些變量很重要可以幫助我們及早發現甚至改進產品/服務!
了解機器學習模型的特征重要性可以通過多種方式使您受益,例如:
- 通過更好地理解機器學習模型的邏輯,您不僅可以驗證它是正確的,還可以通過僅關注重要變量來改進模型
- 您可以刪除不那么重要的x變量,并且在更短的訓練時間內具有類似或更好的性能
- 在某些商業案例中,為了解釋性而犧牲一些準確性是有道理的。
這就是為什么在本文中,我想通過一個隨機森林模型的例子來探索不同的方法來解釋特征的重要性。它們中的大多數也適用于不同的模型,從線性回歸開始,以XGBoost等黑盒結束。
需要注意的一點是,我們的模型越精確,我們就越能夠信任特征重要性度量和其他解釋。我假設我們構建的模型相當準確(因為每個數據科學家都會努力建立這樣的模型),在本文中我將重點關注重要性度量。
數據
對于這個例子,我將使用波士頓房價數據集(所以回歸問題)。但是本文中描述的方法與分類問題一樣,唯一的區別是用于評估的度量。Python代碼如下:
import pandas as pd from sklearn.datasets import load_boston boston = load_boston() y = boston.target X = pd.DataFrame(boston.data, columns = boston.feature_names) np.random.seed(seed = 42) X['random'] = np.random.random(size = len(X)) X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size = 0.8, random_state = 42)?
下面我檢查隨機特征和目標變量之間的關系。可以看出,散點圖上沒有模式,相關性幾乎為0。
sns.scatterplot(x = 'random', y = 'target', data = X.assign(target = y)).set_title('Random feature vs. target variable', fontsize = 16)?
sns.heatmap(X.assign(target = y).corr().round(2), cmap = 'Blues', annot = True).set_title('Correlation matrix', fontsize = 16)?
這里需要注意的一點是,為CHAS解釋相關性沒有多大意義,因為它是一個二元變量,應該使用不同的方法。
基準模型
我訓練一個普通的隨機森林模型來獲得一個基準。我設置了一個random_state以確保結果的可比性。我也使用bootstrap并設置,oob_score = True以便稍后我可以使用out-of-bag error。
簡單地說,隨機森林中的每棵樹都在不同的數據集上進行訓練,從原始數據中進行替換取樣。這導致每個訓練集中大約有2/3的不同觀測值。out-of-bag誤差是根據所有觀測值計算的,但是對于計算每一行的誤差,模型只考慮在訓練過程中沒有看到這一行的樹。這類似于在驗證集上對模型進行評估。查看如下Python代碼:
from sklearn.ensemble import RandomForestRegressor rf = RandomForestRegressor(n_estimators = 100,n_jobs = -1,oob_score = True,bootstrap = True,random_state = 42) rf.fit(X_train, y_train) print('R^2 Training Score: {:.2f} OOB Score: {:.2f} R^2 Validation Score: {:.2f}'.format(rf.score(X_train, y_train), rf.oob_score_,rf.score(X_valid, y_valid)))?
R^2 Training Score: 0.93
OOB Score: 0.58
R^2 Validation Score: 0.76
模型中存在一些過度擬合,因為它在OOB樣本上和驗證集上的表現都差得多。但我們假設它已經足夠好了,接下來是特征重要性(通過訓練集的性能來衡量)。一些方法還可以用于驗證/OOB集,以獲得對不可見數據的進一步解釋性。
特征重要性
默認Scikit-learn的特征重要性
讓我們從決策樹開始建立一些直覺。在決策樹中,每個節點都是在一個特征中拆分值的條件,這樣在拆分后,相類似的因變量值就會在同一個集合中結束。該條件基于雜質,分類問題為基尼雜質/信息增益(熵),回歸樹為其方差。因此,當訓練一棵樹時,我們可以計算出每個特征對減少加權雜質的貢獻有多大。Scikit-Learn中的feature_importances_是基于這個邏輯的,但是在隨機森林中,我們正在討論的是對樹上的雜質減少進行平均。
優點:
- 快速計算
- 易于檢索
缺點:
- 有偏見的方法,因為它傾向于夸大連續特征或高基數分類變量的重要性
?
?
似乎最重要的三大特征是:
- RM:平均房間數量
- LSTAT:人口lower status的百分比
- DIS:到波士頓五個就業中心的加權距離
然而,令人驚訝的是,一列隨機值竟然比以下內容更重要:
- INDUS:城鎮非零售商業用地比例
- RAD:徑向高速公路的可達性指數
- ZN:占地面積超過25,000平方尺的住宅用地比例。
- CHAS:Charles River虛擬變量(如果有河= 1;否則為0)
直觀地說,這個特征對目標變量的重要性應該是零。讓我們看看它是如何通過不同的方法進行評估的。
排列特征重要性
這種方法通過觀察每個預測器的隨機re-shuffling(從而保持變量的分布)如何影響模型性能來直接度量特征的重要性。
該方法可按以下步驟描述:
優點:
- 適用于任何模型
- 合理有效
- 可靠的技術
- 無需在每次修改數據集時重新訓練機器學習模型
缺點:
- 比默認的feature_importances在計算上更昂貴
- 排列重要性高估了相關預測因子的重要性
我將可視化Spearman的相關性。標準皮爾遜相關性的不同之處在于,它首先將變量轉換為秩,然后在秩上運行皮爾遜相關性。
Spearman相關性:
- 是非參數的
- 不假設變量之間存在線性關系
- 它尋找單調的關系。
?
?
rfpimp:關于這個庫需要注意的一點是,我們必須我們必須以metric(model, X, y)的形式提供一個度量,這樣我們就可以使用更高級的方法,比如使用隨機森林的OOB評分。這個庫已經包含了相應的函數(oob_regression_r2_score)。但是為了保持方法的一致性,我將計算訓練集上的度量(失去了關于泛化的信息)。
from sklearn.metrics import r2_score from rfpimp import permutation_importances def r2(rf, X_train, y_train):return r2_score(y_train, rf.predict(X_train)) perm_imp_rfpimp = permutation_importances(rf, X_train, y_train, r2) perm_imp_rfpimp.reset_index(drop = False, inplace = True) var_imp_plot(perm_imp_rfpimp, 'Permutation feature importance (rfpimp)')?
?
該圖證實了我們上面所看到的,4個變量不如隨機變量重要!令人驚訝的是......前4名保持不變。關于`rfpimp`的一個更好的特性是它包含處理共線特征問題的功能(這是顯示Spearman相關矩陣背后的想法)。
eli5:rfpimp的基本方法和eli5的方法有一些不同。其中一些是:
- 有參數cv和refit連接到使用交叉驗證。在這個例子中,我將它們設置為None,因為我不使用它,但在某些情況下它可能會派上用場。
- 有一個metric參數,在rfpimp中接受metric(model, X, y)形式的函數。如果未指定此參數,則該函數將使用score估計器的默認方法。
- n_iter - 隨機shuffle迭代次數,最終得分是平均值
?
?
結果與前面的結果非常相似,盡管這些結果來自于每列的多次reshuffles。eli5的另一個優點是,通過使用Scikit-learn的SelectFromModel或RFE,可以很容易地使用置換方法的結果進行特征選擇。
刪除列特征重要性
這種方法非常直觀,因為我們通過將模型與所有特征進行比較來研究特征的重要性,而將模型與此特征進行比較以進行訓練。
我為下面的方法創建了一個函數(基于rfpimp的實現),它顯示了底層邏輯。
優點:
- 最準確的特征重要性
缺點:
- 由于為數據集的每個變體重新訓練模型而導致潛在的高計算成本(在刪除單個特征列之后)
?
?
這里很有趣。首先,在這種情況下的負面重要性意味著從模型中刪除給定的特征實際上提高了性能。這在隨機情況下是很好的,但奇怪的是,在刪除DIS之后可以觀察到最高的性能提升,DIS是之前方法中第三個最重要的變量。不幸的是,我沒有一個很好的解釋。
觀察水平特征重要性
通過觀察水平特征重要性,我指的是對解釋給予模型的特定觀察具有最大影響的那些。例如,在信用評分的情況下,我們可以說這些特征對確定客戶的信用評分影響最大。
Treeinterpreter
treeinterpreter的主要思想是使用隨機森林中的底層樹來解釋每個特性如何貢獻最終值。我們可以觀察到預測的值(定義為每個特征貢獻的總和+基于整個訓練集的初始節點給出的平均值)在決策樹中的預測路徑上(每次分割之后)是如何變化的,
以及導致分裂的信息(預測的變化也是如此)。
預測函數的公式(f(x))可以寫為:
?
其中c_full是整個數據集(初始節點)的平均值,K是特征的總數。
這可能聽起來很復雜,但請看一下庫作者的一個例子:
?
由于隨機森林的預測是樹的平均值,因此平均預測的公式如下:
?
其中J是森林中樹的數量
我首先確定具有最低和最高絕對預測誤差的行,并嘗試查看導致差異的原因。Python實現如下:
pred_diff = pd.DataFrame({'difference': abs(y_train - rf.predict(X_train))}) print('Index with smallest error:', pred_diff.sort_values('difference').head(1).index.values[0]) print('Index with largest error:', pred_diff.sort_values('difference', ascending = False).head(1).index.values[0])?
Index with smallest error: 31
Index with largest error: 85
使用treeintrerpreter I獲得3個對象:預測、偏差(數據集的平均值)和貢獻。
for i in range(len(selected_rows)):print("Row", selected_rows[i])print("Prediction:", prediction[i][0], 'Actual Value:', y_train[selected_rows[i]])print("Bias (trainset mean)", bias[i])print("Feature contributions:")for c, feature in sorted(zip(contributions[i], X_train.columns), key=lambda x: -abs(x[0])):print(feature, round(c, 2))print("-"*20)?
對于誤差最小的觀察,主要的影響因素是LSTAT和RM(在以前的例子中,這是最重要的變量)。在錯誤最高的情況下,最大的貢獻來自DIS變量。
?
為了更深入地研究,我們可能還會對許多變量的聯合貢獻感興趣(如這里的XOR所解釋的)。
prediction1, bias1, contributions1 = ti.predict(rf, np.array([selected_df[0]]), joint_contribution=True) prediction2, bias2, contributions2 = ti.predict(rf, np.array([selected_df[1]]), joint_contribution=True) aggregated_contributions1 = utils.aggregated_contribution(contributions1) aggregated_contributions2 = utils.aggregated_contribution(contributions2) res = [] for k in set(aggregated_contributions1.keys()).union(set(aggregated_contributions2.keys())):res.append(([X_train.columns[index] for index in k] , aggregated_contributions1.get(k, 0) - aggregated_contributions2.get(k, 0))) for lst, v in (sorted(res, key=lambda x:-abs(x[1])))[:10]:print (lst, v)?
['RM', 'DIS'] [-3.65430351]
['RM', 'DIS', 'LSTAT'] [-2.15540094]
['CRIM', 'LSTAT'] [-1.65588589]
['DIS', 'LSTAT'] [-1.64678593]
['NOX', 'LSTAT'] [-1.28406056]
['RM', 'TAX', 'LSTAT'] [-1.00245007]
['CRIM', 'RM', 'LSTAT'] [-0.62160057]
['TAX', 'LSTAT'] [-0.55742577]
['RM', 'LSTAT'] [-0.45002332]
['B', 'LSTAT'] [-0.3516441]
最佳和最差預測案例之間的差異主要來自房間(RM)特征的數量,以及到五個波士頓就業中心的加權距離(DIS)。
LIME
LIME (Local interpretation table Model-agnostic interpretation)是一種以可解釋任何分類器/回歸器的預測的技術。要做到這一點,可以通過使用可解釋的模型(如帶正則化的線性模型或決策樹)局部逼近所選模型來獲得解釋。可解釋模型是在原始觀測(表格數據行)的小擾動(加噪聲)上進行訓練的,因此只能提供良好的局部逼近。
要注意的一些缺點:
- 只有線性模型用于近似局部行為
- 需要對數據執行以獲得正確解釋的擾動類型通常是特定于用例的
- 簡單的(默認的)擾動通常是不夠的。在理想的情況下,修改將由數據集中觀察到的變化驅動
您可以在下面看到LIME解釋的輸出。
輸出有3個部分:
- 預測值
- 特征重要性 - 在回歸的情況下,它顯示它是否對預測有負面或正面影響,按絕對影響下降排序。
- 這些特征的實際值用于解釋行。
請注意,LIME已將解釋中的特征離散化。這是因為在上面的構造函數中設置了`discretize_continuous = True`。離散化的原因在于它為連續特征提供了更直觀的解釋。
import lime import lime.lime_tabular explainer = lime.lime_tabular.LimeTabularExplainer(X_train.values,mode = 'regression',feature_names = X_train.columns,categorical_features = [3], categorical_names = ['CHAS'], discretize_continuous = True)np.random.seed(42) exp = explainer.explain_instance(X_train.values[31], rf.predict, num_features = 5) exp.show_in_notebook(show_all=False) #only the features used in the explanation are displayed exp = explainer.explain_instance(X_train.values[85], rf.predict, num_features = 5) exp.show_in_notebook(show_all=False)?
?
LIME解釋認為,對于這兩種觀測結果,最重要的特征是RM和LSTAT,這一點在之前的方法中也有體現。
結論
在本文中,我展示了一些從機器學習模型(不限于隨機森林)中導出特征重要性的方法。我相信理解結果通常與獲得良好結果一樣重要,因此每個數據科學家都應盡力了解哪些變量對模型最重要,以及為什么。這不僅有助于更好地了解業務,還可以進一步改進機器學習模型。
總結
以上是生活随笔為你收集整理的通过随机森林的例子解释特征重要性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: webjs--实现多图片的上传
- 下一篇: GOOGEL翻译软件测试,测试一把goo