為什么機器學習模型會失敗?
點擊下方卡片,關注“新機器視覺”公眾號
視覺/圖像重磅干貨,第一時間送達
本文最初發(fā)表于 Towards Data Science 博客,經原作者 Delgado Panadero 授權,InfoQ 中文站翻譯并分享。
本文通過一個真實的例子,分析了模型選擇不當還是訓練數(shù)據(jù)噪聲導致了模型性能不佳。
在機器學習中,當你建立和訓練一個模型并檢驗其準確性時,一個最常見的問題就是“準確性是我能從數(shù)據(jù)中得到的最好的,還是能找到一個更好的模型呢?”
此外,一旦模型被部署,下一個常見的問題就是“為什么模型會失???”。有時候,這兩個問題都無法回答,但有時我們可以通過研究模型誤差的統(tǒng)計分布,找出預處理錯誤、模型偏差,以及數(shù)據(jù)泄露等。
在本教程中,我們將解釋并演示如何統(tǒng)計分析模型結果,以找出示例中錯誤的原因。
在這個案例中,我們將使用來自 Driven Data 競賽 的數(shù)據(jù),通過一系列社會經濟變量來預測一個民族是否處于貧困狀態(tài)。
這個業(yè)務案例的價值不僅在于能夠用機器學習模型來預測貧困狀況,而且還在于通過社會經濟變量對衡量貧困狀態(tài)的預測程度,并從特征上分析原因。
數(shù)據(jù)由一組九個描述性變量組成,其中四個是類別變量,另外五個是數(shù)值變量(但其中一個似乎是一個 id,所以我們將舍棄它)。
import pandas as pdpd.set_option('display.max_columns', None)train = pd.read_csv('train.csv', index_col='id')print(train)
返回結果如下:
Unnamed: 0 kjkrfgld bpowgknt raksnhjf vwpsxrgk omtioxzz yfmzwkruid29252 2225 KfoTG zPfZR DtMvg NaN 12.0 -3.098286 1598 ljBjd THHLT DtMvg esAQH 21.0 -2.049040 7896 Lsuai zPfZR zeYAm ZCIYy 12.0 -3.035261 1458 KfoTG mDadf zeYAm ZCIYy 12.0 -1.098833 1817 KfoTG THHLT DtMvg ARuYG 21.0 -4.0tiwrsloh weioazcf poorid29252 -1.0 0.5 False98286 -5.0 -9.5 True49040 -5.0 -9.5 True35261 -5.0 -9.5 False98833 -5.0 -9.5 True
數(shù)據(jù)分布可以在下面看到:

