深度學習模型可解釋性初探

Andrew.Hann發表於2018-10-13

1. 可解釋性是什麼

0x1:廣義可解釋性

廣義上的可解釋性指:

在我們需要了解或解決一件事情的時候,我們可以獲得我們所需要的足夠的可以理解的資訊。

比如我們在除錯 bug 的時候,需要通過變數審查和日誌資訊定位到問題出在哪裡。

比如在科學研究中面臨一個新問題的研究時,我們需要查閱一些資料來了解這個新問題的基本概念和研究現狀,以獲得對研究方向的正確認識。

反過來理解,如果在一些情境中我們無法得到相應的足夠的資訊,那麼這些事情對我們來說都是不可解釋的。比如劉慈欣的短篇《朝聞道》中霍金提出的「宇宙的目的是什麼」這個問題一下子把無所不知的排險者卡住了,因為再高等的文明都沒辦法理解和掌握造物主創造宇宙時的全部資訊,這些終極問題對我們來說永遠都是不可解釋的。

0x2:在統計學習中的可解釋性

在傳統統計中,我們通過調查大量的資料來構造和驗證假設。我們建立模型來構建規則,我們可以將其納入我們的模型中。

例如,營銷公司可以建立一個模型,將營銷活動資料與財務資料相關聯,以確定構成有效營銷活動的是什麼。這是一種自上而下的資料科學方法,可解釋性是關鍵,因為它是所定義規則和過程的基石。由於相關性往往不等於因果關係,所以在進行決策和解釋時,需要對模型進行很強的理解。

作為一名資料分析從業者,我們經常關心微調模型以獲得最佳效能。

資料科學通常被定義為:'給出具有X標籤的資料,並以最小誤差找到模型'。

儘管訓練高效能模型的能力對於資料科學家來說是一項關鍵技能,但能夠從更大的角度來看是很重要的。

資料和機器學習模型的可解釋性是在資料科學的 “有用性”中至關重要的方面之一,它確保模型與您想要解決的問題保持一致,能夠正確地解釋你的發現是資料科學過程的重要組成部分。

0x3:機器學習領域的可解釋性

1. 決策樹是目前業內公認可解釋性最好的非線性機器學習演算法

而具體到機器學習領域來說,以最使用者友好的決策樹模型為例,模型每作出一個決策都會通過一個決策序列來向我們展示模型的決策依據

比如男性&未婚&博士&禿頭的條件對應「不感興趣」這個決策。

而且決策樹模型自帶的基於資訊理論的篩選變數標準也有助於幫助我們理解在模型決策產生的過程中哪些變數起到了顯著的作用。

所以在一定程度上,我們認為決策樹模型是一個具有比較好的可解釋性的模型。

2. DNN深度學習模型的可解釋性不是那麼明顯

再以使用者最不友好的多層神經網路模型為例,模型產生決策的依據是什麼呢?

大概是以比如 1/(e^-(2*1/(e^(-(2*x+y))+1) + 3*1/(e^(-(8*x+5*y))+1))+1) 是否大於 0.5 為標準(這已經是最簡單的模型結構了),這一連串的非線性函式的疊加公式讓人難以直接理解神經網路的「腦回路」,所以深度神經網路習慣性被大家認為是黑箱模型。

人腦是不擅長理解非線性關係,更不要說是有層層組合後的複雜非線性組合。

所以,一個很自然的思考對策是,需要找一種維度較低的低階近似對映(即找到一種低維度的表徵方法),使得在即不損失太多原高階決策超曲面空間分佈的前提下,找到一種低階的決策曲面。這樣,人類就可以從低階的決策曲面中得以一窺原始高階決策超曲面的尊容了。

筆者思考:按照這種思路,其實我們可以看到,t-SNE、PCA降維後視覺化就是一種低階近似對映

 

2. 我們為什麼需要可解釋性?

廣義上來說我們對深度學習模型的可解釋性的需求主要來源於對問題和任務瞭解得還不夠充分

儘管高度的非線性賦予了多層神經網路極高的模型表示能力,配合一些合適的調參技術可以在很多問題上達到非常喜人的表現。

一個很難受的問題是,雖然我們造出了準確度極高的機器,但最後只能得到一堆看上去毫無意義的模型引數和擬合度非常高的判定結果。作為模型開發者,我們顯然不能僅僅滿足於模型效果好即可。

