模型解釋–SHAP Value的簡(jiǎn)單介紹

https://mathpretty.com/10699.html

摘要

這一篇文章主要介紹一下關(guān)于SHAP方法,會(huì)介紹關(guān)于Shapley Value的計(jì)算方式,通過(guò)舉例進(jìn)行說(shuō)明。接著會(huì)舉一個(gè)實(shí)際的例子,來(lái)說(shuō)明如何使用SHAP來(lái)進(jìn)行模型的解釋,和對(duì)最后結(jié)果的可視化展示。

簡(jiǎn)介

這一篇是關(guān)于模型可解釋性,文章會(huì)介紹關(guān)于SHAP Value的方法。同時(shí)介紹一下如何簡(jiǎn)單的繪制結(jié)果。還是把參考資料放在最前面。

在看這個(gè)之前, 建議先查看LIMEShapley Value的相關(guān)的內(nèi)容. 鏈接分別如下:

模型解釋-LIME的原理和實(shí)現(xiàn)

模型解釋–Shapley Values

我也是用SHAP的思想, 做了用于入侵檢測(cè)系統(tǒng)的結(jié)果的解釋問(wèn)題, 鏈接如下:An Explainable Machine Learning Framework for Intrusion Detection Systems

參考資料

一本很全的資料(這是一份很好的資料)?:?Interpretable Machine Learning

整體了解SHAP(整體結(jié)構(gòu)參考了這篇文章)?:?Interpreting your deep learning model by SHAP

也是一個(gè)很好的資料(對(duì)SHAP那篇文章做了解釋,?值得一看):?One Feature Attribution Method to (Supposedly) Rule Them All: Shapley Values

關(guān)于NLP模型的解釋 :3 ways to interpretate your NLP model to management and customer

SHAP的幫助文檔 :SHAP (SHapley Additive exPlanations)


SHAP Value方法的介紹

SHAP的目標(biāo)就是通過(guò)計(jì)算x中每一個(gè)特征對(duì)prediction的貢獻(xiàn), 來(lái)對(duì)模型判斷結(jié)果的解釋. SHAP方法的整個(gè)框架圖如下所示:

SHAP Value的創(chuàng)新點(diǎn)是將Shapley Value和LIME兩種方法的觀點(diǎn)結(jié)合起來(lái)了.?One innovation that SHAP brings to the table is that the Shapley value explanation is represented as an additive feature attribution method, a linear model.?That view connects LIME and Shapley Values.

SHAP解釋的時(shí)候使用下面的表達(dá)式, 這個(gè)和LIME中的原理是相似的(最大的不同是SHAP中設(shè)置了不同的distance的定義, 我們后面會(huì)講):

上面的Simplified Features取值只能是{0, 1},?當(dāng)其取值為1的時(shí)候, 表示的是這個(gè)特征是present(也就是這個(gè)特征的取值與我們要解釋的instance x中對(duì)應(yīng)的特征是相同的), 當(dāng)其特征是0的時(shí)候, 表示這個(gè)特征是absent(這時(shí)候我們從數(shù)據(jù)集中取一個(gè)這個(gè)特征的值, 其他數(shù)據(jù)在這個(gè)特征上的取值).

對(duì)于我們要解釋的instance x來(lái)說(shuō), 也就是所有的Simplified Features都是1. 此時(shí)上面的式子可以化簡(jiǎn)為:

