深度學習告訴你,如何微調智能問答模型?

全文共3738字,預計學習時長27分鐘


深度學習告訴你,如何微調智能問答模型?

圖源:unsplash


Boolean智能問答似乎是個簡單的任務,實際上卻不然,它當前的基礎還遠未達到人類的表現水平。


本文將介紹如何使用 HuggingFace Transformers和PyTorch庫來微調“是/否”問答模型,並創建最新先進成果。


免責聲明:本文旨在為Boolean智能問答提供一個簡短易用的使用途徑。

深度學習告訴你,如何微調智能問答模型?

為何Boolean智能問答令人驚奇


如今,抽取式問答正是火熱,而“是/否”問答將失去問答的半壁江山卻被忽略了。回答封閉式問題的巨大的價值,以下行業中用例就可見一斑:


· 搜索引擎:知識庫查詢、會話代理……

· 信息自動提取:表單填寫、大型文檔解析……

· 語音用戶界面:智能助手、語音會話分析……


事實上,人們已經建立了出色的智能“是/否”問答。

深度學習告訴你,如何微調智能問答模型?

數據集:BoolQ


BoolQ 是由谷歌公司人工智能語言研究員建立的閱讀理解數據集。以下是數據集中的示例,每個示例包括一個問題、一個段落和一個為“是”或“否”的答案。


深度學習告訴你,如何微調智能問答模型?

數據收集途徑如下(本文給出了更加詳細的解釋):


· 問題來自Google搜索引擎的歷史查詢數據

· 在這些情況下,提供問題/文章對作為註釋。

· 註釋者在文章中找到回答問題的段落並標記答案

· 返回問題/段落/回答對

· 如果返回一篇維基百科文章,則將其保留


從該途徑中最終收集了13,000對,並從Natural Questions(自然問答)訓練集中收集了3000對。這些示例被分為9400個訓練集,3200個開發集和未發佈的3200個測試集。


如下所示,回答問題需要多種推理。


深度學習告訴你,如何微調智能問答模型?

推理類型


BoolQ團隊通過在MultiNLI(多語型自然語言推理)數據集上進行BERT-large的預訓練獲得了最佳結果。請注意,多數基線生成的準確性為62%,而註釋者的準確性達到90%(在110個交叉註釋的示例中)。


深度學習告訴你,如何微調智能問答模型?

BoolQ結果


這些結果展示了Transformer模型對語言理解產生的強大作用,但它仍存在改進空間。

深度學習告訴你,如何微調智能問答模型?

模型: RoBERTa


RoBERTa是強大的最優化BERT預訓練方式,由FacebookAI的研究人員發佈。簡言之,RoBERTa是在原始BERT架構之上添加的多項改進的集合。關鍵區別如下:


· 使用動態屏蔽而非靜態屏蔽以完成Masked(遮蔽)語言模型(MLM)

· 完全刪除NextSentence Prediction訓練目標

· 優化步驟在8000而不是256的小批量上執行

· 文本編碼是由BPE(字節對編碼)的實現處理的,它使用字節而非統一字符編碼作為構建塊

· 對更多數據(從16 GB到160 GB)進行預訓練以進行更多步驟(從100K到500K)


深度學習告訴你,如何微調智能問答模型?

向貝吉塔(Vegeta)詢問RoBERTa的批量


RoBERTa在所有9個GLUE任務中以及SQuAD排行榜上的表現均優於BERT。考慮到RoBERTa和BERT共享相同的MLM預訓練目標和體系建構,這令人印象十分深刻。


深度學習告訴你,如何微調智能問答模型?


深度學習告訴你,如何微調智能問答模型?

GLUE和SQuAD結果


據作者說:“與我們在這項工作中探索的更普通的細節(例如數據集大小和訓練時間)相比,這提出了關於模型架構和預訓練目標的相對重要性的問題”。


然而,ALBERT和ELECTRA是這一領域的最新成員,他們在GLUE和SQuAD排行榜中的門檻更高。

深度學習告訴你,如何微調智能問答模型?

