這個月谷歌提出的 BERT 受到了很多關注,,該研究憑借預訓練模型刷新了 11 項 NLP 任務的當前最優(yōu)性能記錄。論文作者表示這個月月末會放出代碼與預訓練模型,,但目前還沒有發(fā)布,。因此很多研究者嘗試使用中等數據集降低計算力,或使用 OpenAI 的 Transformer 預訓練模型作為初始化條件。
本文介紹的兩個 BERT 實現項目分別基于 TensorFlow 和 Keras,,其中基于 TensorFlow 的項目會使用中等數據集與其它技巧降低計算力,,并發(fā)現使用 TextCNN 代替 Transformer 主干網絡,且保留 BERT 預訓練任務也能得到非常好的效果,。而基于 Keras 的項目嘗試使用預訓練的 OpenAI Transformer 作為初始化權重,,并以較小的計算力重新訓練 BERT 預訓練模型,再將該預訓練的 BERT 應用到不同任務,。
這兩個項目都在嘗試使用 BERT 核心思想,并以較小的計算成本應用于其它 NLP 任務,。當然如果讀者希望使用大型 BERT 預訓練模型,,還需要等谷歌官方發(fā)布代碼與模型。
BERT 簡介
機器之心SyncedBERT小程序
BERT 的全稱是基于 Transformer 的雙向編碼器表征,,其中「雙向」表示模型在處理某一個詞時,,它能同時利用前面的詞和后面的詞兩部分信息。這種「雙向」的來源在于 BERT 與傳統(tǒng)語言模型不同,,它不是在給定所有前面詞的條件下預測最可能的當前詞,,而是隨機遮掩一些詞,并利用所有沒被遮掩的詞進行預測,。下圖展示了三種預訓練模型,,其中 BERT 和 ELMo 都使用雙向信息,OpenAI GPT 使用單向信息,。
如上所示為不同預訓練模型的架構,,BERT 可以視為結合了 OpenAI GPT 和 ELMo 優(yōu)勢的新模型。其中 ELMo 使用兩條獨立訓練的 LSTM 獲取雙向信息,,而 OpenAI GPT 使用新型的 Transformer 和經典語言模型只能獲取單向信息,。BERT 的主要目標即在 OpenAI GPT 的基礎上對預訓練任務做一些改進,以同時利用 Transformer 深度模型與雙向信息的優(yōu)勢,。
BERT 的核心過程非常簡潔,,它會先從數據集抽取兩個句子,其中第二句是第一句的下一句概率是 50%,,這樣就能學習句子之間的關系,。其次隨機去除兩個句子中的一些詞,并要求模型預測這些詞是什么,,這樣就能學習句子內部的關系,。最后再將經處理的句子傳入大型 Transformer 模型,并通過兩個損失函數同時學習上面兩個目標就能完成訓練,。
TensorFlow 實現項目簡介
BERT 最近在 10 幾項 NLP 任務上取得了新進展,,這個項目是《BERT:Pre-training of Deep Bidirectional Transformers for Language Understanding》和《Attention is all You Need》這兩篇論文的 tensorflow 實現,。
這個項目提供了預訓練方法與代碼,,并做了一些調整以加快收斂速度。這一份 TensorFlow 實現在使用中等數據集下計算力并不是太大,所以感興趣的讀者也可以嘗試使用,。當然,希望使用大型預訓練 BERT 模型的讀者可以等谷歌發(fā)布官方模型,。
項目地址:https://github.com/brightmart/bert_language_understanding
預訓練和微調實驗
項目作者把 Transfprmer 換成 TextCNN,,替換了 BERT 的主干網絡,結果發(fā)現使用大量原始數據用遮蔽語言模型預訓練的模型可以顯著提高性能,,因此他們認為預訓練和微調策略是獨立于模型和預訓練任務的,。因此,可以修正主干網絡添加更多的預訓練任務或者定義一些新的預訓練任務,,預訓練不限于遮蔽語言模型或預測下一句的任務,。讓人驚訝的是,對于中等規(guī)模的數據集(比如說一百萬條數據)來說,,即使不使用外部數據,,只要借助于預訓練任務(如帶掩碼的語言模型),性能也可以大幅提升,,而且模型可以更快地收斂,。有時在微調階段,訓練僅需幾個 epoch,。
目的
雖然開放源碼(tensor2tensor)和 Transformer 與 BERT 的官方實現實現即將到來,,但是有可能很難懂,不易理解,。該項目的作者并不打算完全復制原始文件,,而是要應用主要思想,以更好的方式解決 NLP 問題,。本文的大部分工作是去年由另一個 GitHub 項目修改完成的:文本分類(https://github.com/brightmart/text_classification),。
性能
下圖是在中等規(guī)模數據集(cail2018,45 萬)上文本分類任務的測試效果,其中采用 BERT 預訓練思想的 TextCNN 要顯著優(yōu)于其它模型:
下圖是在小規(guī)模數據集(private,,10 萬)上的測試效果:
TensorFlow 實現項目細節(jié)
使用方法
如果想在 Masked 語言模型上預訓練 BERT 模型,,并在新 NLP 任務上使用它,那么使用方法主要可以分為兩步驟,。值得注意的是,,該項目并沒有提供預訓練模型,所以需要大量計算力的預訓練過程仍然需要自行執(zhí)行,。
1. 通過 BERT 預訓練語言模型
python train_bert_lm.py [DONE]
2. 在新任務微調模型
python train_bert_fine_tuning.py [Done]
在項目作者的試驗中,,即使在微調的起點,剛剛從預訓練模型恢復參數也能獲得比從頭訓練更低的損失,。預訓練模型的 F1 值同樣要比從頭訓練的高,,且從頭訓練的 F1 值還需要從零開始增長,。
此外為了快速測試新想法與模型,可以將超參數 test_mode 設置為 True,,在這種模式下模型只會加載少量的數據進行測試,,因此訓練非常迅速。
在基本步驟中,,還可以通過 Transform 解決文本分類問題:
python train_transform.py [DONE, but a bug exist prevent it from converge, welcome you to fix, email: [email protected]]
預訓練和微調過程還有其它一些可選超參數,,它們控制了模型的大小與訓練過程:
d_model:模型維度,默認為 [512],。
num_layer:層級數,,默認為 [6]。
num_header:自注意力機制的 Head 數,,默認為 [8],。
d_k:Key(K)和 Query(Q)的維度,默認為 [64],。
d_v:Value(V)的維度,,默認為 [64],。
default hyperparameter is d_model=512,h=8,d_k=d_v=64(big). if you have want to train the model fast, or has a small data set
or want to train a small model, use d_model=128,h=8,d_k=d_v=16(small), or d_model=64,h=8,d_k=d_v=8(tiny).
實現細節(jié)
首先,,TensorFlow 的實現環(huán)境比較簡單:python 3+ tensorflow 1.10。其次,,實現時要注意以下問題:
1. 預訓練和微調階段之間有哪些能夠共享和無法共享的參數,?
基本來說,預訓練和微調階段的主干網絡使用的所有參數都能共享,。
既然可以共享很多參數,,那微調階段只需學習很少的參數,此外這兩個階段的詞嵌入也可以共享,。
因此,,在微調的初始階段已經學習了大部分參數。
2. 如何實現帶 Mask 的語言模型,?
為了讓事情變得簡單點,,可以根據文件生成很多句子。把每個句子截斷并填充到相同的長度,,然后隨機選擇一個單詞,,用 [MASK]、單詞本身或一個隨機單詞替換它,。
3. 如何使微調階段變得更高效并同時不影響在預訓練階段學到的結果和知識,?
在微調階段使用較小的學習率,因此只需在很小的范圍內進行調整,。
Keras 實現
基于 TensorFlow 的實現同樣沒有提供預訓練語言模型,,這樣的模型在預訓練階段會需要大量的計算力,,這樣的計算力需求對于很多研究者與開發(fā)者都是接受不了的。但是現在的官方實現與預訓練模型仍然沒有放出來,,因此有開發(fā)者利用 OpenAI 預訓練的 Transformer 作為初始化參數,,并訓練新的 BERT 預訓練模型,這種方式大大降低了計算力需求,。
項目地址:https://github.com/Separius/BERT-keras
在這個 Keras 實現項目中,,作者用預訓練的 OpenAI Transformer 作為初始化條件,并訓練新的 BERT,,項目作者表示這樣可以不使用 TPU 而實現預訓練,。以下展示了 Keras 實現的主要語句,包括加載 OpenAI Transformer 預訓練模型,、加載 BERT 模型和保存新的預訓練權重等,。
# this is a pseudo code you can read an actual working example in tutorial.ipynb
text_encoder = MyTextEncoder(**my_text_encoder_params) # you create a text encoder (sentence piece and openai's bpe are included)
lm_generator = lm_generator(text_encoder, **lm_generator_params) # this is essentially your data reader (single sentence and double sentence reader with masking and is_next label are included)
task_meta_datas = [lm_task, classification_task, pos_task] # these are your tasks (the lm_generator must generate the labels for these tasks too)
encoder_model = create_transformer(**encoder_params) # or you could simply load_openai()
trained_model = train_model(encoder_model, task_meta_datas, lm_generator, **training_params) # it does both pretraing and finetuning
trained_model.save_weights('my_awesome_model') # save it
model = load_model('my_awesome_model', encoder_model) # load it later and use it!
作者表示這個項目有一些很重要說明,針對不同的任務與需求,,可以根據這些說明修改模型結構和預訓練過程,。
這個庫的核心觀點是使用 OpenAI 的預訓練模型作為訓練新模型的初始狀態(tài),因此通過 GPU 就能訓練 BERT,。
通過 Keras 加載 OpenAI 模型已經在 TensorFlow 后端和 Theano 后端得到測試,。
對于大多數 NLP 模型,能使用這個項目定義的數據生成器和任務元數據,,即使在其它框架中也是,。
數據集和 Transformer 都會執(zhí)行一些單元測試,如果你不太了解代碼可以閱讀這些測試,。
還可以使用其它編碼器進行訓練,,例如 LSTM 或 BiQRNN 等。
當官方代碼發(fā)布后會發(fā)生什么,?數據讀取器仍然會保持穩(wěn)定,,甚至可以導入官方發(fā)布的權重到這個庫中(作者認為他會完成這一過程,因為實際的 Transformer 還是比較容易實現的)
作者強烈建議閱讀項目中的 tutorial.ipynb 文件,,它展示了整個項目的使用過程,。
重要的代碼概念
任務:有兩個一般任務,句子級任務(如下一句預測和情感分析)和 token 級任務(如詞性標注和命名實體識別),。
句子:「句子」表示一段帶有標簽和所有內容的實例,,它為每個任務提供了一個目標(句子級任務的單個標注值,token 級任務的每個 token 標簽)和一個掩碼,;對于 token 級任務,,除了忽略 padding 外,還要使用第一個符號向量預測類別(BERT 中的 [CLS] 符號),。
TaskWeightScheduler:項目作者希望從語言模型開始訓練,,然后遷移到到分類任務,,我們可以用這個類快速實現。
special_tokens:pad, start, end, delimiter, mask