收錄於話題 #自然語言處理 203個
去年在文章那個屠榜的T5模型,現在可以在中文上玩玩了中我們介紹了 Google 的多國語言版 T5 模型(mT5),並給出了用 mT5 進行中文文本生成任務的例子。誠然,mT5 做中文生成任務也是一個可用的方案,但缺乏完全由中文語料訓練出來模型總感覺有點彆扭,於是決心要搞一個出來。
經過反覆斟酌測試,我們決定以 mT5 為基礎架構和初始權重,先結合中文的特點完善 Tokenizer,然後模仿 PEGASUS [1] 來構建預訓練任務,從而訓練一版新的 T5 模型,這就是本文所開源的 T5 PEGASUS。
Tokenizer
首先,這裡介紹我們對 Tokenizer 的完善工作。mT5 使用的 Tokenizer 是 sentencepiece [2] ,這是一個 C++ 所寫的分詞庫,具有高效輕便的特點,但是很遺憾,對於中文來說它並不是特別友好,主要體現為:
1. sentencepiece 會把某些全形符號強制轉化為半角符號,這在某些情況下是難以接受的,而且還可能影響任務的評測結果;
2. sentencepiece 內置的算法雖然有能力分出中文詞來,但對於中文分詞來說其實還是不夠智能的;
3. sentencepiece 用 C++ 寫的,雖然開源了,但對於用慣 Python 的人來說 C++ 就相當於黑箱,難以閱讀源碼,改起來也不容易。
這些特點讓我們決定將 Tokenizer 切換回 BERT 的 Tokenizer。但直接替換原始版本的中文 BERT 的 Tokenizer 是不夠的,一來是我們之前的工作《提速不掉點:基於詞顆粒度的中文 WoBERT》[3] 已經表明以詞為單位來做生成模型能獲得更好的效果。
二來哪怕只看字中文 BERT 的 vocab.txt 也是很不完善的,漏了一些常見的標點符號(如雙引號)和中文字(比如「琊」等)。為此,我們選擇給 BERT 的 tokenizer 加入分詞功能,並進一步完善 vocab.txt。
具體來說,我們往原始中文 BERT 的 token_dict 裡邊加入結巴分詞的前 20 萬個詞,然後修改 Tokenizer 的邏輯,使得它能夠切分出詞來,這些改動都已經內置在 bert4keras 中了,直接調用就行。接著,我們用這個修改後的 Tokenizer 去遍歷切分我們準備的預訓練語料,統計各個 token 的頻數,最後只保留最高頻的 5 萬個 token,得到一個規模為 5 萬的 vocab.txt 來構建我們最終的 Tokenizer。
除了用這個新 Tokenizer 來訓練 T5 PEGASUS 外,我們還用它來重新訓練了一版 WoBERT 模型(WoBERT+),也歡迎讀者嘗試。
https://github.com/ZhuiyiTechnology/WoBERT
預訓練任務
對於預訓練任務,我們希望更加接近自然語言生成(而不是像 T5 那樣的只預測挖空部分),並且儘可能具有實用價值。為此,我們關注到了 PEGASUS,來自論文《PEGASUS: Pre-training with Extracted Gap-sentences for Abstractive Summarization》[4]。
PEGASUS 在其論文稱是專門為摘要定製的預訓練模型,但在我們看來,它也可以作為通用的生成式預訓練任務。PEGASUS 的大體思路是通過最長公共子序列的方式該摘要類似的數據對,T5 PEGASUS 並沒有完全復現 PEGASUS 的做法,只是借鑑了 PEGASUS 的思路做語料構建。▲ T5 PEGASUS的訓練數據示例
具體來說,假設一個文檔有 n 個句子,我們從中挑出大約 n/4 個句子(可以不連續),使得這 n/4 個句子拼起來的文本,跟剩下的 3n/4 個句子拼起來的文本,最長公共子序列儘可能長,然後我們將 3n/4 個句子拼起來的文本視為原文,n/4 個句子拼起來的文本視為摘要,這樣就構成了一個「(原文, 摘要)」的偽摘要數據對了,就用這些數據對去訓練 Seq2Seq 模型即可。
注意,如果文檔裡沒有重複句子的話,那麼原文跟摘要的句子是不會有交集的,所以這樣的生成任務並非是原文的簡單複製,因此還是有一定難度的。
搜索算法則是通過如下的貪心算法逐步搜索至滿足長度要求:
1. 先找出 1 個句子,使得它跟生成的 n-1 個句子的最長公共子序列最長;
2. 假設已經找到了 k 個句子,那麼繼續找第 k+1 個句子,使得這 k+1 個句子拼起來的文本,跟剩下的 n-k-1 個句子拼起來的文本的最長公共子序列最長。
參數與配置
目前開源的 T5 PEGASUS 是 base 版,總參數量為 2.75 億,訓練時最大長度為512,batch_size [5] 為 96,學習率為