實際操作“是/否”問答


現在我們已經瞭解了數據集和模型,開始操作問答吧!


任務說明:

擊敗BoolQ團隊獲得的開發設置結果。RoBERTa將成為我們的選擇武器。


安裝

訓練和開發設置可在此下載:
https://github.com/google-research-datasets/boolean-questions

至於開發環境,筆者建議使用Google Colab,因為它提供免費的GPU(圖形處理器)。


您將需要下載以下庫:


<code>

pip

install torch torchvision

pip

install transformers

pip

install pandas

pip

install numpy

/<code>


可以用以下指令下載數據:


<code>

gsutil

cp gs://boolq/train.jsonl. gsutil cp gs://boolq/dev.jsonl ./<code>


<code>

import

random

import

torch

import

numpy

as

np

import

pandas

as

pd

from

tqdm

import

tqdm

from

torch.utils.data importTensorDataset, DataLoader, RandomSampler, SequentialSampler

from

transformers importAutoTokenizer, AutoModelForSequenceClassification, AdamW/<code>

導入庫


模型加載


得益於Transformers庫,加載模型及其相關的tokenizer(令牌解析器)變得輕鬆。我們從RoBERTa-base(125M參數)開始。至於優化器,將使用Adam和BoolQ論文中推薦的學習率。


<code> 
        device = torch.device(

"cuda"

if

torch.cuda.is_available()

else

"cpu"

) random.seed(26) np.random.seed(26) torch.manual_seed(26) tokenizer =AutoTokenizer.from_pretrained(

"roberta-base"

) model =AutoModelForSequenceClassification.from_pretrained(

"roberta-base"

) model.to(device) learning_rate =1e-5 optimizer =AdamW(model.parameters(), lr=learning_rate,eps=1e-8)/<code>

模型加載


數據加載


首先,定義一個輔助函數來處理令牌化過程,encode_data函數將執行以下步驟:


· 將問題和段落分成令牌

· 添加語句起始令牌 和令牌表示問題和段落間的分隔,以及輸入的結尾

· 將令牌映射到其ID(標識符)中

· (用 令牌)填充或將每個問題/段落對縮短為max_seq_length

· 生成attentionmask,將相關令牌與填充令牌區分開


注意,max_seq_length必須小於512,這是類似BERT模型的標準輸入容量。


<code>defencode_data(tokenizer,questions, passages, max_length):
                         

"""Encode thequestion/passage pairs into features than can be fed to themodel."""

input_ids = [] attention_masks = []

for

question, passage inzip(questions,passages): encoded_data = tokenizer.encode_plus(question,passage, max_length=max_length, pad_to_max_length=

True

,truncation_strategy=

"longest_first"

) encoded_pair = encoded_data[

"input_ids"

] attention_mask = encoded_data[

"attention_mask"

] input_ids.append(encoded_pair) attention_masks.append(attention_mask)

return

np.array(input_ids), np.array(attention_masks) train_data_df = pd.read_json(

"/content/train.jsonl"

, lines=

True

, orient=

'records'

) dev_data_df = pd.read_json(

"/content/dev.jsonl"

, lines=

True

, orient=

"records"

) passages_train = train_data_df.passage.values questions_train = train_data_df.question.values answers_train =train_data_df.answer.values.astype(int) passages_dev =dev_data_df.passage.values questions_dev =dev_data_df.question.values answers_dev =dev_data_df.answer.values.astype(int) max_seq_length =

256

input_ids_train, attention_masks_train =encode_data(tokenizer, questions_train, passages_train, max_seq_length) input_ids_dev,attention_masks_dev =encode_data(tokenizer,questions_dev, passages_dev, max_seq_length) train_features =(input_ids_train, attention_masks_train, answers_train) dev_features = (input_ids_dev,attention_masks_dev, answers_dev)/<code>

數據加載


現在,數據已轉換為RoBERTa兼容特點,剩下的就是構建PyTorch Dataloader。


batch_size參數可被隨意使用,但請記住,批量越大佔用的GPU內存越多。


