1. 程式人生 > >乾貨 | 谷歌BERT模型fine-tune終極實踐教程

乾貨 | 谷歌BERT模型fine-tune終極實踐教程

640?wx_fmt=jpeg


作者 | 奇點機智


從11月初開始,Google Research就陸續開源了BERT的各個版本。Google此次開源的BERT是通過TensorFlow高階API—— tf.estimator進行封裝(wrapper)的。因此對於不同資料集的適配,只需要修改程式碼中的processor部分,就能進行程式碼的訓練、交叉驗證和測試。


奇點機智技術團隊將結合利用BERT在AI-Challenger機器閱讀理解賽道的實踐表現以及多年的NLP經驗積累,為大家奉上BERT在中文資料集上的fine tune全攻略。


在自己的資料集上執行 BERT


BERT的程式碼同論文裡描述的一致,主要分為兩個部分。一個是訓練語言模型(language model)的預訓練(pretrain)部分。另一個是訓練具體任務(task)的fine-tune部分。在開源的程式碼中,預訓練的入口是在run_pretraining.py而fine-tune的入口針對不同的任務分別在run_classifier.py和run_squad.py。其中run_classifier.py適用的任務為分類任務。如CoLA、MRPC、MultiNLI這些資料集。而run_squad.py適用的是閱讀理解(MRC)任務,如squad2.0和squad1.1。


預訓練是BERT很重要的一個部分,與此同時,預訓練需要巨大的運算資源。按照論文裡描述的引數,其Base的設定在消費級的顯示卡Titan x 或Titan 1080ti(12GB RAM)上,甚至需要近幾個月的時間進行預訓練,同時還會面臨視訊記憶體不足的問題。


不過所幸的是谷歌滿足了Issues#2(https://github.com/google-research/bert/issues/2)裡各國開發者的請求,針對大部分語言都公佈了BERT的預訓練模型。因此在我們可以比較方便地在自己的資料集上進行fine-tune。


下載預訓練模型


對於中文而言,google公佈了一個引數較小的BERT預訓練模型。具體引數數值如下所示:


Chinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters


模型的下載連結:(https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip)


對下載的壓縮檔案進行解壓,可以看到檔案裡有五個檔案,其中bert_model.ckpt開頭的檔案是負責模型變數載入的,而vocab.txt是訓練時中文文字採用的字典,最後bert_config.json是BERT在訓練時,可選調整的一些引數。


修改 processor


任何模型的訓練、預測都是需要有一個明確的輸入,而BERT程式碼中processor就是負責對模型的輸入進行處理。我們以分類任務的為例,介紹如何修改processor來執行自己資料集上的fine-tune。在run_classsifier.py檔案中我們可以看到,google對於一些公開資料集已經寫了一些processor,如XnliProcessor,MnliProcessor,MrpcProcessor和ColaProcessor。這給我們提供了一個很好的示例,指導我們如何針對自己的資料集來寫processor。

對於一個需要執行訓練、交叉驗證和測試完整過程的模型而言,自定義的processor裡需要繼承DataProcessor,並重載獲取label的get_labels和獲取單個輸入的get_train_examples,get_dev_examples和get_test_examples函式。其分別會在main函式的FLAGS.do_train、FLAGS.do_eval和FLAGS.do_predict階段被呼叫。


這三個函式的內容是相差無幾的,區別只在於需要指定各自讀入檔案的地址。


以get_train_examples為例,函式需要返回一個由InputExample類組成的list。InputExample類是一個很簡單的類,只有初始化函式,需要傳入的引數中guid是用來區分每個example的,可以按照train-%d'%(i)的方式進行定義。text_a是一串字串,text_b則是另一串字串。在進行後續輸入處理後(BERT程式碼中已包含,不需要自己完成) text_a和text_b將組合成[CLS] text_a [SEP] text_b [SEP]的形式傳入模型。最後一個引數label也是字串的形式,label的內容需要保證出現在get_labels函式返回的list裡。