實際上,對模型可解釋性的探究就是在探尋模型work well的原理,瞭解到了原理,也許就不需要進行很多無用的模型調參工作,優化的方向也會更有方向性。

從模式識別的角度來看,模型本身也意味著知識(或者說模型本身儲存了一些模式知識),我們希望知道模型究竟從資料中學到了哪些知識(以人類可以理解的方式表達的)從而產生了最終的決策。從中是不是可以幫助我們發現一些潛在的關聯。

比如我想基於深度學習模型開發一個幫助醫生判定病人風險的應用,除了最終的判定結果之外,我可能還需要了解模型產生這樣的判定是基於病人哪些因素的考慮。

0x1:可解釋性的不同動機

1. 判別並減輕偏差(Identify and mitigate bias)

偏差可能存在於任何資料集中,資料科學家需要確定並嘗試修正偏差。

1. 資料集的規模可能有限,並且不能代表所有資料。
2. 或者資料捕獲過程可能沒有考慮到潛在的偏差。

在徹底進行資料分析後,或者分析模型預測與模型輸入之間的關係時,偏差往往會變得明顯。
請注意,解決偏差問題沒有唯一的解決方案,但是可解釋性的關鍵一步是意識並發現潛在的偏差。

1)特徵工程中存在的偏差

虛擬一個簡歷篩選的場景,我們對面試者投遞的簡歷進行了分詞後,建立了word index table,但是一個很容易被忽視的問題是,對於一家IT公司的技術部門來說,男性求職者往往佔了絕大多數的比例,因此像類似“技術簡歷”這種詞有更大的可能性會出現在男性求職者的簡歷中。

當使用這些詞向量進行訓練模型時,招聘人員搜尋“技術簡介”將使女性履歷保留在最下面。

但是另一方面我們也需要注意,如果這種偏差是我們有意而為之的,則這種bais就是可以接受的。例如我們的目標並不是平等地搜尋求職者簡歷,而是儘可能發現有技術能力的求職者,或者我們的招聘更傾向於男性,則這種word table index編碼方式就沒有問題。

總之,資料探勘專案中的偏差需要謹慎對待,它可能對我們是有益的也可能是有害的。

2)訓練資料樣本分佈不均衡偏差

例如當您在小型資料集上訓練目標檢測模型時,通常情況下影象的寬度太有限。為了避免只適用於資料中噪音和不重要元素的模型,需要在不同環境,不同光照條件和不同角度下的各種物體影象。

在非影象領域,尤其是NLP文字檢測領域,不同文字風格(程式碼風格)的樣本數量常常是分佈不均的,這點在WEBSHELL網站後門中非常常見,黑客們會傾向於使用某一種或某幾種WEBSHELL樣本,這種偏差會帶來的最明顯問題是,模型可能會忽視某些非常小眾的黑樣本。

2. 考慮問題的上下文(Accounting for the context of the problem)

在大多數問題中,您正在使用的資料集僅僅是您正試圖解決的問題的粗略表示,而機器學習模型無法捕捉到真實任務的完整複雜性。純理論上的無窮大N樣本在實際工程中是非常難達到的。

這個時候,可解釋模型可幫助您瞭解並解釋模型中包含和未包含的因素。

3. 改進泛化能力和效能(Improving generalisation and performance)

高解釋性模型通常有更好的泛化能力。

可解釋性不是要了解所有資料點的模型的每個細節。必須將可靠的資料模型問題理解結合起來才能獲得效能更好的解決方案。

0x2:在安全攻防AI 領域同樣需要可解釋性

筆者在行業內以及在技術圈子內,近幾年不斷看到很多效果很好的實際工程應用,深度學習模型在海量資料以及大規模深度結構下,取得相當好,甚至超過國際上老牌AV引擎的檢測能力。從結果上看,我們似乎也不是那麼需要可解釋性,畢竟結果才是最重要的,世界上不能解釋的事情太多了,也不見得每件事都能得到完美的可解釋性。

但是筆者認為,依然存在一些場景下,我們會需要可解釋性:

1. 找到模型檢測效果下降的真正原因 - 對異常產生的原因進行追蹤和定位

某天下午晴空萬里,我們正在位子上寫部落格,突然一堆誤報告警響起,你的深度模型遇到了一批誤報。你緊急下線了該模型,平復了一下心情,接下來要做的是找到誤報的原因。