這個(gè)時(shí)候,?就可以看成每一個(gè)特征的Shapley Value的和做出的貢獻(xiàn), 使得預(yù)測(cè)結(jié)果從均值變?yōu)槲覀冾A(yù)測(cè)的結(jié)果, 也就是g(x').

在原始論文中, 作者提出了KernelSHAP和TreeSHAP.?下面我們只詳細(xì)介紹KernelSHAP.


KernelSHAP的簡(jiǎn)單介紹

KernelSHAP包含下面的5個(gè)步驟:

初始化一些數(shù)據(jù),?z', 作為Simplified Features, 例如隨機(jī)生成(0, 1, 0, 1), (1, 1, 1, 0)等.

將上面的Simplified Features轉(zhuǎn)換到原始數(shù)據(jù)空間, 并計(jì)算對(duì)應(yīng)的預(yù)測(cè)值,?f(h(z')).

對(duì)每一個(gè)z'計(jì)算對(duì)應(yīng)的權(quán)重(這里權(quán)重的計(jì)算是關(guān)鍵, 也是SHAP與LIME不同的地方)

擬合線性模型

計(jì)算出每一個(gè)特征的Shapley Value, 也就是線性模型的系數(shù).

對(duì)于上面的第二個(gè)步驟, 我們做一下解釋, 當(dāng)Simplified Features是1的時(shí)候, 使用原始數(shù)據(jù)的特征, 當(dāng)Simplified Features是0的時(shí)候, 我們使用其他的數(shù)據(jù)進(jìn)行替換(這種置換的方式會(huì)存在一些問(wèn)題, 當(dāng)特征之間不是獨(dú)立的, 而是相互有關(guān)聯(lián)的, 那么這樣生成的數(shù)據(jù)可能是實(shí)際上不存在的), 如下圖所示.

下面就是重點(diǎn)介紹對(duì)z'權(quán)重的計(jì)算了, 這個(gè)也是SHAP最大的不同. (The big difference to LIME is the weighting of the instances in the regression model.)

LIME根據(jù)相似度進(jìn)行計(jì)算距離,?如sample的數(shù)據(jù)中有很多0, 也就是會(huì)和原始數(shù)據(jù)有很大的不同, 那么他的距離是遠(yuǎn)的, 也就是他的權(quán)重會(huì)是小的. (LIME weights the instances according to how close they are to the original instance. The more 0's in the coalition vector, the smaller the weight in LIME.)

SHAP中距離的計(jì)算根據(jù)Simplified Features中0的數(shù)量, 若有很多0或是很多1, 他的權(quán)重都是比較高的.

這是因?yàn)槿舳际?, 只有一個(gè)是1, 那么我們可以很好的計(jì)算出那個(gè)是1的特征的貢獻(xiàn).

若只有一個(gè)是0, 我們可以計(jì)算出那個(gè)是0的特征的貢獻(xiàn).

如果一半是0, 一半是1, 那么會(huì)有很多種組合, 就很難計(jì)算出每一個(gè)特征的貢獻(xiàn)(下面是原文).

SHAP weights the sampled instances according to the weight the coalition would get in the Shapley value estimation.?Small coalitions (few 1's) and large coalitions (i.e. many 1's) get the largest weights. The intuition behind it is: We learn most about individual features if we can study their effects in isolation.

If a coalition?consists of a single feature, we can learn about the features' isolated main effect on the prediction.

If a coalition?consists of all but one feature, we can learn about this features' total effect (main effect plus feature interactions).

If a coalition consists of half the features, we learn little about an individual features contribution, as there are many possible coalitions with half of the features.

所以最終的SHAP kernel中的weight的計(jì)算公式如下:

使用這個(gè)weight和線性回歸, 計(jì)算出的結(jié)果就是Shapley Value. (If you would use the SHAP kernel with LIME on the coalition data, LIME would also estimate Shapley values!)

到這里為止, 我們就有了data, target和weight來(lái)計(jì)算我們需要的線性回歸的式子(也就是下面的式子).

我們可以通過(guò)減少下面function的loss的方法, 來(lái)得到上面的回歸方程的系數(shù)(也就是Shapley Value). (The estimated coefficients of the model are the Shapley values.)


SHAP的簡(jiǎn)單使用方法

說(shuō)完上面的SHAP的方法介紹之后, 我們看一下在具體使用場(chǎng)景中, 我們需要如何來(lái)進(jìn)行實(shí)現(xiàn),. 我們可以直接使用python中的SHAP的庫(kù)來(lái)進(jìn)行實(shí)現(xiàn).

項(xiàng)目地址(這里面會(huì)有最詳細(xì)的介紹, 下面只是簡(jiǎn)單介紹一下我使用到的功能):?SHAP

SHAP provides multiple explainers for different kind of models.

TreeExplainer?: Support XGBoost, LightGBM, CatBoost and scikit-learn models by Tree SHAP.

DeepExplainer (DEEP SHAP)?: Support TensorFlow and Keras models by using DeepLIFT and Shapley values.

GradientExplainer?: Support TensorFlow and Keras models.

KernelExplainer (Kernel SHAP)?: Applying to any models by using?LIME and Shapley values.

一些函數(shù)具體的使用方式, 可以查看SHAP的幫助文檔, 文檔的地址如下:

SHAP的幫助文檔 :SHAP (SHapley Additive exPlanations)

下面我們來(lái)看一下具體的使用例子, 來(lái)看一下使用SHAP如何來(lái)進(jìn)行模型的解釋.


需要注意--出現(xiàn)報(bào)錯(cuò)的解決

在使用Pytorch進(jìn)行模型的創(chuàng)建和解釋的時(shí)候, 我們需要對(duì)模型創(chuàng)建的時(shí)候進(jìn)行注意, 需要使用Sequential的方式進(jìn)行創(chuàng)建. 否則在解釋的時(shí)候會(huì)出現(xiàn)問(wèn)題. 參考資料:?PyTorch Deep Explainer

我們創(chuàng)建模型的時(shí)候應(yīng)該使用下面的方式進(jìn)行創(chuàng)建, 即使用Sequential的方式進(jìn)行創(chuàng)建.

classNet(nn.Module):

def__init__(self):

super(Net,self).__init__()

self.conv_layers?=?nn.Sequential(

????????????nn.Conv2d(1,?10,?kernel_size=5),

????????????nn.MaxPool2d(2),

????????????nn.ReLU(),

????????????nn.Conv2d(10,?20,?kernel_size=5),

????????????nn.Dropout(),

????????????nn.MaxPool2d(2),

????????????nn.ReLU(),

????????)

self.fc_layers?=?nn.Sequential(

????????????nn.Linear(320,?50),

????????????nn.ReLU(),

????????????nn.Dropout(),

????????????nn.Linear(50,?10),

????????????nn.Softmax(dim=1)

????????)

defforward(self,?x):

x?=self.conv_layers(x)

x?=?x.view(-1,?320)#?doesn't?affect?the?gradient,?so?can?be?outside?an?nn.Module?object

x?=self.fc_layers(x)

returnx

而不要使用下面的方式進(jìn)行創(chuàng)建.

classNet(nn.Module):

def__init__(self):

super(Net,self).__init__()

self.conv1?=?nn.Conv2d(1,?10,?kernel_size=5)

self.conv2?=?nn.Conv2d(10,?20,?kernel_size=5)

self.conv2_drop?=?nn.Dropout2d()

self.fc1?=?nn.Linear(320,?50)

self.fc2?=?nn.Linear(50,?10)

defforward(self,?x):

x?=?F.relu(F.max_pool2d(self.conv1(x),?2))

x?=?F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)),?2))

