從11月初開(kāi)始,Google Research就陸續(xù)開(kāi)源了BERT的各個(gè)版本。Google此次開(kāi)源的BERT是通過(guò)TensorFlow高級(jí)API—— tf.estimator進(jìn)行封裝(wrapper)的。因此對(duì)于不同數(shù)據(jù)集的適配,只需要修改代碼中的processor部分,就能進(jìn)行代碼的訓(xùn)練、交叉驗(yàn)證和測(cè)試。
奇點(diǎn)機(jī)智技術(shù)團(tuán)隊(duì)將結(jié)合利用BERT在AI-Challenger機(jī)器閱讀理解賽道的實(shí)踐表現(xiàn)以及多年的NLP經(jīng)驗(yàn)積累,為大家奉上BERT在中文數(shù)據(jù)集上的fine tune全攻略。
在自己的數(shù)據(jù)集上運(yùn)行 BERT
BERT的代碼同論文里描述的一致,主要分為兩個(gè)部分。一個(gè)是訓(xùn)練語(yǔ)言模型(language model)的預(yù)訓(xùn)練(pretrain)部分。另一個(gè)是訓(xùn)練具體任務(wù)(task)的fine-tune部分。在開(kāi)源的代碼中,預(yù)訓(xùn)練的入口是在run_pretraining.py而fine-tune的入口針對(duì)不同的任務(wù)分別在run_classifier.py和run_squad.py。其中run_classifier.py適用的任務(wù)為分類(lèi)任務(wù)。如CoLA、MRPC、MultiNLI這些數(shù)據(jù)集。而run_squad.py適用的是閱讀理解(MRC)任務(wù),如squad2.0和squad1.1。
預(yù)訓(xùn)練是BERT很重要的一個(gè)部分,與此同時(shí),預(yù)訓(xùn)練需要巨大的運(yùn)算資源。按照論文里描述的參數(shù),其Base的設(shè)定在消費(fèi)級(jí)的顯卡Titan x 或Titan 1080ti(12GB RAM)上,甚至需要近幾個(gè)月的時(shí)間進(jìn)行預(yù)訓(xùn)練,同時(shí)還會(huì)面臨顯存不足的問(wèn)題。
不過(guò)所幸的是谷歌滿(mǎn)足了Issues#2(https://github.com/google-research/bert/issues/2)里各國(guó)開(kāi)發(fā)者的請(qǐng)求,針對(duì)大部分語(yǔ)言都公布了BERT的預(yù)訓(xùn)練模型。因此在我們可以比較方便地在自己的數(shù)據(jù)集上進(jìn)行fine-tune。
下載預(yù)訓(xùn)練模型
對(duì)于中文而言,google公布了一個(gè)參數(shù)較小的BERT預(yù)訓(xùn)練模型。具體參數(shù)數(shù)值如下所示:
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)
對(duì)下載的壓縮文件進(jìn)行解壓,可以看到文件里有五個(gè)文件,其中bert_model.ckpt開(kāi)頭的文件是負(fù)責(zé)模型變量載入的,而vocab.txt是訓(xùn)練時(shí)中文文本采用的字典,最后bert_config.json是BERT在訓(xùn)練時(shí),可選調(diào)整的一些參數(shù)。
修改 processor
任何模型的訓(xùn)練、預(yù)測(cè)都是需要有一個(gè)明確的輸入,而B(niǎo)ERT代碼中processor就是負(fù)責(zé)對(duì)模型的輸入進(jìn)行處理。我們以分類(lèi)任務(wù)的為例,介紹如何修改processor來(lái)運(yùn)行自己數(shù)據(jù)集上的fine-tune。在run_classsifier.py文件中我們可以看到,google對(duì)于一些公開(kāi)數(shù)據(jù)集已經(jīng)寫(xiě)了一些processor,如XnliProcessor,MnliProcessor,MrpcProcessor和ColaProcessor。這給我們提供了一個(gè)很好的示例,指導(dǎo)我們?nèi)绾吾槍?duì)自己的數(shù)據(jù)集來(lái)寫(xiě)processor。
對(duì)于一個(gè)需要執(zhí)行訓(xùn)練、交叉驗(yàn)證和測(cè)試完整過(guò)程的模型而言,自定義的processor里需要繼承DataProcessor,并重載獲取label的get_labels和獲取單個(gè)輸入的get_train_examples,get_dev_examples和get_test_examples函數(shù)。其分別會(huì)在main函數(shù)的FLAGS.do_train、FLAGS.do_eval和FLAGS.do_predict階段被調(diào)用。
這三個(gè)函數(shù)的內(nèi)容是相差無(wú)幾的,區(qū)別只在于需要指定各自讀入文件的地址。
以get_train_examples為例,函數(shù)需要返回一個(gè)由InputExample類(lèi)組成的list。InputExample類(lèi)是一個(gè)很簡(jiǎn)單的類(lèi),只有初始化函數(shù),需要傳入的參數(shù)中g(shù)uid是用來(lái)區(qū)分每個(gè)example的,可以按照train-%d'%(i)的方式進(jìn)行定義。text_a是一串字符串,text_b則是另一串字符串。在進(jìn)行后續(xù)輸入處理后(BERT代碼中已包含,不需要自己完成) text_a和text_b將組合成[CLS] text_a [SEP] text_b [SEP]的形式傳入模型。最后一個(gè)參數(shù)label也是字符串的形式,label的內(nèi)容需要保證出現(xiàn)在get_labels函數(shù)返回的list里。
舉一個(gè)例子,假設(shè)我們想要處理一個(gè)能夠判斷句子相似度的模型,現(xiàn)在在data_dir的路徑下有一個(gè)名為train.csv的輸入文件,如果我們現(xiàn)在輸入文件的格式如下csv形式:
1,你好,您好0,你好,你家住哪
那么我們可以寫(xiě)一個(gè)如下的get_train_examples的函數(shù)。當(dāng)然對(duì)于csv的處理,可以使用諸如csv.reader的形式進(jìn)行讀入。
defget_train_examples(self,data_dir):file_path=os.path.join(data_dir,'train.csv')withopen(file_path,'r')asf:reader=f.readlines()examples=[]forindex,lineinenumerate(reader):guid='train-%d'%indexsplit_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))returnexamples
同時(shí)對(duì)應(yīng)判斷句子相似度這個(gè)二分類(lèi)任務(wù),get_labels函數(shù)可以寫(xiě)成如下的形式:
defget_labels(self):return['0','1']
在對(duì)get_dev_examples和get_test_examples函數(shù)做類(lèi)似get_train_examples的操作后,便完成了對(duì)processor的修改。其中g(shù)et_test_examples可以傳入一個(gè)隨意的label數(shù)值,因?yàn)樵谀P偷念A(yù)測(cè)(prediction)中l(wèi)abel將不會(huì)參與計(jì)算。
修改 processor 字典
修改完成processor后,需要在在原本main函數(shù)的processor字典里,加入修改后的processor類(lèi),即可在運(yùn)行參數(shù)里指定調(diào)用該processor。
processors={"cola":ColaProcessor,"mnli":MnliProcessor,"mrpc":MrpcProcessor,"xnli":XnliProcessor,"selfsim":SelfProcessor#添加自己的processor}
運(yùn)行 fine-tune
之后就可以直接運(yùn)行run_classsifier.py進(jìn)行模型的訓(xùn)練。在運(yùn)行時(shí)需要制定一些參數(shù),一個(gè)較為完整的運(yùn)行參數(shù)如下所示:
exportBERT_BASE_DIR=/path/to/bert/chinese_L-12_H-768_A-12#全局變量下載的預(yù)訓(xùn)練bert地址exportMY_DATASET=/path/to/xnli#全局變量數(shù)據(jù)集所在地址pythonrun_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#模型參數(shù)--train_batch_size=32--learning_rate=5e-5--num_train_epochs=2.0--output_dir=/tmp/selfsim_output/#模型輸出路徑
BERT 源代碼里還有什么
在開(kāi)始訓(xùn)練我們自己fine-tune的BERT后,我們可以再來(lái)看看BERT代碼里除了processor之外的一些部分。
我們可以發(fā)現(xiàn),process在得到字符串形式的輸入后,在file_based_convert_examples_to_features里先是對(duì)字符串長(zhǎng)度,加入[CLS]和[SEP]等一些處理后,將其寫(xiě)入成TFrecord的形式。這是為了能在estimator里有一個(gè)更為高效和簡(jiǎn)易的讀入。
我們還可以發(fā)現(xiàn),在create_model的函數(shù)里,除了從modeling.py獲取模型主干輸出之外,還有進(jìn)行fine-tune時(shí)候的loss計(jì)算。因此,如果對(duì)于fine-tune的結(jié)構(gòu)有自定義的要求,可以在這部分對(duì)代碼進(jìn)行修改。如進(jìn)行NER任務(wù)的時(shí)候,可以按照BERT論文里的方式,不只讀第一位的logits,而是將每一位logits進(jìn)行讀取。
BERT這次開(kāi)源的代碼,由于是考慮在google自己的TPU上高效地運(yùn)行,因此采用的estimator是tf.contrib.tpu.TPUEstimator,雖然TPU的estimator同樣可以在gpu和cpu上運(yùn)行,但若想在gpu上更高效地做一些提升,可以考慮將其換成tf.estimator.Estimator,于此同時(shí)model_fn里一些tf.contrib.tpu.TPUEstimatorSpec也需要修改成tf.estimator.EstimatorSpec的形式,以及相關(guān)調(diào)用參數(shù)也需要做一些調(diào)整。在轉(zhuǎn)換成較普通的estimator后便可以使用常用的方式對(duì)estimator進(jìn)行處理,如生成用于部署的.pb文件等。
GitHub Issues 里一些有趣的內(nèi)容
從google對(duì)BERT進(jìn)行開(kāi)源開(kāi)始,Issues里的討論便異常活躍,BERT論文第一作者Jacob Devlin也積極地在Issues里進(jìn)行回應(yīng),在交流討論中,產(chǎn)生了一些很有趣的內(nèi)容。
在GitHub Issues#95 (https://github.com/google-research/bert/issues/95) 中大家討論了BERT模型在今年AI-Challenger比賽上的應(yīng)用。我們也同樣嘗試了BERT在AI-Challenger的機(jī)器閱讀理解(mrc)賽道的表現(xiàn)。如果簡(jiǎn)單得地將mrc的文本連接成一個(gè)長(zhǎng)字符串的形式,可以在dev集上得到79.1%的準(zhǔn)確率。
如果參考o(jì)penAI的GPT論文(https://s3-us-west-2.amazonaws.com/openai-assets/research-covers/language-unsupervised/language_understanding_paper.pdf)里multi-choice的形式對(duì)BERT的輸入輸出代碼進(jìn)行修改則可以將準(zhǔn)確率提高到79.3%。采用的參數(shù)都是BERT默認(rèn)的參數(shù),而單一模型成績(jī)?cè)谫惖赖膖est a排名中已經(jīng)能超過(guò)榜單上的第一名。因此,在相關(guān)中文的任務(wù)中,bert能有很大的想象空間。
在GitHubIssues#123(https://github.com/google-research/bert/issues/123)中,@hanxiao(https://github.com/hanxiao)給出了一個(gè)采用ZeroMQ便捷部署B(yǎng)ERT的service,可以直接調(diào)用訓(xùn)練好的模型作為應(yīng)用的接口。同時(shí)他將BERT改為一個(gè)大的encode模型,將文本通過(guò)BERT進(jìn)行encode,來(lái)實(shí)現(xiàn)句子級(jí)的encode。此外,他對(duì)比了多GPU上的性能,發(fā)現(xiàn)bert在多GPU并行上的出色表現(xiàn)。
總結(jié)
總的來(lái)說(shuō),Google此次開(kāi)源的BERT和其預(yù)訓(xùn)練模型是非常有價(jià)值的,可探索和改進(jìn)的內(nèi)容也很多。相關(guān)數(shù)據(jù)集上已經(jīng)出現(xiàn)了對(duì)BERT進(jìn)行修改后的復(fù)合模型,如squad2.0上哈工大(HIT)的AoA + DA + BERT以及西湖大學(xué)(DAMO)的SLQA + BERT。 在感謝google這份付出的同時(shí),我們也可以借此站在巨人的肩膀上,嘗試將其運(yùn)用在自然語(yǔ)言處理領(lǐng)域的方方面面,讓人工智能的夢(mèng)想更近一步。
-
谷歌
+關(guān)注
關(guān)注
27文章
6161瀏覽量
105298 -
代碼
+關(guān)注
關(guān)注
30文章
4779瀏覽量
68521 -
tensorflow
+關(guān)注
關(guān)注
13文章
329瀏覽量
60527
原文標(biāo)題:干貨 | 谷歌BERT模型fine-tune終極實(shí)踐教程
文章出處:【微信號(hào):rgznai100,微信公眾號(hào):rgznai100】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論