但是現在問題來了,你的模型有2000w的自有引數,層與層之間完全是非線性的組合,如何知道到底是哪些神經元導致了對這批樣本的誤報呢?這批導致誤報的樣本的共性是什麼?其中的哪些特徵模式被模型誤認定為是一個黑模式了呢?

這一連串的問題,會驅動我們去更深入地探究我們訓練出的深度學習模型的可解釋性。

2. 找到迭代優化的最快方向

相信大家在進行模型開發的過程中,都會經歷一個階段,大量的翻閱google上的學術paper,嘗試各種神經網路組合,會發現現在有非常多的神經網路結構被提出,論文裡的實驗結果也非常驚豔,這個時候,我們會抱著“僥倖心理”去進行大量嘗試,雖然paper裡的問題場景我們的專案可能並不一致。

最終通過數週的大量實驗,我們“摸索”出了一種模型結構,在訓練集和驗證集上的表現都很好,都拿到了99.8/99.9的acc,但還是有少量的誤報(筆者所在的行業是誤報敏感型的,漏報相對還可以接受),這個時候我們會面臨一個問題,接下來怎麼優化?繼續增大樣本?還是繼續翻閱paper,優化模型結構?是否不同的神經網路結構之間存在一些原理上的共性?

想要系統化、工程化的解決這個問題,就需要進行可解釋性的研究。例如我們通過可解釋性的視覺化得到了模型的熱點權重分佈,即分析出模型對輸入特徵空間中的哪些特徵維度更“看中”,對另外一些特徵維度“相對輕視”,而這有可能恰恰就是導致我們誤報的原因之一。

Relevant Link: 

https://www.leiphone.com/news/201805/Ti3mOdeRlWTplIlZ.html
https://cloud.tencent.com/developer/article/1096716
https://zhuanlan.zhihu.com/p/38151985
https://www.jiqizhixin.com/articles/2018-01-10

 

3. 有哪些可解釋性方法

機器學習的目的是從資料中發現知識或解決問題,那麼在這個過程中只要是能夠提供給我們關於資料或模型的可以理解的資訊,有助於我們更充分地發現知識、理解和解決問題的方法,那麼都可以歸類為可解釋性方法。如果按照可解釋性方法進行的過程進行劃分的話,大概可以劃分為三個大類:

1. 在建模之前的可解釋性方法
2. 建立本身具備可解釋性的模型
3. 在建模之後使用可解釋性方法對模型作出解釋

0x1:在建模之前的可解釋性方法

這一類方法其實主要涉及一些資料預處理資料展示的方法。機器學習解決的是從資料中發現知識和規律的問題,如果我們對想要處理的資料特徵所知甚少,指望對所要解決的問題本身有很好的理解是不現實的。因此,在建模之前的可解釋性方法的關鍵在於幫助我們迅速而全面地瞭解資料分佈的特徵,從而幫助我們考慮在建模過程中可能面臨的問題並選擇一種最合理的模型來逼近問題所能達到的最優解。

1. 資料視覺化

資料視覺化方法就是一類非常重要的建模前可解釋性方法。大多數時候,我們在真正要研究一個資料問題之前,通過建立一系列方方面面的視覺化方法來建立我們對資料的直觀理解是非常必須的,特別是當資料量非常大或者資料維度非常高的時候,比如一些時空高維資料,如果可以建立一些一些互動式的視覺化方法將會極大地幫助我們從各個層次角度理解資料的分佈。

這方面的相關討論,可以參閱我的另一篇blog

2. 樣本資料探索

還有一類比較重要的方法是探索性質的資料分析,可以幫助我們更好地理解資料的分佈情況。比如一種稱為 MMD-critic 方法中,可以幫助我們找到資料中一些具有代表性或者不具代表性的樣本。

3. 特徵重要性探索

特徵重要性是解釋模型的一種基本方法

0x2:建立本身具備可解釋性的模型

模型本身具備可解釋性是最好也是最直接的一類可解釋性方法,同樣也是一類要求和限定很高的方法(深度神經網路在很多情況下就不具備可解釋性),具備可解釋性模型大概可以分為以下幾種

1. 基於規則的方法(Rule-based)
2. 基於單個特徵的方法(Per-feature-based)
3. 基於例項的方法(Case-based)
4. 稀疏性方法(Sparsity)
5. 單調性方法(Monotonicity)

1. 基於規則的方法(Rule-based)

基於規則的方法比如我們提到的非常經典的決策樹模型。這類模型中任何的一個決策鏈都可以對應到一個邏輯規則表示。在決策樹中,可解釋性通過一連串的 if-else 邏輯組合來表達,

但當規則表示過多或者原始的特徵本身就不是特別好解釋的時候,基於規則的方法有時候也不太適用。

2. 基於單個特徵的方法(Per-feature-based)

基於單個特徵的方法主要是一些非常經典的線性模型,比如線性迴歸、邏輯迴歸、廣義線性迴歸、廣義加性模型等,這類模型可以說是現在可解釋性最高的方法。線性迴歸方程中的 w 和 b 本身就有非常強的可解釋性。

線性迴歸可謂是一種非常經典的數學模型,在計量經濟學中,大半本書都在討論線性模型,包括經濟學及相關領域的論文其實大多數也都是使用線性迴歸作為方法來進行研究。這種非常經典的模型全世界每秒都會被用到大概 800 多萬次。

為什麼大家這麼青睞這個模型呢?除了模型的結構比較簡單之外,更重要的是線性迴歸模型及其一些變種擁有非常 solid 的統計學基礎,統計學可以說是最看重可解釋性的一門學科了,上百年來無數數學家統計學家探討了在各種不同情況下的模型的引數估計、引數修正、假設檢驗、邊界條件等等問題,目的就是為了使得在各種不同情況下都能使模型具有有非常好的可解釋性。

3. 基於例項的方法(Case-based)

基於例項的方法主要是通過一些代表性的樣本來解釋聚類/分類結果的方法。通過觀察代表性的樣本,我們可以直觀得獲得其對應族類的樣本巨集觀特徵。

比如下圖所展示的貝葉斯例項模型(Bayesian Case Model,BCM),我們將樣本分成三個組團,可以分別找出每個組團中具有的代表性樣例重要的子空間

對於下面第一類聚類來說:

綠臉是具有代表性的樣本;而綠色、方塊是具有代表性的特徵子空間(包含2個特徵維度)。

由此,我們可以得到初步判斷,第一類的巨集觀特徵是一堆綠臉方塊。
筆者思考:社群發現的聚類中心、Kmeans聚類的聚類質心本質上也可以提供一種基於例項的資料視覺化視角。
關於社群發現的相關討論,可以參閱我的另一篇blog

4. 稀疏性方法(Sparsity)

基於稀疏性的方法主要是利用資訊的稀疏性特質,將模型儘可能地簡化表示。

比如如下圖的一種圖稀疏性的 LDA 方法,根據層次性的單詞資訊形成了層次性的主題表達,這樣一些小的主題就可以被更泛化的主題所概括,從而可以使我們更容易理解特定主題所代表的含義。

5. 單調性方法(Monotonicity)

在很多機器學習問題中,有一些輸入和輸出之間存在正相關/負相關關係,如果在模型訓練中我們可以找出這種單調性的關係就可以讓模型具有更高的可解釋性。

比如醫生對患特定疾病的概率的估計主要由一些跟該疾病相關聯的高風險因素決定,找出單調性關係就可以幫助我們識別這些高風險因素。
但是同時另一方面,很多情況下,輸入資料的特徵空間是高維特徵,例如筆者所在的安全領域,抽象後的向量特徵常常高達上萬,面對這種高維資料,尋找input-ouput之間的線性相關,是非常困難的。

0x3:在建模之後使用可解釋性方法對模型作出解釋

建模後的可解釋性方法主要是針對具有黑箱性質的深度學習模型而言的,主要分為以下幾類的工作:

1. 隱層分析方法
2. 模擬/代理模型
3. 敏感性分析方法

Relevant Link:

https://www.leiphone.com/news/201805/Ti3mOdeRlWTplIlZ.html
https://new.qq.com/omn/20180117/20180117A0T4JE.html 

 

4. Lime - Explaining the predictions of any machine learning classifier

Lime可通過視覺化的方式向我們展示機器學習決策器是根據哪些“因素”進行了綜合決策的。

0x1:Lime的原理

直觀上說,Lime採用了區域性線性近似來對目標模型進行模擬。雖然目標模型本身可能在全域性上非常複雜,但是我們卻可以較容易地針對一個特徵的區域性例項進行近似模擬。

