天天看點

給模型加入先驗知識

01

模型加入先驗知識的必要性

端到端的深度神經網絡是個黑盒子,雖然能夠自動學習到一些可區分度好的特征,但是往往會拟合到一些非重要特征,導緻模型會局部坍塌到一些不好的特征上面。常常一些人們想讓模型去學習的特征模型反而沒有學習到。

為了解決這個問題,給模型加入人為設計的先驗資訊會讓模型學習到一些關鍵的特征。下面就從幾個方面來談談如何給模型加入先驗資訊。

為了友善展示,我這邊用一個簡單的分類案例來展示如何把先驗知識加入到一個具體的task中。我們的task是在所有的鳥類中識别出一種萌萌的鹦鹉,這中鹦鹉叫鸮(xiāo)鹦鹉,它長成下面的樣子:

鸮(xiāo)鹦鹉

這種鳥有個特點:

就是它可能出現在任何地方,但就是不可能在天上,因為它是世界上唯一一種不會飛的鹦鹉(不是唯一一種不會飛的鳥)。

好,介紹完task的背景,咱們就可以分分鐘搭建一個端到端的分類神經網絡,可以選擇的網絡結構可以有很多,如resnet, mobilenet等等,loss往往是一個常用的分類Loss,如交叉熵,進階一點的用個focal loss等等。确定好了最優的資料(擾動方式),網絡結構,優化器,學習率等等這些之後,往往模型的精度也就達到了一個上限。

然後你測試模型發現,有些困難樣本始終分不開,或者是一些簡單的樣本也容易分錯。這個時候如果你還想提升網絡的精度,可以通過給模型加入先驗的方式來進一步提升模型的精度。

02

基于pretrain模型給模型加入先驗

給模型加入先驗,大家最容易想到的是把網絡的weight替換成一個在另外一個任務上pretrain好的模型weight。經過的預訓練的模型(如ImageNet預訓練)往往已經具備的識别到一些基本的圖檔pattern的能力,如邊緣,紋理,顔色等等,而識别這些資訊的能力是識别一副圖檔的基礎。如下圖所示:

給模型加入先驗知識

但這些先驗資訊都是一些比較general的資訊,我們是否可以加入一些更加high level的先驗資訊呢。

03

基于輸入給模型加入先驗

假如你有這樣的一個先驗:

你覺得鸮鹦鹉的頭是一個差別其他它和鳥類的重要部分,也就是說相比于身體,它的頭部更能區分它和其他鳥類。

這時怎麼讓網絡更加關注鸮鹦鹉的頭部呢。這時你可以這樣做,把整個鸮鹦鹉和它的頭部作為一個網絡的兩路輸入,在網咯的後端再把兩路輸入的資訊融合。以達到既關注局域,又關注整體的目的。一個簡單的示意圖如下所示。

給模型加入先驗知識

04

基于模型重制給模型加入先驗

接着上面的設定來,假如說你覺得給模型兩路輸入太麻煩,而且增加的計算量讓你感覺很不爽。

這時,你可以嘗試讓模型自己發現你設定的先驗知識。

假如說你的模型可以自己輸出鳥類頭部的位置,雖然這個鳥類頭部的位置資訊是你不需要的,但是輸出這樣的資訊代表着你的網絡能夠locate鳥類頭部的位置,也就給鳥類的頭部更加多的attention,也就相當于給把鳥類頭部這個先驗資訊給加上去了。

當然直接模仿detection那樣去回歸出位置來這個任務太heavy了,你可以通過一個生成網絡的支路來生成一個鳥類頭部位置的Mask,一個簡單的示意圖如下:

給模型加入先驗知識
給模型加入先驗知識

05

基于CAM圖激活限制給模型加入先驗

針對鸮鹦鹉的分類,我在上面的提到一個非常有意思的先驗資訊:

那就是鸮鹦鹉是世界上唯一一種不會飛的鹦鹉。

這個資訊從側面來說就是,鸮鹦鹉所有地方都可能出現,就是不可能出現在天空中(當然也不可能出現在水中)。

也就是說不但鸮鹦鹉本身是一個分類的重點,鸮鹦鹉出現的背景也是分類的一個重要參考。假如說背景是天空,那麼就一定不是鸮鹦鹉,同樣的,假如說背景是海水,那麼也一定不是鸮鹦鹉,假如說背景是北極,那麼也一定不是鸮鹦鹉,等等。

也就是說,你不能通過背景來判斷一隻未知的鳥是鸮鹦鹉,但是你能通過背景來判斷一隻未知的鳥肯定不是鸮鹦鹉(是其他的鳥類)。

是以假如說擷取了一張輸入圖檔的激活圖(包含背景的),那麼這張激活圖的鳥類身體部分肯定包含了鸮鹦鹉和其他鳥類的激活,但是鳥類身體外的背景部分隻可能包含其他鳥類的激活。

給模型加入先驗知識

是以具體的做法是基于激活圖,通過限制激活圖的激活區域,加入目标先驗。

CAM[1]激活圖是基于分類網絡的倒數第二層卷積層的輸出的 feature_map 的線性權重,權重就是最後一層分類層的權重,由于分類層的權重編碼了類别的資訊,是以權重後的響應圖就有了基于不同類别的區域相應。(具體的介紹可以看 https://zhuanlan.zhihu.com/p/51631163),具體的激活圖生成方式可以如下表示:

給模型加入先驗知識

說了這麼多,下面就展示展示激活圖的樣子:

給模型加入先驗知識

大家可以看到,上面一張是一隻鸮鹦鹉的激活圖,下面是一隻在天空飛翔的大雁的激活圖。

因為鸮鹦鹉的Label是0,其他鳥類的Label是1,是以在激活圖上,隻要是負值的激活區域都是鸮鹦鹉的激活,也就是Label為0的激活,隻要是正值的激活都是其他鳥類的激活,也就是Label為1的激活。

為了友善展示,我把負值的激活用冷色調來顯示,把正值的激活用暖色調來顯示,是以就是變成了上面兩幅激活圖的樣子。而右邊的數字是具體的激活矩陣(把激活矩陣進行GAP就可以變成最終輸出的Logits)。

到這裡不知道大家有沒有發現一個問題,就是無論對于鸮鹦鹉還是大雁的圖檔,它們的激活圖除了分布在鳥類本身,也會有一部分分布在背景上。對于大雁我們好了解,因為大雁是飛在天空中的,而鸮鹦鹉是不可能在天空中的,是以天空的正激活是非常合理的。但是對于鸮鹦鹉來說,其在鳥類身體以外的負激活就不是太合理,因為,大雁或者是其他的鳥類,也可能在鸮鹦鹉的地面栖息環境中(但是鸮鹦鹉卻不可能在天空中)。

是以環境不能提供任何證據來證明這一次鳥類是一隻鸮鹦鹉,鸮鹦鹉的負激活隻是在鳥類的身體上是合理的。而其他鳥類的正激活卻可以同時在鳥類身體上又可能在鳥類的背景上(如天空或者海洋)。

是以我們需要這樣模組化這個問題,就是在除鳥類身體的背景上,不能出現鸮鹦鹉的激活,也就是說不能出現負激活(Label為0的激活)。 是以下面的激活才是合理的:

給模型加入先驗知識

從上面來看,在除鳥類身體外的背景部分是不存在負激活的,雖然上面的背景部分有一些正的激活(其他鳥類的激活),但是從右邊的激活矩陣來看,負激活的scale是占據絕對優勢的,是以完全不會幹擾對于鸮鹦鹉的判斷。

是以問題來了,怎麼從網絡設計方面來達到這個目的呢?

其實可以從Loss設計方面來達到這個效果。我們假設每一個鳥都有個對應的mask,mask内是鳥類的身體部分,mask外是鳥類的背景部分。那麼我們需要做的就是抑制mask外的背景部分激活矩陣的負值,把那一部分負值給抑制到0即可。

鳥類的激活矩陣和mask的關系如下圖(紅色的曲線代表鳥的邊界mask):

給模型加入先驗知識

我們的Loss設計可以用下面的公式表示:

具體的網絡的framework可以如下所示:

給模型加入先驗知識

其中虛線部分隻是訓練時候需要用到,inference的時候是不需要的,是以這種方法也是不會占用任何在inference前向時候的計算量。

06

基于輔助學習給模型加入先驗知識

到現在為止,咱們還隻是把我們的鳥類分類的task當成一個二分類來處理,即鸮鹦鹉是一類,其他的鳥類是一類。

但是我們知道,世界的鳥類可不僅僅是兩類,除了鸮鹦鹉之外還有很多種類的鳥類。而不同鳥類的特征或許有很大的差别,比如鴕鳥的特征就是脖子很長,大雁的特征就是翅膀很大。

給模型加入先驗知識

假如隻是把鸮鹦鹉當做一類,把其他的鳥類當做一類來學習的話,那麼模型很可能不能學到可以利用的區分非鸮鹦鹉的特征,或者是會坍塌到一些區分度不強的特征上面,進而沒有學到能夠很好的區分不同其他鳥類的特征,而那些特征對去差別鸮鹦鹉和其他鳥類或許是重要的。

是以我們有必要加入其他鳥類存在不同類别的先驗知識。而這裡,我主要介紹基于輔助學習的方式去學習類似的先驗知識。首先我要解釋一下什麼是輔助學習,以及輔助學習和多任務學習的差別:

給模型加入先驗知識

上圖的左側是多任務學習的例子,右側是輔助學習的例子。左側是個典型的face attribute的task,意思是輸入一張人臉,通過多個branch來輸出這一張人臉的年齡,性别,發型等等資訊,各個branch的任務是獨立的,同時又共享同一個backbone。右邊是一個典型的輔助學習的task,意思是出入一張人臉,判斷這一張人臉的性别,同時另外開一個(或幾個)branch,通過這個branch來讓網絡學一些輔助資訊,比如發型,皮膚等等,來幫助網絡主任務(分男女)的判别。

好,回到我們的鸮鹦鹉分類的task,我們可能首先會想到下面的Pipeline:

給模型加入先驗知識

這樣雖然可以把不同類别的鳥類的特征都學到,但是卻削弱了網絡對于鸮鹦鹉和其他鳥類特征的分别。

經過實驗發現,這種網絡架構不能很好的增加主任務的分類精度。為了充分的學到鸮鹦鹉和其他鳥類特征的分别,同時又能帶入不同種類鳥類類别的先驗,我們引入輔助任務:

給模型加入先驗知識

在上面的Pipeline中,輔助任務相比如主任務,把其他鳥類做更加細緻的分類。這樣網絡就學到了區分不同其他鳥類的能力。

但是從實驗效果來看這個Pipeline的精度并不高。經過分析原因,發現在主任務和輔助任務裡面都有鸮鹦鹉這一類,這樣當回傳梯度的時候,相當于把區分鸮鹦鹉和其他鳥類的特征回傳了兩次梯度,而回傳兩次梯度明顯是沒用的,而且會幹擾輔助任務學習不同其他鳥類的特征。

是以我們可以把輔助任務的鸮鹦鹉類去除,于是便形成了下面的pipeline:

給模型加入先驗知識

經過實驗發現,這種pipeline是有利于主任務精度提升的,網絡對于特征明顯的其他鳥類的分類能力得到了一定程度的提升,同時對于困難類别的分類能力也有一定程度的提升。

當然,輔助任務的branch可以不隻是一類,你可以通過多個類别來定義你的輔助任務的branch:

給模型加入先驗知識

這時候你會想,上面的pipeline好是好,但是我沒有那麼多的label啊。是的,上面的pipeline除了主任務的label标注,它還同時需要很多的輔助任務的label标注,而标注label是深度學習任務裡面最讓人頭疼的問題(之一)。

别怕,我下面介紹一個work,它基于meta-learning的方法,讓你不再為給輔助任務标注label而煩惱,它的framework如下:

這個framework采用基于maxl[2]的方案(github.com/lorenmt/maxl),輔助任務的資料和label不是由人為手工劃分,而是由一個label generator來産生,label generator的優化目标是讓主網絡在主任務的task上的loss降低,主網絡的目标是在主任務和輔助任務上的loss同時降低。

給模型加入先驗知識

但是這個framework有個缺點,就是訓練時間會上升一個數量級,同時label generator會比較難優化。感興趣的同學可以自己嘗試。但是不得不說,這篇文章有兩個結論倒是很有意思:

1. 假設 primary 和 auxiliary task 是在同一個 domain,那麼 primary task 的 performance 會提高當且僅當 auxiliary task 的 complexity 高于 primary task。

2. 假設 primary 和 auxiliary task 是在同一個 domain,那麼 primary task 的最終 performance 隻依賴于 complexity 最高的 auxiliary task。

07

結語

先總結一下所有可以有效的加入先驗資訊的架構:

給模型加入先驗知識

你可以通過上述架構的選擇來加入自己的先驗資訊。

給神經網絡的黑盒子裡面加入一些人為設定的先驗知識,這樣往往能給你的task帶來一定程度的提升,不過具體的task需要加入什麼樣的先驗知識,需要如何加入先驗知識還需要自己探索。

猜您喜歡:

​​<b> </b>​​

給模型加入先驗知識