【BERT】小學生級上手教程,從原理到上手全有圖示,還能直接在線運行
- 2019 年 12 月 15 日
- 筆記
作者 Jay Alammar 伊瓢 編譯 量子位 出品 | 公眾號 QbitAI
BERT,作為自然語言處理領域的C位選手,總是NLPer們逃不過的一環。
但是,如果是經驗匱乏、基礎薄弱的選手,想玩轉BERT還是有點難的。
現在,科技博主Jay Alammar創作了一篇《第一次使用BERT的圖形化指南》,用非常簡單清晰的方式介紹了如何上手BERT,從BERT的原理到實際操作的過程都有圖示,甚至圖比代碼都多。量子位為大家編譯搬運如下~

這篇文章主要以用BERT的變體對句子進行分類為例,介紹了BERT的使用方式。
最後的傳送門處還有Colab的地址。
數據集:SST2
首先,我們需要用到SST2數據集,裏面的句子來自於一些電影評論。
如果評論者對電影表示肯定讚賞,就會有「1」的標籤;
如果評論者不喜歡這個電影,發表了負面評論,就會有「0」的標籤。
數據集里的電影評論是用英文寫的,大概長這樣:
句子 |
標籤 |
---|---|
a stirring , funny and finally transporting re imagining of beauty and the beast and 1930s horror films一部激動人心、有趣的、最後傳達出對美女與野獸和1930年代恐怖片的重新想像的電影 |
1 |
apparently reassembled from the cutting room floor of any given daytime soap顯然是從隨便哪個日間肥皂劇里剪一剪拼貼起來的 |
0 |
they presume their audience won't sit still for a sociology lesson他們認為他們的觀眾不會安靜的坐下聽社會學的課 |
0 |
this is a visually stunning rumination on love , memory , history and the war between art and commerce這是關於愛情、回憶、歷史以及藝術與商業之爭的視覺上令人驚嘆的反省 |
1 |
jonathan parker 's bartleby should have been the be all end all of the modern office anomie films喬納森·帕克的劇作應當是所有現代辦公室失范電影的終結 |
1 |
句子情感分類模型
現在,藉助SST2影評數據集,我們需要創建一個自動對英文句子進行分類的模型。
如果判斷是肯定的、正面的,就標註1;如果判斷是否定的、負面的,就標註0。
大致的邏輯是這樣的:

輸入一句話,經過電影評論句子分類器,輸出積極或消極的結果。
這個模型實際上是兩個模型組成的。
DistilBERT負責處理句子,提取信息,然後傳遞給下一個模型,這是?「抱抱臉公司」(HuggingFace)做的一個開源BERT版本,比較輕量級而且運行快,性能和原版差不多。
下一個模型就是一個基本的邏輯回歸模型,它的輸入是DistilBERT的處理結果,輸出積極或消極的結果。
我們在兩個模型之間傳遞的數據是大小為768的向量,可以把這個向量當成可以用來分類的句子嵌入。

模型的訓練過程
雖然我們會用到兩個模型,但是我們只需要訓練邏輯回歸模型就行了,DistilBERT可以直接用預訓練好的版本。
不過,這個模型從來都沒有針對句子分類任務被訓練或微調過,我們從通用目標BERT獲取一些句子分類能力,尤其是對於第一個位置的BERT輸出而言(與[CLS]token相關),這是BERT的第二個訓練目標,接下來就是句子分類了,這個目標似乎是訓練模型將全句意義封裝到第一位置的輸出位置。
這個Transformer庫為我們提供了DistilBERT的實施和模型的預訓練版本。

教程概述
這是本篇教程的整個計劃,我們先用訓練過的DistilBERT來生成2000個句子的句子嵌入。

之後就不用再碰DistilBERT了,這裡都是Scikit Learn,我們在這個數據集上做常規的訓練和測試:

針對第一個模型也就是DistilBERT進行訓練測試,創建我們訓練用的數據集並評估第二個模型也就是邏輯回歸模型。
然後在訓練集上訓練邏輯回歸模型:

單個預測是如何進行的
在研究代碼解釋如何訓練模型之前,我們先看看一個訓練後的模型如何進行預測。
我們試着給這句話進行分類預測:
a visually stunning rumination on love 關於愛情的視覺上令人驚嘆的反省
第一步,用BERT tokenizer把句子分為兩個token;
第二步,我們加入句子分類用的特殊token(第一個位置的是[CLS],句子結束的位置是[SEP])。

第三步,tokenizer用嵌入表中的ID代替每個token,成為訓練模型的組件。

注意,tokenizer是在這一行代碼里完成所有步驟的:
1tokenizer.encode("a visually stunning rumination on love", add_special_tokens=True)
現在我們的輸入句子是可以傳遞給DistilBERT的適當狀態了。
這個步驟可視化起來長這樣:

從DistilBERT經過
輸入向量從DistilBERT經過,輸出每個輸入token的向量,每個向量有768個數字組成。