<code>

batch_size

=

32

train_features_tensors

=

[torch.tensor(feature, dtype=torch.long) for feature in train_features]

dev_features_tensors

=

[torch.tensor(feature, dtype=torch.long) for feature in dev_features]

train_dataset

=

TensorDataset(*train_features_tensors)

dev_dataset

=

TensorDataset(*dev_features_tensors)train_sampler =RandomSampler(train_dataset)

dev_sampler

=

SequentialSampler(dev_dataset)

train_dataloader

=

DataLoader(train_dataset,sampler=train_sampler, batch_size=batch_size)

dev_dataloader

=

DataLoader(dev_dataset,sampler=dev_sampler, batch_size=batch_size)

/<code>

建立PyTorch數據集


訓練和評估


該過程分為兩個階段,每個階段交替進行:


訓練:

· 抽取一批數據並將其加載到GPU中(如果有的話)

· 將其送入模型,該模型將返回batchloss(批量損失)

· 反向傳遞損失並截斷梯度,以避免梯度爆炸

· 執行優化步驟


深度學習告訴你,如何微調智能問答模型?

圖源:unsplash


評估:

· 抽取一批數據並將其加載到GPU中(如果有的話)

· 將其送入模型,該模型將返回batchlogit

· 使用logit進行預測

· 計算模型的準確性


<code>

epochs

=

5

grad_acc_steps

=

1

train_loss_values

=

[]

dev_acc_values

=

[]

for

_ intqdm(range(epochs), desc="Epoch"):

epoch_train_loss

=

0# Cumulativeloss

model.train()

model.zero_grad()

for

step, batch inenumerate(train_dataloader):

input_ids

=

batch[0].to(device)

attention_masks

=

batch[1].to(device)

labels

=

batch[2].to(device)

outputs

=

model(input_ids,token_type_ids=None, attention_mask=attention_masks, labels=labels) # loss, logits,...

loss

=

outputs[0]

loss

=

loss / grad_acc_steps

epoch_train_loss

+= loss.item()

loss.backward()

if

(step+1) % grad_acc_steps ==0: # Gradientaccumulation is over

1.0) # Clippinggradients

optimizer.step()

model.zero_grad()

epoch_train_loss

=

epoch_train_loss /len(train_dataloader)

train_loss_values.append(epoch_train_loss)

epoch_dev_accuracy

=

0# Cumulativeaccuracy

model.eval()

for

batch in dev_dataloader:

input_ids

=

batch[0].to(device)

attention_masks

=

batch[1].to(device)

labels

=

batch[2]

with

torch.no_grad(): # Do not keep track of computations during evaluation

outputs

=

model(input_ids,token_type_ids=None, attention_mask=attention_masks) # logits, ...

logits

=

outputs[0]

logits

=

logits.detach().cpu().numpy()

predictions

=

np.argmax(logits, axis=1).flatten()

labels

=

labels.numpy().flatten()

epoch_dev_accuracy

+= np.sum(predictions == labels) /len(labels)

epoch_dev_accuracy

=

epoch_dev_accuracy /len(dev_dataloader)

dev_acc_values.append(epoch_dev_accuracy)

/<code>

訓練和評估


<code>

import

seaborn

as

sns

import

matplotlib

.pyplot

as

plt

sns

.set

()

plt

.plot

(train_loss_values,label=

"train_loss"

)

plt

.xlabel

(

"Epoch"

)

plt

.ylabel

(

"Loss"

)

plt

.title

(

"TrainingLoss"

)

plt

.legend

()

plt

.xticks

(np.arange(

0

,

5

))

plt

.show

()

plt

.plot

(dev_acc_values,label=

"dev_acc"

)

plt

.xlabel

(

"Epoch"

)

plt

.ylabel

(

"Accuracy"

)

plt

.title

(

"EvaluationAccuracy"

)

plt

.legend

()

plt

.xticks

(np.arange(

0

,

5

))

plt

.show

()/<code>


繪製結果


深度學習告訴你,如何微調智能問答模型?


