更好的閱讀體驗(yàn)請?zhí)D(zhuǎn)至Knowledge Distillation (1) 模塊替換之bert-of-theseus-上篇
<blockquote>如果忒修斯的船上的木頭被逐漸替換,直到所有的木頭都不是原來的木頭,那這艘船還是原來的那艘船嗎?
-普魯塔克</blockquote>
最近遇到一個需要對算法加速的場景,了解到了一個比較簡潔實(shí)用的方法:Bert-of-theseus,了解了原理后參考代碼實(shí)驗(yàn)后,驗(yàn)證了其有效性,所以總結(jié)一下。
模型壓縮
模型在設(shè)計(jì)之初都是過參數(shù)化的,這是因?yàn)槟P偷膮?shù)量與復(fù)雜度代表著模型的容量與學(xué)習(xí)能力,但當(dāng)我們實(shí)際使用時,我們需要更好的部署他(低資源),更快的響應(yīng)(快速推理),常常需要進(jìn)行模型壓縮。
模型壓縮就是<code>簡化大的模型,得到推理快資源占用低的小模型</code>,而想"即要馬而跑又不用吃草"通常是很難的,所以壓縮后的模型常常也會有不同程度的犧牲,如模型性能下降。
此外,模型壓縮是作用在推理階段,帶來的常常是訓(xùn)練時間的增加。
模型壓縮又分為兩種方式:一種是<code>剪枝(Pruning)</code>與<code>量化(Quantization)</code>,一種是<code>知識蒸餾(Knowledge Distillation)</code>,
還有一種是<code>權(quán)重共享(Sharing)與因數(shù)分解(Factorization)</code>。該部分內(nèi)容推薦一篇博客:All The Ways You Can Compress BERT
剪枝
剪枝技術(shù)是通過將大模型中一些"不重要"的連接剪斷,得到一個"稀疏"結(jié)構(gòu)的模型。剪枝又分為"結(jié)構(gòu)性剪枝"與"非結(jié)構(gòu)性剪枝".剪枝可以作用在權(quán)重粒度,
也可以作用在attention heads / layer粒度上。不過剪枝技術(shù)感覺會逐步被<cdoe>NAS(Neural Architecture Search)</code>取。
量化
量化不改變模型的網(wǎng)絡(luò)結(jié)構(gòu),而是改變模型的參數(shù)的數(shù)據(jù)格式,通常模型在建立與訓(xùn)練時使用的是 float32 格式的,量化就是將格式轉(zhuǎn)換為 <code>low-bit</code>, 如 float16 甚至二值化,如此即提速又省顯存。
知識蒸餾
知識蒸餾是訓(xùn)練一個小模型(student)來學(xué)習(xí)大模型(teacher),由于大模型是之前已經(jīng)fine-tuning的,所以此時學(xué)習(xí)的目標(biāo)已經(jīng)轉(zhuǎn)換為對應(yīng)的logit而
不再是one-hot編碼了,所以student有可能比teacher的性能更好。這樣即小又準(zhǔn)的模型實(shí)在太好了。不過為了達(dá)到這樣的效果,通常設(shè)計(jì)小模型時不
光要學(xué)習(xí)大模型的輸出,還要學(xué)習(xí)各個中間層結(jié)果,相關(guān)矩陣等,這就需要仔細(xì)設(shè)計(jì)模型的結(jié)構(gòu)與loss及l(fā)oss融合方案了。一種簡單的方法是只學(xué)習(xí)大模型的logit,這與對label做embedding有點(diǎn)類似,不過我沒做過實(shí)驗(yàn)還。
權(quán)重共享
將部分權(quán)重在多個層中共享以達(dá)到壓縮模型的效果,如ALBERT中共享self-attention中的參數(shù)
權(quán)重分解
將權(quán)重矩陣進(jìn)行因數(shù)分解,形成兩個低秩的矩陣相乘的形式,從而降低計(jì)算量
模型壓縮的必要性
看了上面模型壓縮的方法,每一個都有種"脫褲子放屁"的感覺,與其訓(xùn)練一個大模型,再費(fèi)力把它變小,為何不直接開始就弄個小的呢?
首先,模型在設(shè)計(jì)之初是都是會或多或少的過參數(shù)化,因?yàn)槟P偷膮?shù)量與復(fù)雜度代表著模型的容量與學(xué)習(xí)能力;
其次,開始就用一個小模型,那這個小模型也是需要設(shè)計(jì)的,不能隨便拿來一個,而設(shè)計(jì)一個性能高參數(shù)規(guī)模小的小模型難度是非常大的,往往是模型小了性能也低了;
第三點(diǎn),大模型壓縮后與小模型雖然參數(shù)規(guī)模相當(dāng),但是對應(yīng)的模型空間并不相同
此外,為了更好的部署,如手機(jī)或FPGA等,得到精度更高模型更小(distillation)或者利用硬件加速(low-bit),模型壓縮都是值得試一試的手段。
更詳細(xì)的討論,可以參考為什么要壓縮模型,而不直接訓(xùn)練一個小的CNN
Bert of theseus
Bert of theseus 方法屬于上面提到的知識蒸餾,知識蒸餾中我們提到,在蒸餾時,我們不光要學(xué)習(xí)teacher的輸出,對中間層我們也希望他們直接盡量相似,
那想象一個這種狀態(tài)對應(yīng)對理想情況:<code>中間層的結(jié)果一致,最終的結(jié)果一致</code>,既然我們的期望中間結(jié)果一致,那也就意味著兩者可以互相替換。
正如開頭提到的忒修斯之船一樣。所以核心思想是:
<code>與其設(shè)計(jì)復(fù)雜的loss來讓中間層結(jié)果相似不如直接用小模型替換大模型來訓(xùn)練</code>
通過復(fù)雜loss來達(dá)到與中間層結(jié)果相似可以看作是一種整體漸進(jìn)式的逼近,讓小模型一點(diǎn)點(diǎn)去學(xué)習(xí),而直接替換可以看作是一種簡單粗暴的方式,
但是他不需要設(shè)計(jì)各種loss,優(yōu)化目標(biāo)也是同一個,就只有一個下游任務(wù)相關(guān)的loss,突出一個<code>簡潔</code>。
這就好比高中上學(xué)一樣,即使花高價也要讓孩子去一所好高中,因?yàn)閷W(xué)校的"氛圍"能讓孩子的學(xué)習(xí)成績進(jìn)步,其實(shí)是因?yàn)橹車暮⒆訋е黄饘W(xué),
弱雞也能學(xué)的比平時更多一點(diǎn)。bert-of-theseus也是類似的道理,跟著大佬(teacher)總比單獨(dú)fine-tuning效果好。
具體流程
如果直接將小模型替換大模型,那其實(shí)是在對小模型進(jìn)行微調(diào),與大模型就脫離了,也達(dá)不到對應(yīng)的效果,所以作者采用了一種概率替換的方式。
首先呢,想象我們現(xiàn)在已經(jīng)訓(xùn)練好了一個6層的BERT,我們成為<code>Predecessor(前輩)</code>, 而我們需要訓(xùn)練一個三層的bert,
他的結(jié)果近似12層BERT的效果,我們成為<code>Successor(傳承者)</code>,那 bert-of-theseus的模型結(jié)構(gòu)如下圖所示:

