BERT实战(1):使用DistilBERT作为词嵌入进行文本情感分类,与其它词向量(FastText,Word2vec,Glove)进行对比
這次根據一篇教程Jay Alammar: A Visual Guide to Using BERT for the First Time學習下如何在Pytorch框架下使用BERT。
主要參考了中文翻譯版本
教程提供了可用的代碼,可以在colab或者github獲取。
1. huggingface/transformers
Transformers提供了數千個預訓練的模型來執行文本任務,如100多種語言的分類、信息提取、問答、摘要、翻譯、文本生成等。
文檔:https://huggingface.co/transformers/
模型:https://huggingface.co/models
huggingface團隊用pytorch復現許多模型,本次要使用它們提出的DistilBERT模型。
2. 數據集
本次使用的數據集是 SST2,是一個電影評論的數據集。用標簽 0/1 代表情感正負。
3. 模型
句子的情感分類模型由兩部分組成:
- DistilBERT處理輸入的句子,并將它從句子中提取的一些信息傳遞給下一個模型。 DistilBERT 是一個更小版本的 BERT 模型,是由 HuggingFace 團隊開源的。它保留了 BERT 能力的同時,比 BERT 更小更快。
- 一個基本的 Logistic Regression 模型,它將處理 DistilBERT 的輸出結果并且將句子進行分類,輸出0或1。
在這兩個模型之間傳遞的數據是一個 768 維的向量。
假設句子長度為n,那及一個句子經過BERT應該得到n個768 維的向量。
實際上只使用[CLS]位置的向量看作是我們用來分類的句子的embedding向量。
4. 訓練與預測
4.1 訓練
雖然我們使用了兩個模型,但是只需要訓練回歸模型(Logistic Regression)即可。
對于 DistilBERT 模型,使用該模型預訓練的參數即可,這個模型沒有被用來做句子分類任務的訓練和微調。
使用 Scikit Learn 工具包進行操作。將整個BERT輸出的數據分成 train/test 數據集。
將75%的數據劃為訓練集,將25%的數據劃分為測試集。
sklearn的train/test split在進行分割之前會對示例進行shuffles。
接下來就用機器學習的方法訓練回歸模型就行了。
4.2 預測
如何使用模型進行預測呢?
比如,我們要對句子 “a visually stunning rumination on love” 進行分類
第一步,用 BERT 的分詞器(tokenizer)將句子分成 tokens;
第二步,添加特殊的 tokens 用于句子分類任務(在句子開頭加上 [CLS],在句子結尾加上 [SEP]);
第三步,分詞器(tokenizer)會將每個 token 替換成 embedding 表中的ID,embedding 表是我們預訓練模型自帶的;
下面這一行代碼就完成了上述3步。
每個token的輸出都是一個一個768維的向量。
由于這是一個句子分類任務,我們只取第一個向量(與 [CLS] token有關的向量)而忽略其他的 token 向量。
將該向量作為 邏輯回歸的輸入。
5. 代碼(加入圖片注釋)
環境的配置就不細說了,可以在transformers的github頁面查閱
5.1 導入所需的工具包
import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.model_selection import GridSearchCV from sklearn.model_selection import cross_val_score import torch import transformers as ppb import warnings warnings.filterwarnings('ignore')5.2 導入數據集
數據集的鏈接,這里我已經提前下載到本地了。
df = pd.read_csv('./train.tsv', delimiter='\t', header=None) # 為做示例只取前2000條數據 batch_1 = df[:2000] # 查看正負例的數量 batch_1[1].value_counts()5.3 導入預訓練模型
下載大概花費了40s
# For DistilBERT: model_class, tokenizer_class, pretrained_weights = (ppb.DistilBertModel, ppb.DistilBertTokenizer, 'distilbert-base-uncased')## Want BERT instead of distilBERT? Uncomment the following line: #model_class, tokenizer_class, pretrained_weights = (ppb.BertModel, ppb.BertTokenizer, 'bert-base-uncased')# Load pretrained model/tokenizer tokenizer = tokenizer_class.from_pretrained(pretrained_weights) model = model_class.from_pretrained(pretrained_weights)5.4 數據預處理
包括token化和padding
token化的過程與4.2中圖片相同
需要把所有的向量用 id 0 來填充較短的句子到一個相同的長度。
padding的過程如下圖所示,值得注意的是,如果我們直接把padded發給BERT,那會讓它有點混亂。我們需要創建另一個變量,告訴它在處理輸入時忽略(屏蔽)我們添加的填充。這就是attention_mask:
5.5 使用BERT
# 基本可以看作又進行了一次embedding input_ids = torch.LongTensor(padded) attention_mask = torch.tensor(attention_mask)with torch.no_grad():last_hidden_states = model(input_ids, attention_mask=attention_mask)# BERT模型輸出的張量尺寸為[2000, 59, 768] # 取出[CLS]token對應的向量 features = last_hidden_states[0][:,0,:].numpy() labels = batch_1[1] # 取出標簽
5.5 訓練一個Logistic回歸模型
# 劃分訓練集和測試集 train_features, test_features, train_labels, test_labels = train_test_split(features, labels) # 搜索正則化強度的C參數的最佳值。 parameters = {'C': np.linspace(0.0001, 100, 20)} grid_search = GridSearchCV(LogisticRegression(), parameters) grid_search.fit(train_features, train_labels)print('best parameters: ', grid_search.best_params_) print('best scrores: ', grid_search.best_score_)lr_clf = LogisticRegression(C = 10.526405263157894) lr_clf.fit(train_features, train_labels) lr_clf.score(test_features, test_labels)
在使用完整數據集的時候,預測的準確率約為0.847。
6.結果評估
from sklearn.dummy import DummyClassifier clf = DummyClassifier()scores = cross_val_score(clf, train_features, train_labels) print("Dummy classifier score: %0.3f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))可以發現訓練出的分類器的結果明顯好于隨即預測的結果。
但這個例子恐怕完全沒有體現出BERT的性能,因為缺少了fine-tuning階段。根據19年的教程,該數據集上最高的準確率是 96.8。
通過fine-tuning 更新 BERT 的參數權重, 可以提升DistilBERT 模型在句子分類任務(稱為下游任務)上得到的分數。原教程中表示,通過對 DistilBERT 進行 fine-tuned 之后達到了 91.3 的準確率(模型見huggingface官網),全參數量的 BERT 模型能達到 92.7 的分數。
訓練參數如下圖所示:
接下來我們將數據集按照8:1:1切分為訓練集,驗證集和測試集。
通過在訓練集上的微調,模型在測試集上的準確度為0.8876,可能因為參數設置問題,沒有達到原文中的效果。
代碼見我的GitHub倉庫:Comparison-of-Word-Vectors
7. 與常規的詞向量對比
很多工作表明使用句子中所有詞匯的Glove向量平均,比直接使用不經fine-tune的BERT [CLS]嵌入表現得更好。 為了驗證這個觀點,在數據集SST2上分別使用fastText,word2vec, Glove做詞嵌入,再訓練分類器,比較它們與BERT的效果。
代碼見我的GitHub倉庫:Comparison-of-Word-Vectors
7.1 fastText
fastText是Facebook于2016年開源的一個詞向量計算和文本分類工具,在文本分類任務中,fastText(淺層網絡)往往能取得和深度網絡相媲美的精度,卻在訓練時間上比深度網絡快許多數量級。在標準的多核CPU上, 能夠訓練10億詞級別語料庫的詞向量在10分鐘之內,能夠在1分鐘之內分類有著30萬多類別的50多萬句子。
- fastText在輸入時對每個詞加入了n-gram特征,在輸出時使用分層softmax加速訓練。
- fastText將整篇文章的詞向量求平均作為輸入得到文檔向量,用文本分類做有監督訓練,對輸出進行softmax回歸,詞向量為副產品。
- fastText也可以無監督訓練詞向量,與CBOW非常相似。
fastText論文地址
fastText的github地址
使用fasttext預訓練的英文詞向量下載地址
這里我們使用的預訓練詞向量是上圖中的1。
結果如下:
| 直接使用預訓練的fasttext詞向量 | 0.7951 |
| 使用預訓練的fasttext詞向量并用訓練集微調 | 0.7847 |
| 只使用訓練集訓練 | 0.7899 |
總體來說,效果比不進行微調的BERT差了不少,但是我們選用的fasttext詞向量維數為300,比DistilBERT產生的768維的詞向量小了不少。
參考資料:
https://blog.csdn.net/ymaini/article/details/81489599
https://blog.csdn.net/sinat_26917383/article/details/83041424
7.2 word2vec
使用word2vec預訓練的英文詞向量下載地址
word2vec說明文檔
gensim文檔
關于word2vec的原理,可見我的這篇博客:NLP方向組會內容整理(1)詞向量
結果如下:
| 直接使用google提供預訓練的word2vec詞向量 | 0.7917 |
| 只使用訓練集訓練 | 0.6685 |
可以發現,使用預訓練詞向量的準確度和fasttext相差無幾。同時,由于word2vec不具備fasttext的識別OOV詞的能力,在較少數據集上訓練出的詞向量效果不佳。
筆者未能實現在預訓練詞向量的基礎上使用訓練集繼續訓練,根據gensim3.8的文檔,Google提供的預訓練詞向量,只能以KeyedVectors形式讀取,不支持繼續訓練,只有完整的word2vec模型(model.word2vec.Word2Vec)才能繼續訓練。
7.3 Glove
Glove將Word2Vec原本預測一個詞和它的語境是否會共現的任務,升級成預測兩個詞之間的共現頻率大小的問題。
GloVe官網
GloVe的github地址
使用方法與word2vec類似,也是通過gensim使用,這里不再贅述。
7.4 總結對比
在本任務中,只是用預訓練的DistilBERT模型做詞嵌入的效果要好于預訓練的fasttext和word2vec,也好于在訓練集上微調過的fasttext。
7.5 更多詞向量
https://github.com/Embedding/Chinese-Word-Vectors/blob/master/README_zh.md
https://ai.tencent.com/ailab/nlp/en/embedding.html
總結
以上是生活随笔為你收集整理的BERT实战(1):使用DistilBERT作为词嵌入进行文本情感分类,与其它词向量(FastText,Word2vec,Glove)进行对比的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python格式化字符%e_用%格式化P
- 下一篇: steam泰坦之旅dlc_泰坦之旅亚特兰