舉一個例子,假設我們想要處理一個能夠判斷句子相似度的模型,現在在data_dir的路徑下有一個名為train.csv的輸入檔案,如果我們現在輸入檔案的格式如下csv形式:



  

1,你好,您好
0,你好,你家住哪 


那麼我們可以寫一個如下的get_train_examples的函式。當然對於csv的處理,可以使用諸如csv.reader的形式進行讀入。



  

def get_train_examples(self, data_dir):
    file_path = os.path.join(data_dir, 'train.csv')
    with open(file_path, 'r'as f:
        reader = f.readlines()
    examples = []
    for index, line in enumerate(reader):
        guid = 'train-%d'%index
        split_line = line.strip().split(',')
        text_a = tokenization.convert_to_unicode(split_line[1])
        text_b = tokenization.convert_to_unicode(split_line[2])
        label = split_line[0]
        examples.append(InputExample(guid=guid, text_a=text_a, 
                                     text_b=text_b, label=label))
    return examples


同時對應判斷句子相似度這個二分類任務,get_labels函式可以寫成如下的形式:



  

def get_labels(self):
    return ['0','1']


在對get_dev_examples和get_test_examples函式做類似get_train_examples的操作後,便完成了對processor的修改。其中get_test_examples可以傳入一個隨意的label數值,因為在模型的預測(prediction)中label將不會參與計算。


修改 processor 字典


修改完成processor後,需要在在原本main函式的processor字典裡,加入修改後的processor類,即可在執行引數裡指定呼叫該processor。



  

processors = {
      "cola": ColaProcessor,
      "mnli": MnliProcessor,
      "mrpc": MrpcProcessor,
      "xnli": XnliProcessor, 
      "selfsim": SelfProcessor #新增自己的processor
  }


執行 fine-tune


之後就可以直接執行run_classsifier.py進行模型的訓練。在執行時需要制定一些引數,一個較為完整的執行引數如下所示:



  

export BERT_BASE_DIR=/path/to/bert/chinese_L-12_H-768_A-12 #全域性變數 下載的預訓練bert地址
export MY_DATASET=/path/to/xnli #全域性變數 資料集所在地址

python run_classifier.py \
  --task_name=selfsim \ #自己新增processor在processors字典裡的key名
  --do_train=true \
  --do_eval=true \
  --dopredict=true \
  --data_dir=$MY_DATASET \
  --vocab_file=$BERT_BASE_DIR/vocab.txt \
  --bert_config_file=$BERT_BASE_DIR/bert_config.json \
  --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \
  --max_seq_length=128 \ #模型引數
  --train_batch_size=32 \
  --learning_rate=5e-5 \
  --num_train_epochs=2.0 \
  --output_dir=/tmp/selfsim_output/ #模型輸出路徑


BERT 原始碼裡還有什麼


在開始訓練我們自己fine-tune的BERT後,我們可以再來看看BERT程式碼裡除了processor之外的一些部分。


我們可以發現,process在得到字串形式的輸入後,在file_based_convert_examples_to_features裡先是對字串長度,加入[CLS]和[SEP]等一些處理後,將其寫入成TFrecord的形式。這是為了能在estimator裡有一個更為高效和簡易的讀入。


我們還可以發現,在create_model的函式裡,除了從modeling.py獲取模型主幹輸出之外,還有進行fine-tune時候的loss計算。因此,如果對於fine-tune的結構有自定義的要求,可以在這部分對程式碼進行修改。如進行NER任務的時候,可以按照BERT論文裡的方式,不只讀第一位的logits,而是將每一位logits進行讀取。


BERT這次開源的程式碼,由於是考慮在google自己的TPU上高效地執行,因此採用的estimator是tf.contrib.tpu.TPUEstimator,雖然TPU的estimator同樣可以在gpu和cpu上執行,但若想在gpu上更高效地做一些提升,可以考慮將其換成tf.estimator.Estimator,於此同時model_fn裡一些tf.contrib.tpu.TPUEstimatorSpec也需要修改成tf.estimator.EstimatorSpec的形式,以及相關呼叫引數也需要做一些調整。在轉換成較普通的estimator後便可以使用常用的方式對estimator進行處理,如生成用於部署的.pb檔案等。


GitHub Issues 裡一些有趣的內容


從google對BERT進行開源開始,Issues裡的討論便異常活躍,BERT論文第一作者Jacob Devlin也積極地在Issues裡進行迴應,在交流討論中,產生了一些很有趣的內容。


在GitHub Issues#95 (https://github.com/google-research/bert/issues/95) 中大家討論了BERT模型在今年AI-Challenger比賽上的應用。我們也同樣嘗試了BERT在AI-Challenger的機器閱讀理解(mrc)賽道的表現。如果簡單得地將mrc的文字連線成一個長字串的形式,可以在dev集上得到79.1%的準確率。


如果參考openAI的GPT論文(https://s3-us-west-2.amazonaws.com/openai-assets/research-covers/language-unsupervised/language_understanding_paper.pdf)裡multi-choice的形式對BERT的輸入輸出程式碼進行修改則可以將準確率提高到79.3%。採用的引數都是BERT預設的引數,而單一模型成績在賽道的test a排名中已經能超過榜單上的第一名。因此,在相關中文的任務中,bert能有很大的想象空間。


在GitHub Issues#123(https://github.com/google-research/bert/issues/123)中,@hanxiao(https://github.com/hanxiao)給出了一個採用ZeroMQ便捷部署BERT的service,可以直接呼叫訓練好的模型作為應用的介面。同時他將BERT改為一個大的encode模型,將文字通過BERT進行encode,來實現句子級的encode。此外,他對比了多GPU上的效能,發現bert在多GPU並行上的出色表現。


總結


總的來說,Google此次開源的BERT和其預訓練模型是非常有價值的,可探索和改進的內容也很多。相關資料集上已經出現了對BERT進行修改後的複合模型,如squad2.0上哈工大(HIT)的AoA + DA + BERT以及西湖大學(DAMO)的SLQA + BERT。 在感謝google這份付出的同時,我們也可以藉此站在巨人的肩膀上,嘗試將其運用在自然語言處理領域的方方面面,讓人工智慧的夢想更近一步。


原文地址:https://www.jianshu.com/p/aa2eff7ec5c1



BDTC 2018

精彩紛呈



2018 年12月6-8 日,由中國計算機學會主辦,CCF 大資料專家委員會承辦,CSDN、中科天璣資料科技股份有限公司協辦的 2018 中國大資料技術大會(BDTC 2018),將在北京新雲南皇冠假日酒店隆重舉行。


除 Keynote 外,主辦方精心策劃了 13 場專題技術和行業論壇,涵蓋大資料分析與生態系統、深度學習、推薦系統、大資料安全與政策、大資料可視分析、精準醫療大資料、資料科學與大資料技術教育、資料庫、金融大資料、知識圖譜、工業大資料、區塊鏈、交通與旅遊大資料等主題。


屆時,近百位技術專家及行業領袖將齊聚於此,聚焦大資料技術如何促進數字經濟迅速發展,關注大資料新應用,思辨通達,深入解析熱門技術在行業中的實踐和落地。緊貼時代脈搏,走近資料前沿。

       640?wx_fmt=jpeg


推薦閱讀

ImageNet時代將終結?何愷明新作:Rethinking ImageNet Pre-training

“萬”字諫言,給那些想學Python的人,建議收藏後細看!

2W臺伺服器、每秒數億請求,微信如何不“失控”?

Istio,下一個Kubernetes?

炸了!剛寫完這段程式碼,就被開除了…

中國可以沒有俞敏洪, 區塊鏈不能沒有這些女王們, 女性從業者現狀調查