在bert-of-theseus中,首先固定predecessor的權(quán)重,然后將6層的Bert分為3個block,每個block與successor的一層對應(yīng),訓(xùn)練過程分為兩個stage:
首先用successor中的層概率替換predecessor中對應(yīng)的block,在下游任務(wù)中直接fine-tuning(只訓(xùn)練successor),
然后將successor從bert-of-theseus中分離出來,單獨(dú)在下游任務(wù)中進(jìn)行fine-tuning,直到指標(biāo)不再上升。
所謂替換,就是輸出的替換,在進(jìn)入下一層前在predecessor和successor的輸出中二選一。
替換概率作者也給出了兩種方式,一種是固定 0.5,一種是線性從0-1,如下圖所示:

實(shí)驗(yàn)效果
實(shí)驗(yàn)代碼主要參考bert-of-theseus, 實(shí)驗(yàn)主要做了三組,一組文本分類兩組ner-crf,結(jié)果如下:
文本分類:CLUE的iflytek數(shù)據(jù)集
ner-crf: 公司數(shù)據(jù)
可以看到,相比直接那前幾層微調(diào),bert-of-theseus的效果確實(shí)更好,此外,我還嘗試了線性策略的替換概率,效果上差別不大。
實(shí)驗(yàn)代碼:classification_ifytek_bert_of_theseus
sequence_labeling_ner_bert_of_theseus