深度學(xué)習(xí)文本分類|模型&代碼&技巧
文本分類是NLP的必備入門任務(wù),在搜索、推薦、對話等場景中隨處可見,并有情感分析、新聞分類、標(biāo)簽分類等成熟的研究分支和數(shù)據(jù)集。
本文主要介紹深度學(xué)習(xí)文本分類的常用模型原理、優(yōu)缺點以及技巧。
P.S. 有基礎(chǔ)的同學(xué)可以直接看文末的技巧
Fasttext
論文:https://arxiv.org/abs/1607.01759
代碼:https://github.com/facebookresearch/fastText
Fasttext是Facebook推出的一個便捷的工具,包含文本分類和詞向量訓(xùn)練兩個功能。
Fasttext的分類實現(xiàn)很簡單:把輸入轉(zhuǎn)化為詞向量,取平均,再經(jīng)過線性分類器得到類別。輸入的詞向量可以是預(yù)先訓(xùn)練好的,也可以隨機初始化,跟著分類任務(wù)一起訓(xùn)練。

Fasttext直到現(xiàn)在還被不少人使用,主要有以下優(yōu)點:
模型本身復(fù)雜度低,但效果不錯,能快速產(chǎn)生任務(wù)的baseline Facebook使用C++進(jìn)行實現(xiàn),進(jìn)一步提升了計算效率 采用了char-level的n-gram作為附加特征,比如paper的trigram是 [pap, ape, per],在將輸入paper轉(zhuǎn)為向量的同時也會把trigram轉(zhuǎn)為向量一起參與計算。這樣一方面解決了長尾詞的OOV (out-of-vocabulary)問題,一方面利用n-gram特征提升了表現(xiàn) 當(dāng)類別過多時,支持采用hierarchical softmax進(jìn)行分類,提升效率
對于文本長且對速度要求高的場景,F(xiàn)asttext是baseline首選。同時用它在無監(jiān)督語料上訓(xùn)練詞向量,進(jìn)行文本表示也不錯。不過想繼續(xù)提升效果還需要更復(fù)雜的模型。
TextCNN
論文:https://arxiv.org/abs/1408.5882
代碼:https://github.com/yoonkim/CNN_sentence
TextCNN是Yoon Kim小哥在2014年提出的模型,開創(chuàng)了用CNN編碼n-gram特征的先河。