深度學習告訴你,如何微調智能問答模型?

RoBERTa-base結果


這些結果看起來充滿希望:使用比BERT-large小得多的模型,卻十分接近BERT-large(340M參數)的性能。


下面來擴大規模,改用RoBERTa-large。為此,只需要進行一些微調:


· 在模型加載代碼的一小段中,用"roberta-large"代替 "roberta-base" (第10行)

· 因較大型的模型會佔用更多的GPU內存,在PyTorch建構數據集代碼的一小段中,將batch_size設置為8(第1行)

· 在訓練和評估代碼段中(1至2行),將epochs設置為3,grad_acc_steps設置為4,以控制訓練時間並讓有效batchsize(批量)為32

· 再次運行上述步驟


深度學習告訴你,如何微調智能問答模型?


深度學習告訴你,如何微調智能問答模型?

RoBERTa-large結果


全新的先進“是/否”問答結果誕生了!

深度學習告訴你,如何微調智能問答模型?

預測


下面從SQuAD 數據集的一個段落中測試得到的模型:


<code>defpredict(question,passage):
                 sequence = tokenizer.encode_plus(passage,question, return_tensors=

"pt"

)[

'input_ids'

].to(device) logits =model(sequence)[

0

] probabilities = torch.softmax(logits, dim=

1

).detach().cpu().tolist()[

0

] proba_yes =round(probabilities[

1

],

2

) proba_no =round(probabilities[

0

],

2

) print(

f"Question:

{question}

, Yes:

{proba_yes}

"

,

f"No:

{proba_no}

"

) passage_superbowl =

"""SuperBowl 50 was an American football game to determine the champion of the NationalFootball League (NFL) for the 2015 season.The American Football Conference (AFC) champion Denver Broncos defeated the National FootballConference (NFC) champion Carolina Panthers 24–10 to earn their third SuperBowl title. The game was played onFebruary 7, 2016, at Levi's Stadium in the San Francisco Bay Area at SantaClara, California. As this was the50th Super Bowl, the league emphasized the 'golden anniversary' with various gold-themed initiatives, aswell as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (underwhich the game would have been known as 'Super Bowl L'), so that the logo could prominently feature theArabic numerals 50."""

passage_illuin =

"""Illuindesigns and builds solutions tailored to your strategic needs using ArtificialIntelligence and the new means of humaninteraction this technology enables."""

superbowl_questions= [

"Did theDenver Broncos win the Super Bowl 50?"

,

"Did theCarolina Panthers win the Super Bowl 50?"

,

"Was the SuperBowl played at Levi's Stadium?"

,

"Was the SuperBowl 50 played in Las Vegas?"

,

"Was the SuperBowl 50 played in February?"

,

"Was the Super Bowl 50 played inMarch?"

] question_illuin =

"Is Illuinthe answer to your strategic needs?"

for

question insuperbowl_questions: predict(question, passage_superbowl) predict(question_illuin, passage_illuin)/<code>

預測


深度學習告訴你,如何微調智能問答模型?

SQuAD 2.0示例段落


深度學習告訴你,如何微調智能問答模型?

預測


如預期的那樣,模型可以預測正確答案。


可見在許多用例中,Boolean智能問答是一個難題。通過實用指南,我們對BoolQ數據集上的RoBERTa進行了微調,並創建了新的SOTA結果。


深度學習告訴你,如何微調智能問答模型?

後續步驟


後續步驟包括:


深度學習告訴你,如何微調智能問答模型?

圖源:unsplash


· 試驗更新的模型,如 ALBERT 和 ELECTRA

· 通過Knowledge Distillation縮短推理時間

· 將“是/否”問答與抽取式問答相結合

· 進行dataaugmentation(數據擴充)以增加訓練樣本的數量


看來僅僅是判斷yes or no也並非易事!


深度學習告訴你,如何微調智能問答模型?

留言點贊關注

我們一起分享AI學習與發展的乾貨

如轉載,請後臺留言,遵守轉載規範


分享到:


相關文章: