圖像中的Pretraning往往是在大規模圖像集上進行訓練後,再在特定的任務上進行fine-turning。而nlp領域的fine-turning就是word embedding了。而詞嵌入(例如word2vec,GloVe)通常是在一個較大的語料庫上利用詞的共現統計預訓練得到的。例如king和queen上下文時常相同或相似,所以詞向量相似,在向量空間中詞距離很近。
但是word2vec在訓練完畢後,某個單詞的表示向量就已經確定了,這樣的向量對於一詞多義其實是沒有幫助的,換一個語境單詞所表達的意思就完全不一樣的,即Word2vec的處理其實是與上下文無關的(雖然可能用了多個不同語境的句子進行計算,但是最後總會得到一個向量,無法得到區分)。雖然可以在此基礎之上做一些聚類,但是也只是治標不治本。那如何做到靜態變動態呢?
ELMo(embedding from language models)
paper:https://arxiv.org/pdf/1802.05365.pdf
官網:https://allennlp.org/elmo
code:https://github.com/allenai/allennlp
ELMO的fine-turning:在使用中動態調整。
即,對於已經用語言模型訓練好了的Word Embedding,雖然無法處理多義詞,但在句子中使用這個詞向量的時候,會結合當前句子的上下文去調整這個單詞的Embedding,就ok了。(有點不同的是,NLP的pre往往是無監督的)
- 實現細節如上圖。想要結合上下文信息,很自然的想法就是利用LSTM,<s>是句子的開頭要逐詞的預測下一位。(這個bilstm就可以理解爲 nlp的fine-turning…有時候也把原輸入embedding也拼接,即三個向量的cat)另外由於ELMo的輸入是字母爲單位,而不是單詞,所以對於 hhhhhh 這種沒有記錄的單詞也有很好的效果,至少能給出相近詞性的解釋。
- ELMo是基於BiLSTM來抽取文本特徵的,即得到左右綠色和藍色的兩個LSTM特徵後,直接拼接(或者用alpha做控制)輸入到模型中去做下游任務。
- 最後通過打印出在不同下游預訓練任務中哪些特徵比較重要,如右下角,在不同的時候某些特徵參數會格外的large,這在BERT等其他模型中可以觀察到,即預訓練模型究竟做了什麼,它是怎麼根據應用而適時的調整詞向量的。
GPT(Generative Pre-Training)
paper:https://www.cs.ubc.ca/~amuham01/LING530/papers/radford2018improving.pdf
code:https://openai.com/blog/language-unsupervised/
GPT和ELMo一樣,也是先語言模型預訓練embedding,然後再fine-turning。fine-turning的不同點在於
- LSTM升級爲Transformer提取特徵
- Bi捨棄,變成單向而不是雙向。作者認爲如果雙向,在編碼解碼中有些單詞可能會間接的作弊,即提前的‘’看到‘’自己。(但是實際證明上下文信息對詞語的理解是非常重要了,所以之後的模型還是使用了雙向,並使用別的trick解決“作弊”問題)
由於結果固定住了,GPT在使用時需要讓 下游任務 往GPT的輸出靠攏,不過效果好什麼都中。
- 序列標註:中文分詞,詞性標註,命名實體識別,語義角色標註等。
- 分類任務:文本分類,情感計算
- 句子關係判斷:Entailment,QA,語義改寫,自然語言推理
- 生成式任務:機器翻譯,文本摘要,寫詩造句,看圖說話
- 細分下來有,自然語言十項全能挑戰(decaNLP),涉及十個任務:問答,機器翻譯,摘要,自然語言推理,情感分析,詞性標註,關係抽取,目標導向對話,數據庫查詢生成和代詞解析
BERT(Bidirectional Encoder Representation from Transformer)
劃時代之作。
paper:https://arxiv.org/abs/1810.04805
code:https://github.com/google-research/bert
- LSTM太慢了啦 —> 還是Transformer吧,並行而且還沒有梯度的問題
- 人類理解語言會同時考慮上下文的 —> 雙向吧,只要不看要預測的部分就可以
從圖看BERT的輸入是兩個句子A和B進行成對訓練,輸入以【CLS】(classification token)隔開,句子則是【SEP】(special token)隔開。。然後特徵由三個部分組成:詞嵌入後的token embedding,句子類別的符號segment embedding句子A,句子B,詞的位置信息position embedding,這跟Transformer裏面是一致的。這三個部分相加之後作爲輸入。然後Loss=MLM+NSP進行fine-turning:
- Masked Language Model(MLM)。靈感來自完型填空,先扣掉(以【MASK】標記),再嘗試恢復。【MASK】總詞的15%,在15%中:80%用[MASK]標記替換,10%用其他的詞隨機替換,10%保持原詞不做替換,這一部分的loss只針對【MASK】位置的輸出。(即其他位置的詞輸出正確與否,不在MLM計算)
- Next Sentence Prediction(NSP)。NSP關注兩個句子間的關係,所以輸出是binarized task即成對訓練。對於成對訓練的【句子A,句子B】的構建,50%的兩個句子對間有上下關係,此時label爲IsNext或1,剩下50%的句子無上下關係(隨機選擇),標記label爲NoNext或0。
Training tips:
- 1dupe_factor,從訓練文檔中重複使用一些數據的比例,訓練集數目少的時候可以設置高一些
- 2max_predictions_per_eq,控制一句話裏面被mask的最大數目
- 3do_whole_word_mask,如果是true就整個詞矇住,如果false則是會傾向矇住單詞裏的字母(個人理解這種操作同樣也是填空的一種形式,矇住後的詞同樣是不認識的新詞,需要靠語境來判斷)
- 4length of a sentence is larger than the limit,句子中的最大長度限制
BERT的缺點:
- 【MASK】只會在預訓練中使用,後續任務是不會出現的,所以導致訓練環境和測試環境不一樣(一個有mask,一個沒有mask)。實際處理中,在後續任務時需要同樣按相同的比率替換句子。即把輸入序列中的k%(一般爲15%,k太小:計算代價過大。k太大:剩下的上下文不足以準確預測)的詞掩蓋住,然後通過上下文預測這些被掩蓋住的詞。
- independent assumption。每個詞都是獨立的,很多短語被強行拆開(如New York)。(這點由於ELMo服從概率完備,用LSTM基於前面的詞的隱向量,預測後面的句子,所以可以處理好這個問題,即詞和詞之間有聯繫的)
- 缺乏生成能力。是mased地方的重構。
BERT的本質
BERT的本質是Denoising Autoencoder,降噪自編碼。
- 預訓練是無監督的,尋找詞自己之間的關係和表達。之後在下游任務中才會有監督。
- AE:輸入–中間特徵–輸出,約束輸入==輸出。DAE:輸入+噪聲–中間特徵–輸出,在同樣的約束下起降噪的作用使結果更加的魯棒。而BERT:輸入+【MASK】–Transformer–輸出預測【MASK】。所以實際上,【MASK】的作用就是加了噪音,然後利用Transformer進行降噪。
XLNet(Generalized Autoregressive Pretraining for Language)
集大成者來了。
- 自迴歸語言模型(Autoregressive LM)。代表是ELMo 和 GPT,根據前文預測後面的單詞或者反過來的思路。優點是:詞與詞之間是有聯繫的。缺點是:只能利用上文或者下文的信息,不能同時利用上文和下文的信息(ELMO雖然雙向都做,但是是僞雙向,即正向的時候用不到反向,反向用不到正向)。
- 自編碼語言模型(Autoencoder LM)。代表BERT。優點:直接就是上下文都能夠考慮到。缺點:1 詞和詞割裂 2使用了【MASK】使訓練和測試的環境不一致。
XLNet:我要融合。我要構建一個
- 考慮上下文雙向信息
- 考慮詞間依賴關係
- 測試環境一致
融合思路顯然是改裝ELMo更爲方便,只需要把ELMo變得可以處理真正的上下文就行。那麼如何在只看得到前面詞的時候,也能揉入後面的詞?
- 亂序全排列語言模型(Permutation Language model),即如果每個詞都可以有不同的位置,就可以利用到上下文信息了!如上圖上標是層數(多層Transformer),下標是位置信息,正常情況下一個四個詞的句子詞順序是【1 2 3 4】,那麼打亂這個順序變成【3 2 4 1】,【2 4 3 1】等等,那麼在預測比如的時候,【1 2 3 4】的3的前面有1 2的信息,【3 2 4 1】的3的沒有其他的詞即會是開始符,【2 4 3 1】的3的前面有2 4的信息可以利用,以前正常的順序是無法看到4號的信息,打亂之後就可以用到了。
- 在具體的實現中,不用打亂輸入(即還是1234),只是此時的3要用到誰,就連接誰,如上圖的第2個圖【2 4 3 1】,3的前面有2 4的信息可以用,那麼連接了和,還有mem(即在當前詞輸出前,已經輸出的隱向量表示,即在預測3號的時候,實際上2和4已經輸出了,mem是它倆的表示)
- 雙流自注意力機制(Two-stream Self Attention)。PLM實際上已經完成了融合的任務,但是此處存在一個衝突問題需要解決即:1 預測自己時不能有自己的內容信息(如果知道了去預測就作弊了,只能用和的信息預測) 2預測別人時,自己由作爲了上下文,又需要給出所有信息(內容信息+位置信息)。(預測完後,需要輸出,此時需要用到其他詞的向量)。衝突就在於:既要沒有自己,自己又不能丟?怎麼辦?
- two-stream,雙流,一個流content stream是既有內容又有位置信息,一個流query stream是隻有位置信息,沒有自己的內容信息。實現如下圖,和分別是兩個流的隱層,圖a是標準self-Attention組成的content stream,查詢得到,圖b是不能跟自己內容有連接的query stream,然後在不同情況下用不同的流進行計算就行。
- 此外關於打亂的順序由利用Attention masks實現,即如圖中的矩陣紅色代表有關係。(在content stream第一行與1234有關,第二行與23有關,第三行與3有關,第四行與234有關,即邏輯上的順序是【3 2 4 1】,query stream中則是沒有對角線即不能看到自己,不作弊)通過不同的掩碼,就能得到不同邏輯順序的句子了。
Trick:
引入了Transformer-XL做提升,Transformer-XL=Transformer+RNN。
- 原因:Transformer的最大缺點是每個Transformer的所有的Decoder層的矩陣都要保存以便BP,但是預訓練模型往往需要很大量的計算,比如直接輸入好幾本書,計算資源消耗比較大,所以導致Transformer每次無法輸入很長的文本進行訓練。
- 主要思路:相對位置編碼以及分段RNN機制。即第一頁句子在第一個Transformer訓練,第二頁在另一個訓練,由於兩者之間可能存在關係,再使用分段RNN機制進行連接。實踐已經證明這兩點對於長文檔任務是很有幫助的。
未來:1Transformer-XL爲什麼有效?2permutation的採樣如何更加有效 3compress成Albert 4合適的融合應用。
和BERT其實是一回事?
- Bert是直接在輸入端顯示地通過引入Mask標記,在輸入側隱藏掉一部分單詞,讓這些單詞在預測的時候不發揮作用,要求利用上下文中其它單詞去預測某個被Mask掉的單詞;而XLNet則拋棄掉輸入側的Mask標記,通過Attention Mask機制,在Transformer內部隨機Mask掉一部分單詞(這個被Mask掉的單詞比例跟當前單詞在句子中的位置有關係,位置越靠前,被Mask掉的比例越高,位置越靠後,被Mask掉的比例越低),讓這些被Mask掉的單詞在預測某個單詞的時候不發生作用。
(詳細請參看張俊林大牛:https://zhuanlan.zhihu.com/p/70257427)
應用BERT提特徵
實際訓練BERT 需要用 WordPiece工具分詞,並插入各種特殊的分離符如([CLS],用來分隔樣本)和分隔符([SEP],用來分隔樣本內的不同句子)等等細節,其訓練花費很輕鬆只需要76分鐘!(如果我們能有1024塊TPU…在16 個 Cloud TPU 上則需要訓練 4 天, GPT-2是BERT的3倍,而XLNet在128 個 Cloud TPU v3 下需要訓練 2 天半,花費以美金算emmm)
所以還是看怎麼使用Pretraning!
版本一:使用自己的詞彙庫進行訓練
https://github.com/codertimo/BERT-pytorch
1. pip install bert-pytorch
2. 準備語料庫,兩個句子在同一行,並用 \t 分割。如
Welcome to the \t the jungle\n
I can stay \t here all night\n
3. 建立vocab
bert-vocab -c data/corpus.small -o data/vocab.small
4. 訓練bert
bert -c data/corpus.small -v data/vocab.small -o output/bert.model
版本二:利用訓練好的模型進行向量抽取
https://github.com/hanxiao/bert-as-service 或者keras的版本:https://github.com/CyberZHG/keras-bert
1. 下載英文詞向量:https://storage.googleapis.com/bert_models/2018_11_03/english_L-12_H-768_A-12.zip
2. 或者中文詞向量:https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip
3. 開啓服務器BERT:bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
訪問本機
from bert_serving.client import BertClient
bc = BertClient()
bc.encode(['First do it', 'then do it right', 'then do it better'])
遠程訪問,加入端口號
from bert_serving.client import BertClient
bc = BertClient(ip='xx.xx.xx.xx') # ip address of the GPU machine
bc.encode(['First do it', 'then do it right', 'then do it better'])
或者跑example的代碼,親測真好用…給大佬加星。
版本三:直接使用開源工具
文本摘要:https://github.com/dmmiller612/bert-extractive-summarizer
from summarizer import Summarizer
text='xxxx xxx'
model=Summarizer()
model(text)
ALBERT: A Lite BERT for Language Understanding:
最後,最近又有火爆的模型出現了!由於BERT參數太了(base版1.1億,large版3.4億),ALBERT模型的目的是實現參數量輕量級。它主要的優化是:
- factorized embedding parametrization。在one-hot處理的過程中先用矩陣做低秩分解,從到,減少了19M個參數。
- 共享Transformer注意力所有層的參數。即不管多少層,每層參數都用一樣的(雖然參數共享,但每一層的輸出不一樣,Attention的動態調整效果也是不一樣的)。以前使用12層Transformer,所以參數數量變成12分之一。
- 不要dropout。提升不明顯,還佔內存,所以不使用。
- 將參數變少後性能略有降低,然後學Alex把網絡加寬加深,這樣在參數量比較小的情況下得到了比BERT性能很好的提升。
- 之前有人發文詬病:NSP構建正負例的方式,由於負例的句子是隨機組合,這樣容易讓網絡找到topic進行學習(即線索詞,A句子和B句子很無關,所以很容易分別上下文是否有聯繫)。所以作者將NSP變成SOP(sentence order prediction),即直接把A,B對換就行了,這樣保證了topic一致的情況下構建了正負例。
ERNIE2.0
ERNIE1.0如上,是把BERT改爲適合中文的模型,想法是來自對於中文的詞很容易猜到masked某個詞,所以masked是一整個詞(做到這一點將BERT融合了知識圖譜(knowledge graphs,KG),以知識圖譜中的多信息實體(informative entity)作爲外部知識改善語言表徵)。
而ERNIE2.0挺有意思的,也放上來一下,它主要貢獻是兩個:
- 序列多任務學習的預訓練任務機制(sequential multi-task learning),使模型能夠學習到詞彙,語法,語義信息。不同於持續學習和多任務學習,序列多任務學習在引入新的訓練任務時,先利用之前學習到的參數對模型進行初始化,再同時訓練新任務和舊任務。
- 定製和引入了多種預訓練任務。側重詞彙的任務(mask,大寫字詞預測,字詞-文章關係),側重結構/語法的任務(詞語重排序,語句距離),側重語義的任務(文章關係任務,信息檢索相關性任務)。
Future?
- Pre+KG。
- Pre+ENDEcodeer。如UULM。
- Pre+Multi-Learning。如MT-DNN,ERNIE。
- Updated Pre。如XLNet,RoBERTa。
- Pre+Chinese。BERT-WWM。
- Pre+Turning。
- Updated Transformer?Transformerxl?
模型對比
預訓練語言模型(PLM)論文清單:https://github.com/t/thunlp/PLMpaper