模型結(jié)構(gòu)如圖,圖像中的卷積都是二維的,而TextCNN則使用「一維卷積」,即filter_size * embedding_dim,有一個維度和embedding相等。這樣就能抽取filter_size個gram的信息。以1個樣本為例,整體的前向邏輯是:
對詞進(jìn)行embedding,得到 [seq_length, embedding_dim]用N個卷積核,得到N個 seq_length-filter_size+1長度的一維feature map對feature map進(jìn)行max-pooling(因為是時間維度的,也稱max-over-time pooling),得到N個 1x1的數(shù)值,拼接成一個N維向量,作為文本的句子表示將N維向量壓縮到類目個數(shù)的維度,過Softmax
在TextCNN的實踐中,有很多地方可以優(yōu)化(參考這篇論文[1]):
Filter尺寸:這個參數(shù)決定了抽取n-gram特征的長度,這個參數(shù)主要跟數(shù)據(jù)有關(guān),平均長度在50以內(nèi)的話,用10以下就可以了,否則可以長一些。在調(diào)參時可以先用一個尺寸grid search,找到一個最優(yōu)尺寸,然后嘗試最優(yōu)尺寸和附近尺寸的組合 Filter個數(shù):這個參數(shù)會影響最終特征的維度,維度太大的話訓(xùn)練速度就會變慢。這里在100-600之間調(diào)參即可 CNN的激活函數(shù):可以嘗試Identity、ReLU、tanh 正則化:指對CNN參數(shù)的正則化,可以使用dropout或L2,但能起的作用很小,可以試下小的dropout率(<0.5),L2限制大一點 Pooling方法:根據(jù)情況選擇mean、max、k-max pooling,大部分時候max表現(xiàn)就很好,因為分類任務(wù)對細(xì)粒度語義的要求不高,只抓住最大特征就好了 Embedding表:中文可以選擇char或word級別的輸入,也可以兩種都用,會提升些效果。如果訓(xùn)練數(shù)據(jù)充足(10w+),也可以從頭訓(xùn)練 蒸餾BERT的logits,利用領(lǐng)域內(nèi)無監(jiān)督數(shù)據(jù) 加深全連接:原論文只使用了一層全連接,而加到3、4層左右效果會更好[2]
TextCNN是很適合中短文本場景的強baseline,但不太適合長文本,因為卷積核尺寸通常不會設(shè)很大,無法捕獲長距離特征。同時max-pooling也存在局限,會丟掉一些有用特征。另外再仔細(xì)想的話,TextCNN和傳統(tǒng)的n-gram詞袋模型本質(zhì)是一樣的,它的好效果很大部分來自于詞向量的引入[3],因為解決了詞袋模型的稀疏性問題。
DPCNN
論文:https://ai.tencent.com/ailab/media/publications/ACL3-Brady.pdf
代碼:https://github.com/649453932/Chinese-Text-Classification-Pytorch
上面介紹TextCNN有太淺和長距離依賴的問題,那直接多懟幾層CNN是否可以呢?感興趣的同學(xué)可以試試,就會發(fā)現(xiàn)事情沒想象的那么簡單。直到2017年,騰訊才提出了把TextCNN做到更深的DPCNN模型:

上圖中的ShallowCNN指TextCNN。DPCNN的核心改進(jìn)如下:
在Region embedding時不采用CNN那樣加權(quán)卷積的做法,而是對n個詞進(jìn)行pooling后再加個1x1的卷積,因為實驗下來效果差不多,且作者認(rèn)為前者的表示能力更強,容易過擬合 使用1/2池化層,用size=3 stride=2的卷積核,直接讓模型可編碼的sequence長度翻倍(自己在紙上畫一下就get啦) 殘差鏈接,參考ResNet,減緩梯度彌散問題
憑借以上一些精妙的改進(jìn),DPCNN相比TextCNN有1-2個百分點的提升。
TextRCNN
論文:https://dl.acm.org/doi/10.5555/2886521.2886636
代碼:https://github.com/649453932/Chinese-Text-Classification-Pytorch
除了DPCNN那樣增加感受野的方式,RNN也可以緩解長距離依賴的問題。下面介紹一篇經(jīng)典TextRCNN。

模型的前向過程是:
得到單詞 i 的表示 通過RNN得到左右雙向的表示 和 將表示拼接得到 ,再經(jīng)過變換得到 對多個 進(jìn)行 max-pooling,得到句子表示 ,在做最終的分類
這里的convolutional是指max-pooling。通過加入RNN,比純CNN提升了1-2個百分點。
TextBiLSTM+Attention
論文:https://www.aclweb.org/anthology/P16-2034.pdf
代碼:https://github.com/649453932/Chinese-Text-Classification-Pytorch
從前面介紹的幾種方法,可以自然地得到文本分類的框架,就是先基于上下文對token編碼,然后pooling出句子表示再分類。在最終池化時,max-pooling通常表現(xiàn)更好,因為文本分類經(jīng)常是主題上的分類,從句子中一兩個主要的詞就可以得到結(jié)論,其他大多是噪聲,對分類沒有意義。而到更細(xì)粒度的分析時,max-pooling可能又把有用的特征去掉了,這時便可以用attention進(jìn)行句子表示的融合:

BiLSTM就不解釋了,要注意的是,計算attention score時會先進(jìn)行變換:
其中 是context vector,隨機初始化并隨著訓(xùn)練更新。最后得到句子表示 ,再進(jìn)行分類。
這個加attention的套路用到CNN編碼器之后代替pooling也是可以的,從實驗結(jié)果來看attention的加入可以提高2個點。如果是情感分析這種由句子整體決定分類結(jié)果的任務(wù)首選RNN。
HAN
論文:https://www.aclweb.org/anthology/N16-1174.pdf
代碼:https://github.com/richliao/textClassifier
上文都是句子級別的分類,雖然用到長文本、篇章級也是可以的,但速度精度都會下降,于是有研究者提出了層次注意力分類框架,即Hierarchical Attention。先對每個句子用 BiGRU+Att 編碼得到句向量,再對句向量用 BiGRU+Att 得到doc級別的表示進(jìn)行分類:

方法很符合直覺,不過實驗結(jié)果來看比起avg、max池化只高了不到1個點(狗頭,真要是很大的doc分類,好好清洗下,fasttext其實也能頂?shù)模ㄎ婺槨?/p>
BERT
BERT的原理代碼就不用放了叭~
BERT分類的優(yōu)化可以嘗試:
多試試不同的預(yù)訓(xùn)練模型,比如RoBERT、WWM、ALBERT 除了 [CLS] 外還可以用 avg、max 池化做句表示,甚至可以把不同層組合起來 在領(lǐng)域數(shù)據(jù)上增量預(yù)訓(xùn)練 集成蒸餾,訓(xùn)多個大模型集成起來后蒸餾到一個上 先用多任務(wù)訓(xùn),再遷移到自己的任務(wù)
其他模型
除了上述常用模型之外,還有Capsule Network[4]、TextGCN[5]等紅極一時的模型,因為涉及的背景知識較多,本文就暫不介紹了(嘻嘻)。
雖然實際的落地應(yīng)用中比較少見,但在機器學(xué)習(xí)比賽中還是可以用的。Capsule Network被證明在多標(biāo)簽遷移的任務(wù)上性能遠(yuǎn)超CNN和LSTM[6],但這方面的研究在18年以后就很少了。TextGCN則可以學(xué)到更多的global信息,用在半監(jiān)督場景中,但碰到較長的需要序列信息的文本表現(xiàn)就會差些[7]。
技巧
模型說得差不多了,下面介紹一些自己的數(shù)據(jù)處理血淚經(jīng)驗,如有不同意見歡迎討論~
數(shù)據(jù)集構(gòu)建
首先是標(biāo)簽體系的構(gòu)建,拿到任務(wù)時自己先試標(biāo)一兩百條,看有多少是難確定(思考1s以上)的,如果占比太多,那這個任務(wù)的定義就有問題。可能是標(biāo)簽體系不清晰,或者是要分的類目太難了,這時候就要找項目owner去反饋而不是繼續(xù)往下做。
其次是訓(xùn)練評估集的構(gòu)建,可以構(gòu)建兩個評估集,一個是貼合真實數(shù)據(jù)分布的線上評估集,反映線上效果,另一個是用規(guī)則去重后均勻采樣的隨機評估集,反映模型的真實能力。訓(xùn)練集則盡可能和評估集分布一致,有時候我們會去相近的領(lǐng)域拿現(xiàn)成的有標(biāo)注訓(xùn)練數(shù)據(jù),這時就要注意調(diào)整分布,比如句子長度、標(biāo)點、干凈程度等,盡可能做到自己分不出這個句子是本任務(wù)的還是從別人那里借來的。
最后是數(shù)據(jù)清洗:
去掉文本強pattern:比如做新聞主題分類,一些爬下來的數(shù)據(jù)中帶有的XX報道、XX編輯高頻字段就沒有用,可以對語料的片段或詞進(jìn)行統(tǒng)計,把很高頻的無用元素去掉。還有一些會明顯影響模型的判斷,比如之前我在判斷句子是否為無意義的閑聊時,發(fā)現(xiàn)加個句號就會讓樣本由正轉(zhuǎn)負(fù),因為訓(xùn)練預(yù)料中的閑聊很少帶句號(跟大家的打字習(xí)慣有關(guān)),于是去掉這個pattern就好了不少 糾正標(biāo)注錯誤:這個我真的屢試不爽,生生把自己從一個算法變成了標(biāo)注人員。簡單的說就是把訓(xùn)練集和評估集拼起來,用該數(shù)據(jù)集訓(xùn)練模型兩三個epoch(防止過擬合),再去預(yù)測這個數(shù)據(jù)集,把模型判錯的拿出來按 abs(label-prob) 排序,少的話就自己看,多的話就反饋給標(biāo)注人員,把數(shù)據(jù)質(zhì)量搞上去了提升好幾個點都是可能的
長文本
任務(wù)簡單的話(比如新聞分類),直接用fasttext就可以達(dá)到不錯的效果。
想要用BERT的話,最簡單的方法是粗暴截斷,比如只取句首+句尾、句首+tfidf篩幾個詞出來;或者每句都預(yù)測,最后對結(jié)果綜合。
另外還有一些魔改的模型可以嘗試,比如XLNet、Reformer、Longformer。
如果是離線任務(wù)且來得及的話還是建議跑全部,讓我們相信模型的編碼能力。
少樣本
自從用了BERT之后,很少受到數(shù)據(jù)不均衡或者過少的困擾,先無腦訓(xùn)一版。
如果樣本在幾百條,可以先把分類問題轉(zhuǎn)化成匹配問題,或者用這種思想再去標(biāo)一些高置信度的數(shù)據(jù),或者用自監(jiān)督、半監(jiān)督的方法。
魯棒性
在實際的應(yīng)用中,魯棒性是個很重要的問題,否則在面對badcase時會很尷尬,怎么明明那樣就分對了,加一個字就錯了呢?
這里可以直接使用一些粗暴的數(shù)據(jù)增強,加停用詞加標(biāo)點、刪詞、同義詞替換等,如果效果下降就把增強后的訓(xùn)練數(shù)據(jù)洗一下。
當(dāng)然也可以用對抗學(xué)習(xí)、對比學(xué)習(xí)這樣的高階技巧來提升,一般可以提1個點左右,但不一定能避免上面那種尷尬的情況。
總結(jié)
文本分類是工業(yè)界最常用的任務(wù),同時也是大多數(shù)NLPer入門做的第一個任務(wù),我當(dāng)年就是啥都不會,從訓(xùn)練到部署地實踐了文本分類后就順暢了。上文給出了不少模型,但實際任務(wù)中常用的也就那幾個,下面是快速選型的建議:

實際上,落地時主要還是和數(shù)據(jù)的博弈。數(shù)據(jù)決定模型的上限,大多數(shù)人工標(biāo)注的準(zhǔn)確率達(dá)到95%以上就很好了,而文本分類通常會對準(zhǔn)確率的要求更高一些,與其苦苦調(diào)參想fancy的結(jié)構(gòu),不如好好看看badcase,做一些數(shù)據(jù)增強提升模型魯棒性更實用。
參考資料
A Sensitivity Analysis of (and Practitioners' Guide to) Convolutional Neural Networks for Sentence Classification: https://arxiv.org/pdf/1510.03820.pdf
[2]卷積層和分類層,哪個更重要?: https://www.zhihu.com/question/270245936
[3]從經(jīng)典文本分類模型TextCNN到深度模型DPCNN: https://zhuanlan.zhihu.com/p/35457093
[4]揭開迷霧,來一頓美味的Capsule盛宴: https://kexue.fm/archives/4819
[5]Graph Convolutional Networks for Text Classification: https://arxiv.org/abs/1809.05679
[6]膠囊網(wǎng)絡(luò)(Capsule Network)在文本分類中的探索: https://zhuanlan.zhihu.com/p/35409788
[7]怎么看待最近比較火的 GNN?: https://www.zhihu.com/question/307086081/answer/717456124

