網(wǎng)站建設 靜態(tài)類北京網(wǎng)站優(yōu)化推廣方案
對于序列級和詞元級自然語言處理應用,BERT只需要最小的架構(gòu)改變(額外的全連接層),如單個文本分類(例如,情感分析和測試語言可接受性)、文本對分類或回歸(例如,自然語言推斷和語義文本相似性)、文本標記(例如,詞性標記)和問答。
在下游應用的監(jiān)督學習期間,額外層的參數(shù)是從零開始學習的,而預訓練BERT模型中的所有參數(shù)都是微調(diào)的。
我們可以針對下游應用對預訓練的BERT模型進行微調(diào),例如在SNLI數(shù)據(jù)集上進行自然語言推斷。
在微調(diào)過程中,BERT模型成為下游應用模型的一部分。僅與訓練前損失相關(guān)的參數(shù)在微調(diào)期間不會更新。
在來自Transformers的雙向編碼器表示(BERT)_流螢數(shù)點的博客-CSDN博客中,我們介紹了一個名為BERT的預訓練模型,該模型可以對廣泛的自然語言處理任務進行最少的架構(gòu)更改。一方面,在提出時,BERT改進了各種自然語言處理任務的技術(shù)水平。另一方面,原始BERT模型的兩個版本分別帶有1.1億和3.4億個參數(shù)。因此,當有足夠的計算資源時,我們可以考慮為下游自然語言處理應用微調(diào)BERT。
下面,我們將自然語言處理應用的子集概括為序列級和詞元級。在序列層次上,介紹了在單文本分類任務和文本對分類(或回歸)任務中,如何將文本輸入的BERT表示轉(zhuǎn)換為輸出標簽。在詞元級別,我們將簡要介紹新的應用,如文本標注和問答,并說明BERT如何表示它們的輸入并轉(zhuǎn)換為輸出標簽。在微調(diào)期間,不同應用之間的BERT所需的“最小架構(gòu)更改”是額外的全連接層。在下游應用的監(jiān)督學習期間,額外層的參數(shù)是從零開始學習的,而預訓練BERT模型中的所有參數(shù)都是微調(diào)的。
1.單文本分類
單文本分類將單個文本序列作為輸入,并輸出其分類結(jié)果。語言可接受性語料庫(Corpus of Linguistic Acceptability,COLA)是一個單文本分類的數(shù)據(jù)集,它的要求判斷給定的句子在語法上是否可以接受。?(Warstadt?et al., 2019)。例如,“I should study.”是可以接受的,但是“I should studying.”不是可以接受的。
來自Transformers的雙向編碼器表示(BERT)_流螢數(shù)點的博客-CSDN博客描述了BERT的輸入表示。BERT輸入序列明確地表示單個文本和文本對,其中特殊分類標記“<cls>”用于序列分類,而特殊分類標記“<sep>”標記單個文本的結(jié)束或分隔成對文本。如?圖15.6.1所示,在單文本分類應用中,特殊分類標記“<cls>”的BERT表示對整個輸入文本序列的信息進行編碼。作為輸入單個文本的表示,它將被送入到由全連接(稠密)層組成的小多層感知機中,以輸出所有離散標簽值的分布。
2.文本對分類或回歸
自然語言推斷屬于文本對分類,這是一種對文本進行分類的應用類型。
自然語言推斷(natural language inference)主要研究?假設(hypothesis)是否可以從前提(premise)中推斷出來, 其中兩者都是文本序列。 換言之,自然語言推斷決定了一對文本序列之間的邏輯關(guān)系。這類關(guān)系通常分為三種類型:
蘊涵(entailment):假設可以從前提中推斷出來。
矛盾(contradiction):假設的否定可以從前提中推斷出來。
中性(neutral):所有其他情況。
自然語言推斷也被稱為識別文本蘊涵任務。 例如,下面的一個文本對將被貼上“蘊涵”的標簽,因為假設中的“表白”可以從前提中的“擁抱”中推斷出來。
前提:兩個女人擁抱在一起。
假設:兩個女人在示愛。
下面是一個“矛盾”的例子,因為“運行編碼示例”表示“不睡覺”,而不是“睡覺”。
前提:一名男子正在運行Dive Into Deep Learning的編碼示例。
假設:該男子正在睡覺。
第三個例子顯示了一種“中性”關(guān)系,因為“正在為我們表演”這一事實無法推斷出“出名”或“不出名”。
前提:音樂家們正在為我們表演。
假設:音樂家很有名。
自然語言推斷一直是理解自然語言的中心話題。它有著廣泛的應用,從信息檢索到開放領(lǐng)域的問答。
以一對文本作為輸入但輸出連續(xù)值,語義文本相似度是一個流行的“文本對回歸”任務。 這項任務評估句子的語義相似度。例如,在語義文本相似度基準數(shù)據(jù)集(Semantic Textual Similarity Benchmark)中,句子對的相似度得分是從0(無語義重疊)到5(語義等價)的分數(shù)區(qū)間?(Cer?et al., 2017)。我們的目標是預測這些分數(shù)。來自語義文本相似性基準數(shù)據(jù)集的樣本包括(句子1,句子2,相似性得分):
-
“A plane is taking off.”(“一架飛機正在起飛。”),”An air plane is taking off.”(“一架飛機正在起飛?!?#xff09;,5.000分;
-
“A woman is eating something.”(“一個女人在吃東西?!?#xff09;,”A woman is eating meat.”(“一個女人在吃肉?!?#xff09;,3.000分;
-
“A woman is dancing.”(一個女人在跳舞。),”A man is talking.”(“一個人在說話?!?#xff09;,0.000分。
與?圖15.6.1中的單文本分類相比,?圖15.6.2中的文本對分類的BERT微調(diào)在輸入表示上有所不同。對于文本對回歸任務(如語義文本相似性),可以應用細微的更改,例如輸出連續(xù)的標簽值和使用均方損失:它們在回歸中很常見。
3.文本標注?
現(xiàn)在讓我們考慮詞元級任務,比如文本標注(text tagging),其中每個詞元都被分配了一個標簽。在文本標注任務中,詞性標注為每個單詞分配詞性標記(例如,形容詞和限定詞)。 根據(jù)單詞在句子中的作用。如,在Penn樹庫II標注集中,句子“John Smith‘s car is new”應該被標記為“NNP(名詞,專有單數(shù))NNP POS(所有格結(jié)尾)NN(名詞,單數(shù)或質(zhì)量)VB(動詞,基本形式)JJ(形容詞)”。
?圖15.6.3中說明了文本標記應用的BERT微調(diào)。與?圖15.6.1相比,唯一的區(qū)別在于,在文本標注中,輸入文本的每個詞元的BERT表示被送到相同的額外全連接層中,以輸出詞元的標簽,例如詞性標簽。
4.問答
作為另一個詞元級應用,問答反映閱讀理解能力。 例如,斯坦福問答數(shù)據(jù)集(Stanford Question Answering Dataset,SQuAD v1.1)由閱讀段落和問題組成,其中每個問題的答案只是段落中的一段文本(文本片段)?(Rajpurkar?et al., 2016)。舉個例子,考慮一段話:“Some experts report that a mask’s efficacy is inconclusive.However,mask makers insist that their products,such as N95 respirator masks,can guard against the virus.”(“一些專家報告說面罩的功效是不確定的。然而,口罩制造商堅持他們的產(chǎn)品,如N95口罩,可以預防病毒?!?#xff09;還有一個問題“Who say that N95 respirator masks can guard against the virus?”(“誰說N95口罩可以預防病毒?”)。答案應該是文章中的文本片段“mask makers”(“口罩制造商”)。因此,SQuAD v1.1的目標是在給定問題和段落的情況下預測段落中文本片段的開始和結(jié)束。
為了微調(diào)BERT進行問答,在BERT的輸入中,將問題和段落分別作為第一個和第二個文本序列。為了預測文本片段開始的位置,相同的額外的全連接層將把來自位置i的任何詞元的BERT表示轉(zhuǎn)換成標量分數(shù)。文章中所有詞元的分數(shù)還通過softmax轉(zhuǎn)換成概率分布,從而為文章中的每個詞元位置i分配作為文本片段開始的概率
。預測文本片段的結(jié)束與上面相同,只是其額外的全連接層中的參數(shù)與用于預測開始位置的參數(shù)無關(guān)。當預測結(jié)束時,位置i的詞元由相同的全連接層變換成標量分數(shù)ei。?圖15.6.4描述了用于問答的微調(diào)BERT。?
對于問答,監(jiān)督學習的訓練目標就像最大化真實值的開始和結(jié)束位置的對數(shù)似然一樣簡單。當預測片段時,我們可以計算從位置i到位置j的有效片段的分數(shù)+
(i≤j),并輸出分數(shù)最高的跨度。
5.自然語言推斷:微調(diào)BERT
現(xiàn)在,我們通過微調(diào)BERT來解決SNLI數(shù)據(jù)集上的自然語言推斷任務。斯坦福自然語言推斷語料庫(Stanford Natural Language Inference,SNLI)是由500000多個帶標簽的英語句子對組成的集合?(Bowman?et al., 2015)。我們可以在路徑https://nlp.stanford.edu/projects/snli/snli_1.0.zip中下載并存儲提取的SNLI數(shù)據(jù)集。自然語言推斷是一個序列級別的文本對分類問題,而微調(diào)BERT只需要一個額外的基于多層感知機的架構(gòu),如?圖15.7.1中所示。
本節(jié)將下載一個預訓練好的小版本的BERT,然后對其進行微調(diào),以便在SNLI數(shù)據(jù)集上進行自然語言推斷。
pip install mxnet==1.7.0.post1
pip install d2l==0.17.0
import json
import multiprocessing
import os
from mxnet import gluon, np, npx
from mxnet.gluon import nn
from d2l import mxnet as d2lnpx.set_np()
5.1加載預訓練的BERT
我們已經(jīng)在?預訓練BERT_流螢數(shù)點的博客-CSDN博客WikiText-2數(shù)據(jù)集上預訓練BERT(請注意,原始的BERT模型是在更大的語料庫上預訓練的)。正如其中所討論的,原始的BERT模型有數(shù)以億計的參數(shù)。在下面,我們提供了兩個版本的預訓練的BERT:“bert.base”與原始的BERT基礎(chǔ)模型一樣大,需要大量的計算資源才能進行微調(diào),而“bert.small”是一個小版本,以便于演示。
d2l.DATA_HUB['bert.base'] = (d2l.DATA_URL + 'bert.base.zip','7b3820b35da691042e5d34c0971ac3edbd80d3f4')
d2l.DATA_HUB['bert.small'] = (d2l.DATA_URL + 'bert.small.zip','a4e718a47137ccd1809c9107ab4f5edd317bae2c')
兩個預訓練好的BERT模型都包含一個定義詞表的“vocab.json”文件和一個預訓練參數(shù)的“pretrained.params”文件。我們實現(xiàn)了以下load_pretrained_model
函數(shù)來加載預先訓練好的BERT參數(shù)。
def load_pretrained_model(pretrained_model, num_hiddens, ffn_num_hiddens,num_heads, num_layers, dropout, max_len, devices):data_dir = d2l.download_extract(pretrained_model)# 定義空詞表以加載預定義詞表vocab = d2l.Vocab()vocab.idx_to_token = json.load(open(os.path.join(data_dir,'vocab.json')))vocab.token_to_idx = {token: idx for idx, token in enumerate(vocab.idx_to_token)}bert = d2l.BERTModel(len(vocab), num_hiddens, ffn_num_hiddens,num_heads, num_layers, dropout, max_len)# 加載預訓練BERT參數(shù)bert.load_parameters(os.path.join(data_dir, 'pretrained.params'),ctx=devices)return bert, vocab
為了便于在大多數(shù)機器上演示,我們將在本節(jié)中加載和微調(diào)經(jīng)過預訓練BERT的小版本(“bert.small”)。在練習中,我們將展示如何微調(diào)大得多的“bert.base”以顯著提高測試精度。
devices = d2l.try_all_gpus()
bert, vocab = load_pretrained_model('bert.small', num_hiddens=256, ffn_num_hiddens=512, num_heads=4,num_layers=2, dropout=0.1, max_len=512, devices=devices)
Downloading ../data/bert.small.zip from http://d2l-data.s3-accelerate.amazonaws.com/bert.small.zip...
5.2微調(diào)BERT的數(shù)據(jù)集
對于SNLI數(shù)據(jù)集的下游任務自然語言推斷,我們定義了一個定制的數(shù)據(jù)集類SNLIBERTDataset
。在每個樣本中,前提和假設形成一對文本序列,并被打包成一個BERT輸入序列,如?圖15.6.2所示?;叵雭碜訲ransformers的雙向編碼器表示(BERT)_流螢數(shù)點的博客-CSDN博客,片段索引用于區(qū)分BERT輸入序列中的前提和假設。利用預定義的BERT輸入序列的最大長度(max_len
),持續(xù)移除輸入文本對中較長文本的最后一個標記,直到滿足max_len
。為了加速生成用于微調(diào)BERT的SNLI數(shù)據(jù)集,我們使用4個工作進程并行生成訓練或測試樣本。
class SNLIBERTDataset(gluon.data.Dataset):def __init__(self, dataset, max_len, vocab=None):all_premise_hypothesis_tokens = [[p_tokens, h_tokens] for p_tokens, h_tokens in zip(*[d2l.tokenize([s.lower() for s in sentences])for sentences in dataset[:2]])]self.labels = np.array(dataset[2])self.vocab = vocabself.max_len = max_len(self.all_token_ids, self.all_segments,self.valid_lens) = self._preprocess(all_premise_hypothesis_tokens)print('read ' + str(len(self.all_token_ids)) + ' examples')def _preprocess(self, all_premise_hypothesis_tokens):pool = multiprocessing.Pool(4) # 使用4個進程out = pool.map(self._mp_worker, all_premise_hypothesis_tokens)all_token_ids = [token_ids for token_ids, segments, valid_len in out]all_segments = [segments for token_ids, segments, valid_len in out]valid_lens = [valid_len for token_ids, segments, valid_len in out]return (np.array(all_token_ids, dtype='int32'),np.array(all_segments, dtype='int32'),np.array(valid_lens))def _mp_worker(self, premise_hypothesis_tokens):p_tokens, h_tokens = premise_hypothesis_tokensself._truncate_pair_of_tokens(p_tokens, h_tokens)tokens, segments = d2l.get_tokens_and_segments(p_tokens, h_tokens)token_ids = self.vocab[tokens] + [self.vocab['<pad>']] \* (self.max_len - len(tokens))segments = segments + [0] * (self.max_len - len(segments))valid_len = len(tokens)return token_ids, segments, valid_lendef _truncate_pair_of_tokens(self, p_tokens, h_tokens):# 為BERT輸入中的'<CLS>'、'<SEP>'和'<SEP>'詞元保留位置while len(p_tokens) + len(h_tokens) > self.max_len - 3:if len(p_tokens) > len(h_tokens):p_tokens.pop()else:h_tokens.pop()def __getitem__(self, idx):return (self.all_token_ids[idx], self.all_segments[idx],self.valid_lens[idx]), self.labels[idx]def __len__(self):return len(self.all_token_ids)
下載完SNLI數(shù)據(jù)集后,我們通過實例化SNLIBERTDataset
類來生成訓練和測試樣本。這些樣本將在自然語言推斷的訓練和測試期間進行小批量讀取。
# 如果出現(xiàn)顯存不足錯誤,請減少“batch_size”。在原始的BERT模型中,max_len=512
batch_size, max_len, num_workers = 512, 128, d2l.get_dataloader_workers()
data_dir = d2l.download_extract('SNLI')
train_set = SNLIBERTDataset(d2l.read_snli(data_dir, True), max_len, vocab)
test_set = SNLIBERTDataset(d2l.read_snli(data_dir, False), max_len, vocab)
train_iter = gluon.data.DataLoader(train_set, batch_size, shuffle=True,num_workers=num_workers)
test_iter = gluon.data.DataLoader(test_set, batch_size,num_workers=num_workers)
Downloading ../data/snli_1.0.zip from https://nlp.stanford.edu/projects/snli/snli_1.0.zip... read 549367 examples read 9824 examples
5.3微調(diào)BERT
如?圖15.6.2所示,用于自然語言推斷的微調(diào)BERT只需要一個額外的多層感知機,該多層感知機由兩個全連接層組成(請參見下面BERTClassifier
類中的self.hidden
和self.output
)。這個多層感知機將特殊的“<cls>”詞元的BERT表示進行了轉(zhuǎn)換,該詞元同時編碼前提和假設的信息為自然語言推斷的三個輸出:蘊涵、矛盾和中性。
class BERTClassifier(nn.Block):def __init__(self, bert):super(BERTClassifier, self).__init__()self.encoder = bert.encoderself.hidden = bert.hiddenself.output = nn.Dense(3)def forward(self, inputs):tokens_X, segments_X, valid_lens_x = inputsencoded_X = self.encoder(tokens_X, segments_X, valid_lens_x)return self.output(self.hidden(encoded_X[:, 0, :]))
在下文中,預訓練的BERT模型bert
被送到用于下游應用的BERTClassifier
實例net
中。在BERT微調(diào)的常見實現(xiàn)中,只有額外的多層感知機(net.output
)的輸出層的參數(shù)將從零開始學習。預訓練BERT編碼器(net.encoder
)和額外的多層感知機的隱藏層(net.hidden
)的所有參數(shù)都將進行微調(diào)。
net = BERTClassifier(bert)
net.output.initialize(ctx=devices)
回想一下,在?來自Transformers的雙向編碼器表示(BERT)_流螢數(shù)點的博客-CSDN博客中,MaskLM
類和NextSentencePred
類在其使用的多層感知機中都有一些參數(shù)。這些參數(shù)是預訓練BERT模型bert
中參數(shù)的一部分,因此是net
中的參數(shù)的一部分。然而,這些參數(shù)僅用于計算預訓練過程中的遮蔽語言模型損失和下一句預測損失。這兩個損失函數(shù)與微調(diào)下游應用無關(guān),因此當BERT微調(diào)時,MaskLM
和NextSentencePred
中采用的多層感知機的參數(shù)不會更新(陳舊的,staled)。
為了允許具有陳舊梯度的參數(shù),標志ignore_stale_grad=True
在step
函數(shù)d2l.train_batch_ch13
中被設置。我們通過該函數(shù)使用SNLI的訓練集(train_iter
)和測試集(test_iter
)對net
模型進行訓練和評估。由于計算資源有限,訓練和測試精度可以進一步提高:我們把對它的討論留在練習中。
lr, num_epochs = 1e-4, 5
trainer = gluon.Trainer(net.collect_params(), 'adam', {'learning_rate': lr})
loss = gluon.loss.SoftmaxCrossEntropyLoss()
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,devices, d2l.split_batch_multi_inputs)
如果您的計算資源允許,請微調(diào)一個更大的預訓練BERT模型,該模型與原始的BERT基礎(chǔ)模型一樣大。修改load_pretrained_model
函數(shù)中的參數(shù)設置:將“bert.small”替換為“bert.base”,將num_hiddens=256
、ffn_num_hiddens=512
、num_heads=4
和num_layers=2
的值分別增加到768、3072、12和12。通過增加微調(diào)迭代輪數(shù)(可能還會調(diào)優(yōu)其他超參數(shù)),你可以獲得高于0.86的測試精度嗎?