圖由作者提供。數(shù)據(jù)集中所有特征的配對圖,以目標為顏色。黃色塊代表 False,紫色塊表示 True。
通過某些預處理(NaN 值插補、縮放、分類編碼等等),我們將對一個支持向量機模型進行訓練(通常在獨熱編碼的高維數(shù)據(jù)中工作良好)。
from sklearn.pipeline import Pipelinefrom sklearn.preprocessing import RobustScalerfrom sklearn.neighbors import KNeighborsClassifiermodel = Pipeline(steps=preprocess+[('scaler', RobustScaler()),('estimator', KNeighborsClassifier(n_neighbors=5))])model.fit(X_train, y_train)y_pred = model.predict(X_test)print(classification_report(y_test,y_pred))`
返回結果如下:
precision recall f1-score supportFalse 0.73 0.77 0.75 891True 0.70 0.66 0.68 750accuracy 0.72 1641macro avg 0.72 0.71 0.71 1641weighted avg 0.72 0.72 0.72 1641
就二元分類問題而言,0.72 的準確率并不高。相比之下,召回率和查準率看起來是平衡的,這使得我們認為,這個模型不是一個有利于任何類別的先驗偏見。
想要改進這個模型,下一步就是嘗試其他機器學習模型和超參數(shù),看看我們是否找到任何可以提高性能的配置(甚至只是檢查性能是否保持穩(wěn)定)。
在不同的函數(shù)族集中,我們將使用另外兩個模型。KNN 模型,對于學習局部模型的影響是一個很好的選擇,還有梯度提升樹,它也是機器學習中容量最大的模型之一。
from sklearn.pipeline import Pipelinefrom sklearn.preprocessing import RobustScalerfrom sklearn.neighbors import KNeighborsClassifiermodel = Pipeline(steps=preprocess+[('scaler', RobustScaler()),('estimator', KNeighborsClassifier(n_neighbors=5))])model.fit(X_train, y_train)y_pred = model.predict(X_test)print(classification_report(y_test,y_pred))
返回結果如下:
precision recall f1-score supportFalse 0.71 0.74 0.72 891True 0.67 0.63 0.65 750accuracy 0.69 1641macro avg 0.69 0.69 0.69 1641weighted avg 0.69 0.69 0.69 1641
from sklearn.pipeline import Pipelinefrom sklearn.ensemble import GradientBoostingClassifiermodel = Pipeline(steps=preprocess+[('estimator',GradientBoostingClassifier(max_depth=5,n_estimators=100))])model.fit(X_train, y_train)y_pred = model.predict(X_test)print(classification_report(y_test,y_pred))
返回結果如下:
precision recall f1-score supportFalse 0.76 0.78 0.77 891True 0.73 0.70 0.72 750accuracy 0.74 1641macro avg 0.74 0.74 0.74 1641weighted avg 0.74 0.74 0.74 1641
我們可以看到,其他兩個模型的表現(xiàn)似乎都非常相似。這就提出了以下問題:
這就是我們用機器學習模型所能預測的最好結果嗎?
除了檢查性能的一般指標外,分析模型的輸出分布也很重要。不但要檢查測試數(shù)據(jù)集的分布,也要檢查訓練數(shù)據(jù)集的分布。這是因為我們不想看到模型的表現(xiàn),而是想看看它是否也學會了如何分割訓練數(shù)據(jù)。
import matplotlib.pyplot as pltpd.DataFrame(model.predict_proba(X_train))[1].hist()plt.show()

圖由作者提供。對訓練集進行評估的模型輸出分布。
pd.DataFrame(model.predict_proba(X_test))[1].hist()plt.show()

圖由作者提供。對測試集進行評估的模型輸出分布。
可見,預測為 0 的數(shù)量具有較高的峰值,這表示存在一個數(shù)據(jù)子集,模型非常確定它的標簽是 0,除此之外,分布看起來比較均勻。
如果模型知道一定要區(qū)分這兩個標簽,分布會有兩個峰值,一個在 0 附近,另一個在 1 附近。因此,我們可以看到,模型并沒有正確地學習模式來區(qū)分數(shù)據(jù)。
我們已經看到,該模型還沒有學會明確地區(qū)分這兩個類別,但我們還沒有看到它是否在不自信的情況下也能猜到預測結果,還是一直失敗。
此外,重要的是要檢查模型是否更傾向于一類或另一類的失敗。為檢驗這兩個方面,我們可以繪制預測值與目標值偏差的分布圖:
train_proba = model.predict_proba(X_train)[:,1]pd.DataFrame(train_proba-y_train.astype(int)).hist(bins=50)plt.show()

圖由作者提供。通過訓練集評估的模型置信度輸出與基準真相的偏差。
test_proba = model.predict_proba(X_test)[:,1]pd.DataFrame(test_proba-y_test.astype(int)).hist(bins=50)plt.show()

圖由作者提供。通過測試集評估的模型置信度輸出與基準真相的偏差。
從這兩張圖中,我們可以看到,偏差分布似乎是對稱的,并且以零點為中心。差距只是在零點,因為模型從來沒有返回 0 和 1 的準確值,所以我們不必擔心這個問題。
如果模型的誤差來自于訓練數(shù)據(jù)的統(tǒng)計 / 測量噪聲誤差,而不是偏置誤差,則我們會期望偏差分布遵循高斯分布。
這一分布與高斯分布相似,在零點處有一個較高的峰值,但這個峰值可能是因為模型預測的零點數(shù)量較多(也就是說,模型已經學會了一種模式來區(qū)分 0 和 1 類別的子集)。
由于訓練數(shù)據(jù)中存在的統(tǒng)計噪聲,我們必須確保模型預測的偏差符合高斯分布,然后才能證明其偏差。
import scipyscipy.stats.normaltest(train_proba-y_train.astype(int))
返回結果如下:
NormaltestResult(statistic=15.602215177113427, pvalue=0.00040928141243470884)當 P-value=0.0004 時,我們可以假設預測與目標的偏差遵循高斯分布,這樣從訓練數(shù)據(jù)中的噪聲導致模型誤差的理論是合理的。
如前所述,這一業(yè)務案例的目的不僅僅是要預測模型發(fā)生的原因,還包括與之相關的社會經濟變量。
可解釋的模型不僅能預測未見過的數(shù)據(jù),還能讓你了解特征如何影響模型(全局可解釋性),以及為什么某些預測會如此(局部可解釋性)。
盡管如此,一個模型的可解釋性仍然可以幫助我們理解為什么它能做出預測,以及為什么它會失敗。從梯度提升模型中,我們可以提取全局可解釋性如下:
cols = X_train.columnsvals= dict(model.steps)['estimator'].feature_importances_plt.figure()plt.bar(cols, vals)plt.show()

圖由作者提供。梯度提升特征輸入。
接下來,我們將進行相同的特征重要性分析,但是只對數(shù)據(jù)的一個子集進行訓練。具體地說,我們將只使用明顯為零的數(shù)據(jù)(那些模型之前明確預測為零的數(shù)據(jù))來訓練模型的零類別。
zero_mask = model.predict_proba(X_train)[:,1]<=0.1one_mask = y_train==1mask = np.logical_or(zero_mask,one_mask)X_train = X_train.loc[mask,:]y_train = y_train.loc[mask]model.fit(X_train,y_train)
現(xiàn)在特征的重要性是:

圖由作者提供。在模型表現(xiàn)最好的訓練集子樣本上訓練的梯度提升特征導入。
我們可以看到,現(xiàn)在,tiwrsloh和 yfmzwkru 這兩個變量的重要性增加了,而vwpsxrgk 的數(shù)值卻下降了。這意味著,擁有一個子集的人口顯然不是窮人(類別 0),可以通過這兩個變量從窮人的變量和 vwpsxrgk在許多情況下可能很重要,但不具備決定性。
如果我們繪制這兩個特征的過濾值,我們可以看到:

圖由作者提供,對模型明確檢測到非貧困的特征區(qū)域進行分割并表征。
對于這兩個特征,模型已經學會了區(qū)分兩個類別,同時,對于這些變量的其他值,在整個數(shù)據(jù)集中,類別 0 和類別 1 是混合的,所以不能明確區(qū)分。
我們還可以從前面的圖表中找出一個明顯的非貧困人口子集的特征,即tiwrsloh<0 和 yfmzwkru<-2 的人。
我們分析了在給定的數(shù)據(jù)集中檢測貧困的問題,并在給定的社會經濟數(shù)據(jù)中分析其原因,發(fā)現(xiàn)貧困并不容易預測,但是,我們可以對某些特定數(shù)據(jù)進行定義,以明確確定人們的貧困狀況:tiwrsloh<0 和 yfmzwkru<-2。
我們嘗試了許多不同的模型和配置,在 0.75 的時候性能就會處于平穩(wěn)狀態(tài)。通過這一點,再加上模型預測和誤差偏差分布的統(tǒng)計學性質,我們可以得出結論,問題在于缺乏從訓練數(shù)據(jù)中預測目標的能力。所以不可能建立一個更好的模型。
我們用一個真實的例子解決了模型不能得到足夠好的結果的問題。在這種情況下,我們的目標是嘗試了解模型無法理解的問題是在數(shù)據(jù)中還是在模型中。
回答這一問題的過程是:
嘗試不同的函數(shù)族模型和超參數(shù),并確認所有的下降在性能上處于平穩(wěn)狀態(tài)。
為得到最好的解釋,計算模型的輸出分布和目標的偏差分布。如果數(shù)據(jù)是問題所在,則輸出必須是均勻的,并且偏差必須遵循高斯分布。
盡管數(shù)據(jù)是問題所在,但試著從模型輸出和偏差分布中找到一個模型表現(xiàn)良好的區(qū)域。嘗試對這個區(qū)域進行分割和定性,例如,用這個子集重新訓練模型,并提取其可解釋性。
此外,在表征某些子集時,我們可以嘗試從業(yè)務知識中思考,問題是來自數(shù)據(jù)的統(tǒng)計 / 測量噪音,還是來自缺乏預測因變量所需的一些特征值。
作者介紹:
Delgado Panadero,研究人工智能的物理學家。
原文鏈接:
https://towardsdatascience.com/my-ml-model-fails-why-is-it-the-data-d8fbfc50c254