????????x?=?x.view(-1,?320)

x?=?F.relu(self.fc1(x))

x?=?F.dropout(x,?training=self.training)

x?=self.fc2(x)

returnF.softmax(x,?dim=1)


SHAP例子分析--Iris dataset

這一部分我們使用KernelExplainer來(lái)進(jìn)行模型的解釋, 使用KernelExplainer是不能直接傳入訓(xùn)練好的模型的, 也是需要和LIME一樣, 寫成函數(shù)的樣子.

關(guān)于具體內(nèi)容可以查看LIME部分:Pytorch例子演示及LIME使用例子

下面部分的完整代碼可以在Github中找到:Github-SHAP筆記

準(zhǔn)備工作

我們需要導(dǎo)入需要使用的庫(kù).

importshap

shap.initjs()#?用來(lái)顯示的

創(chuàng)建解釋器

#?新建一個(gè)解釋器

#?這里傳入兩個(gè)變量,?1.?模型;?2.?訓(xùn)練數(shù)據(jù)

explainer?=?shap.KernelExplainer(batch_predict,?x)

print(explainer.expected_value)#?輸出是三個(gè)類別概率的平均值

"""

[0.32909563?0.33329453?0.33760984]

"""

這里最后打印出的是每一類樣本的平均預(yù)測(cè)的值, 三類, 每一類的值還是很平均的.

對(duì)單個(gè)數(shù)據(jù)進(jìn)行解釋

接下來(lái)我們對(duì)單個(gè)數(shù)據(jù)進(jìn)行解釋, 與LIME那一部分一樣, 我們對(duì)數(shù)據(jù)集中第5個(gè)數(shù)據(jù)進(jìn)行解釋.

#?選擇一個(gè)數(shù)據(jù)進(jìn)行解釋(還是選擇第5個(gè)數(shù)據(jù))

shap_values?=?explainer.shap_values(x[5])

解釋結(jié)果可視化

接著我們將上面解釋的結(jié)果進(jìn)行可視化.

#?對(duì)單個(gè)數(shù)據(jù)進(jìn)行解釋

shap.force_plot(base_value=explainer.expected_value[1],

????????????????shap_values=shap_values[1],

????????????????feature_names=['sepal?length','sepal?width','petal?length','petal?width'],

???????????????features=x[5])

對(duì)特征重要度進(jìn)行解釋

除了上面對(duì)于單個(gè)數(shù)據(jù)進(jìn)行解釋之外, 我們還可以對(duì)整個(gè)模型的特征的重要度進(jìn)行分析.

#?對(duì)特征重要度進(jìn)行解釋

shap_values?=?explainer.shap_values(x)

#?--------

#?進(jìn)行繪圖

#?--------

shap.summary_plot(shap_values=shap_values,

?????????????????features=x,

?????????????????feature_names=['sepal?length','sepal?width','petal?length','petal?width'],

?????????????????plot_type='bar')


SHAP例子分析--每一類分析

下面使用以下DeepExplainer, 使用這個(gè)的話是可以直接傳入模型的.

對(duì)單個(gè)數(shù)據(jù)的解釋

首先我們看一下使用SHAP對(duì)單個(gè)數(shù)據(jù)來(lái)進(jìn)行解釋. 我們首先新建一個(gè)解釋器, 這里的X_exp是用來(lái)計(jì)算baseline的, 同時(shí)也是為后面解釋做準(zhǔn)備的.

#?新建一個(gè)解釋器

X_exp?=?torch.from_numpy(XData).float()

explainer?=?shap.DeepExplainer(fullModel.to(device),?X_exp.to(device))

print(explainer.expected_value)

接著我們隨機(jī)選出一個(gè)數(shù)據(jù)來(lái)進(jìn)行解釋.

#?隨機(jī)選擇1個(gè)數(shù)據(jù),?來(lái)進(jìn)行解釋

X_exp?=?XData[-3]

X_tensor?=?torch.from_numpy(X_exp).unsqueeze(0).float().to(device)

#?計(jì)算樣本的shape?value

shap_values?=?explainer.shap_values(X_tensor)

最后我們將結(jié)果畫出(這里我們使用直接保存為文件的形式, 這樣得到的圖像的清晰度可以控制), 同時(shí)我們?cè)谶@里的feature控制一下小數(shù)點(diǎn)的位數(shù).

#?得到特征的名字和解釋數(shù)據(jù)的特征的值

feature_names=list(dfDataTrain.columns[:-4])

features?=?features?=?np.around(X_exp,2)#?解釋的數(shù)據(jù)的特征的值

#?對(duì)單個(gè)數(shù)據(jù)進(jìn)行解釋

t?=?shap.force_plot(base_value=explainer.expected_value[YData[-3]],

????????????????shap_values=shap_values[YData[-3]],

????????????????feature_names=feature_names,

????????????????features=features,

????????????????out_names?=?'DoS?Attack',

????????????????text_rotation=30,

matplotlib=True,

show=False)

最終我們將我們的圖像進(jìn)行保存即可.

t.set_facecolor('white')#?把背景顏色設(shè)置為白色

t.savefig('filename.png',?bbox_inches?=?'tight',?dpi=500)#?保存圖片

最終的結(jié)果如下圖所示, 我們整個(gè)數(shù)據(jù)集對(duì)這個(gè)label的平均預(yù)測(cè)值是0.2, 這里對(duì)這個(gè)樣本的預(yù)測(cè)值是接近1, 同時(shí)是flag_S0=1等特征對(duì)這個(gè)預(yù)測(cè)結(jié)果有很大的作用.


對(duì)特征重要度的解釋

SHAP可以用來(lái)進(jìn)行模型的整體解釋, 我們只需要將訓(xùn)練集中所有數(shù)據(jù)的每一個(gè)特征都計(jì)算一遍, 計(jì)算平均值即可. 這里我們繪制出對(duì)每一個(gè)特征重要度的解釋.

下面是示例代碼, 對(duì)于多分類來(lái)說(shuō), 我們可以繪制出每一類特征的重要度.

#?隨機(jī)選擇1個(gè)數(shù)據(jù),?來(lái)進(jìn)行解釋

X_exp?=?XExplainDate

X_tensor?=?torch.from_numpy(X_exp).float().to(device)

#?計(jì)算樣本的shape?value

shap_values?=?explainer.shap_values(X_tensor)

#?---------

#?進(jìn)行繪圖

#?---------

plt.figure(dpi=500)#?設(shè)置圖片的清晰度

fig?=?plt.gcf()#?獲取后面圖像的句柄

shap.summary_plot(shap_values=shap_values,

features?=?XData,#?所有樣本的feature的值

feature_names=list(dfDataTrain.columns[:-4]),#?特征的名字

?????????????????plot_type?=?'bar',

?????????????????max_display=20,

show=False)

為了最終可以將圖像進(jìn)行保存, 我們需要對(duì)背景的顏色進(jìn)行設(shè)置.

fig.set_facecolor('white')

fig

之后將圖像右鍵另存為即可.? 最終的效果是如下圖所示(這是同時(shí)展示多個(gè)分類的結(jié)果):

我們也是可以對(duì)某一個(gè)分類進(jìn)行解釋, 查看在這個(gè)分類下的特征的重要度, 這個(gè)時(shí)候就是在繪制的時(shí)候指定shap_values即可.

shap.summary_plot(shap_values=shap_values[1],

features?=?XData,#?所有樣本的feature的值

feature_names=list(dfDataTrain.columns[:-4]),#?特征的名字

?????????????????plot_type?=?'bar',

?????????????????max_display=20,

show=False)

最終的繪制效果如下圖所示:


特征取值與重要度之間的關(guān)系

我們只需要將上面的plot_type = 'bar'替換為plot_type = 'dot' (需要注意, 這個(gè)圖只支持二分類, 多分類是不支持的), 就可以繪制出特征的取值與Shapley Value的關(guān)系. 如下圖所示, x軸表示Shapley Value, 不同的顏色表示特征的大小. 如對(duì)于wrong_fragment這個(gè)特征來(lái)說(shuō), 該特征取值較大會(huì)對(duì)模型判讀數(shù)據(jù)為DoS有一個(gè)正向的推動(dòng). 這里每一個(gè)數(shù)據(jù)點(diǎn)表示一組數(shù)據(jù).

除了上面的繪制方式, 我們還可以繪制出每一個(gè)數(shù)據(jù)點(diǎn)的一個(gè)特征的SHAP Value, 我們可以使用下面的方式進(jìn)行繪制.

shap.dependence_plot(ind?=?'wrong_fragment',

?????????????????????shap_values=shap_values[1],

features?=?XExplainDate,#?所有樣本的feature的值

feature_names=list(dfDataTrain.columns[:-4]),#?特征的名字

?????????????????????interaction_index?=?None,

????????????????????)

其中ind表示的是我們需要解釋的特征, 最終會(huì)得到下面的效果圖. 圖中橫坐標(biāo)表示的該特征的取值, 縱坐標(biāo)表示的是每一個(gè)點(diǎn)對(duì)應(yīng)的SHAP Value.


保存圖像的方式

我們使用SHAP進(jìn)行繪圖的時(shí)候, 可以使用下面的方式進(jìn)行圖片的保存(使用這種方法進(jìn)行繪圖的時(shí)候,?可以控制圖片的清晰度, 這一部分的內(nèi)容其實(shí)在上面也是已經(jīng)涉及到了, 在講對(duì)特征進(jìn)行解釋的時(shí)候, 但是我們?cè)谶@里再次強(qiáng)調(diào)一遍).

plt.figure(dpi=1200)?#?設(shè)置圖片的清晰度

fig?=?plt.gcf()?#?獲取后面圖像的句柄

shap.summary_plot(X,?show=False)

fig.set_facecolor('white')?#?設(shè)置背景為白色

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容