我們將目標模型視作一個黑盒,通過不斷的漸進試探以學習到一個低維稀疏的線性模型,作為一個解釋。

下圖是一個複雜模型的決策函式,藍色/粉色背景表示兩個決策面,很顯然,這是非線性的。

圖中亮紅色的叉叉就是Lime得到的解釋例項,我們圍繞解釋例項 X 進行取樣,取樣的權重標準就是和 X 的近似程度(在這裡就是size),我們隨後得到一個線性模型,即虛線。

顯然,這是一個區域性近似的線性模型,不是全域性的。

0x2:通過Lime解釋隨機森林的決策因素

# -*- coding: utf-8 -*-

import lime
import sklearn
import numpy as np
import sklearn
import sklearn.ensemble
import sklearn.metrics

# For this tutorial, we'll be using the 20 newsgroups dataset. In particular, for simplicity, we'll use a 2-class subset: atheism and christianity.
from sklearn.datasets import fetch_20newsgroups
categories = ['alt.atheism', 'soc.religion.christian']
newsgroups_train = fetch_20newsgroups(subset='train', categories=categories)
newsgroups_test = fetch_20newsgroups(subset='test', categories=categories)
class_names = ['atheism', 'christian']

# Let's use the tfidf vectorizer, commonly used for text.
vectorizer = sklearn.feature_extraction.text.TfidfVectorizer(lowercase=False)
train_vectors = vectorizer.fit_transform(newsgroups_train.data)
test_vectors = vectorizer.transform(newsgroups_test.data)

# Now, let's say we want to use random forests for classification. It's usually hard to understand what random forests are doing, especially with many trees.
rf = sklearn.ensemble.RandomForestClassifier(n_estimators=500)
rf.fit(train_vectors, newsgroups_train.target)

pred = rf.predict(test_vectors)
res = sklearn.metrics.f1_score(newsgroups_test.target, pred, average='binary')

print res

# Explaining predictions using lime
from lime import lime_text
from sklearn.pipeline import make_pipeline
c = make_pipeline(vectorizer, rf)

print(c.predict_proba([newsgroups_test.data[0]]))

# Now we create an explainer object. We pass the class_names a an argument for prettier display.
from lime.lime_text import LimeTextExplainer
explainer = LimeTextExplainer(class_names=class_names)

# We then generate an explanation with at most 6 features for an arbitrary document in the test set.
idx = 83
exp = explainer.explain_instance(newsgroups_test.data[idx], c.predict_proba, num_features=6)
print('Document id: %d' % idx)
print('Probability(christian) =', c.predict_proba([newsgroups_test.data[idx]])[0,1])
print('True class: %s' % class_names[newsgroups_test.target[idx]])

# The classifier got this example right (it predicted atheism).
# The explanation is presented below as a list of weighted features.
print exp.as_list()

# These weighted features are a linear model, which approximates the behaviour of the random forest classifier in the vicinity of the test example.
# Roughly, if we remove 'Posting' and 'Host' from the document , the prediction should move towards the opposite class (Christianity) by about 0.27 (the sum of the weights for both features).
# Let's see if this is the case.
print('Original prediction:', rf.predict_proba(test_vectors[idx])[0,1])
tmp = test_vectors[idx].copy()
tmp[0,vectorizer.vocabulary_['Posting']] = 0
tmp[0,vectorizer.vocabulary_['Host']] = 0
print('Prediction removing some features:', rf.predict_proba(tmp)[0,1])
print('Difference:', rf.predict_proba(tmp)[0,1] - rf.predict_proba(test_vectors[idx])[0,1])

# The explanations can be returned as a matplotlib barplot:
fig = exp.as_pyplot_figure()

# The explanations can also be exported as an html page (which we can render here in this notebook), using D3.js to render graphs.
exp.show_in_notebook(text=False)

# Alternatively, we can save the fully contained html page to a file:
exp.save_to_file('./oi.html')

# Finally, we can also include a visualization of the original document, with the words in the explanations highlighted. Notice how the words that affect the classifier the most are all in the email header.
exp.show_in_notebook(text=True)

Relevant Link:

https://github.com/marcotcr/lime
https://www.oreilly.com/learning/introduction-to-local-interpretable-model-agnostic-explanations-lime

 

相關文章