因為這是個句子分類的任務,所以我們忽視掉除第一個向量之外的其他內容(第一個向量和[CLS]token相關),然後把第一個向量作為邏輯回歸模型的輸入。

從這裡開始,邏輯回歸模型的工作就是根據它從訓練過程中學到的經驗,把這個向量進行分類。
這個預測計算的過程是這樣的:

代碼
現在,開始看這整個過程的代碼,後面你也可以在傳送門裡看到GitHub代碼和Colab上的可運行版本。
首先,導入trade工具。
1import numpy as np 2import pandas as pd 3import torch 4import transformers as ppb # pytorch transformers 5from sklearn.linear_model import LogisticRegression 6from sklearn.model_selection import cross_val_score 7from sklearn.model_selection import train_test_split
你可以在GitHub里找到這個數據集,所以我們可以直接把它導入到pandas dataframe里。
1df = pd.read_csv('https://github.com/clairett/pytorch-sentiment-classification/raw/master/data/SST2/train.tsv', delimiter='t', header=None)
可以直接用df.head() 查看dataframe的前五行,看看數據集長啥樣。
1df.head()
然後就輸出:

導入預訓練DistilBERT模型和tokenizer
1model_class, tokenizer_class, pretrained_weights = (ppb.DistilBertModel, ppb.DistilBertTokenizer, 'distilbert-base-uncased') 2 3## Want BERT instead of distilBERT? Uncomment the following line: 4#model_class, tokenizer_class, pretrained_weights = (ppb.BertModel, ppb.BertTokenizer, 'bert-base-uncased') 5 6# Load pretrained model/tokenizer 7tokenizer = tokenizer_class.from_pretrained(pretrained_weights) 8model = model_class.from_pretrained(pretrained_weights)
現在可以對這個數據集tokenize了。
注意,這一步和上面的例子不同,例子只處理了一個句子,但是我們要批處理所有的句子。
Tokenization
1tokenized = df[0].apply((lambda x: tokenizer.encode(x, add_special_tokens=True)))
這一步讓每個句子都變成ID列表。

數據集是當前的列表(或者pandas Series/DataFrame),在DistilBERT處理它之前,我們需要給所有向量統一規格,給短句子加上token 0。
填上0之後,現在就有了一個成形的矩陣/張量可以投餵給BERT了:

用DistilBERT處理
現在,為填充的token矩陣創造一個輸入張量,發送給DistilBERT。
1input_ids = torch.tensor(np.array(padded)) 2 3with torch.no_grad(): 4 last_hidden_states = model(input_ids)
運行這一步之後,last_hidden_states保留DistilBERT的輸出。

打開BERT的輸出張量
解壓縮這個3-d輸出張量,先檢查它的尺寸:

回顧處理句子的過程
每行都和我們數據集里的一個句子關聯,回顧一下,整個處理過程是這樣的:

挑出重要部分
關於句子分類,我們只對BERT的[CLS] token輸出感興趣,所以我們只挑出重要部分就行了。

下面是從3D張量里挑出我們需要的2D張量的辦法:
1# Slice the output for the first position for all the sequences, take all hidden unit outputs 2features = last_hidden_states[0][:,0,:].numpy()
現在的特徵是個2D numpy數組,裏面有我們數據集里所有句子的句子嵌入。

邏輯回歸數據集
現在我們有BERT的輸出了,前面邏輯回歸模型已經已經訓練好了。下圖的798列是特徵,標籤是初始數據集裏面的。

在完成傳統的機器學習訓練測試後,我們可以拿邏輯回歸模型再進行訓練。
1labels = df[1] 2train_features, test_features, train_labels, test_labels = train_test_split(features, labels)
將數據分為訓練集/測試集:

接下來,在訓練集上訓練邏輯回歸模型:
1lr_clf = LogisticRegression() 2lr_clf.fit(train_features, train_labels)
現在模型訓練完了,用測試集給它打分:
1lr_clf.score(test_features, test_labels)
得出的模型準確度為81%。
Score Benchmarks
作為參考,這個數據集目前的最高準確率得分為96.8.
在這個任務里,DistilBERT可以訓練來提升分數,這個過程叫做微調(fine-tuning),可以更新BERT的權重,來實現更好的分類句子。
微調後的DistilBERT可以實現90.7的準確率,完整的BERT模型能達到94.9的準確率。
傳送門
A Visual Guide to Using BERT for the First Time https://jalammar.github.io/a-visual-guide-to-using-bert-for-the-first-time/
代碼 https://github.com/jalammar/jalammar.github.io/blob/master/notebooks/bert/A_Visual_Notebook_to_Using_BERT_for_the_First_Time.ipynb
Colab https://colab.research.google.com/github/jalammar/jalammar.github.io/blob/master/notebooks/bert/A_Visual_Notebook_to_Using_BERT_for_the_First_Time.ipynb
DistilBERT https://medium.com/huggingface/distilbert-8cf3380435b5