第14章 用BERT实现中文语句分类
BERT以Transformer的Encoder為架構,已MLM為模型,在很多領域取得歷史性的的突破。這里以Transformers上基于中文語料庫上訓練的預訓練模型bert-base-chinese為模型,以BertForSequenceClassification為下游任務模型,在一個中文數據集上進行語句分類。具體包括如下內容:
? 使用BERT的分詞庫Tokenizer
? 可視化BERT注意力權重
? 用BERT預訓練模型微調下游任務
? 訓練模型
?
14.1 背景說明
本章用到預訓練模型庫Transformers,Transformers為自然語言理解(NLU)和自然語言生成(NLG)提供了最先進的通用架構(BERT、GPT、GPT-2、Transformer-XL、XLNET、XLM、T5等等),其中有超過32個100多種語言的預訓練模型并同時支持TensorFlow 2.0和Pythorch1.0兩大深度學習框架。可用pip安裝Transformers。
?
| 1 | pip install transformers |
Transformers的官網:https://github.com/huggingface
這章使用BERT模型中漢語版本:BERT-Base, Chinese: 包括簡體和繁體漢字,共12層,768個隱單元,12個Attention head,110M參數。中文 BERT 的字典大小約有 2.1 萬個標識符(tokens),這些預訓練模型可以從Transformers官網下載。
使用了可視化工具BertViz,它的安裝步驟如下:
1.下載bertviz:
https://github.com/jessevig/bertviz
2.解壓到jupyter notebook當前目錄下
bertviz-master
14.1.1 查看中文BERT字典里的一些信息
1.導入需要的庫
指定使用預訓練模型bert-base-chinese。
?
| 1 2 3 4 5 6 7 8 | import torch from transformers import BertTokenizer from IPython.display import clear_output ? # 指定繁簡中文 BERT-BASE預訓練模型 PRETRAINED_MODEL_NAME = "bert-base-chinese"?? # 獲取預測模型所使用的tokenizer tokenizer = BertTokenizer.from_pretrained(PRETRAINED_MODEL_NAME) |
2. 查看tokenizer的信息
?
| 1 2 | vocab = tokenizer.vocab print("字典大小:", len(vocab)) |
運行結果:
字典大小: 21128
3.查看分詞的一些信息
?
| 1 2 3 4 5 6 7 8 | import random random_tokens = random.sample(list(vocab), 5) random_ids = [vocab[t] for t in random_tokens] ? print("{0:20}{1:15}".format("token", "index")) print("-" * 30) for t, id in zip(random_tokens, random_ids): ????print("{0:15}{1:10}".format(t, id)) |
運行結果:
token index
------------------------------
##san 10978
王 4374
##and 9369
蝕 6008
60 8183
BERT 使用當初 Google NMT 提出的 WordPiece Tokenization ,將本來的 words 拆成更小粒度的 wordpieces,有效處理不在字典里頭的詞匯 。中文的話大致上就像是 character-level tokenization,而有 ## 前綴的 tokens 即為 wordpieces。
除了一般的wordpieces以外,BERT還有5個特殊tokens:
? [CLS]:在做分類任務時其最后一層的表示.會被視為整個輸入序列的表示;
? [SEP]:有兩個句子的文本會被串接成一個輸入序列,并在兩句之間插入這個token作為分割;
? [UNK]:沒出現在BERT字典里頭的字會被這個token取代;
? [PAD]:zero padding掩碼,將長度不一的輸入序列補齊方便做batch運算;
? [MASK]:未知掩碼,僅在預訓練階段會用到。
14.1.2 使用Tokenizer分割中文語句
讓我們利用中文BERT的tokenizer將一個中文句子斷詞。
?
| 1 2 3 4 5 6 7 | text = "[CLS] 他移開這[MASK]桌子,就看到他的手表了。" tokens = tokenizer.tokenize(text) ids = tokenizer.convert_tokens_to_ids(tokens) ? print(text) print(tokens[:10], '...') print(ids[:10], '...') |
運行結果:
[CLS] 他移開這[MASK]桌子,就看到他的手表了。
['[CLS]', '他', '移', '開', '這', '[MASK]', '桌', '子', ',', '就'] ...
[101, 800, 4919, 2458, 6821, 103, 3430, 2094, 8024, 2218] ...
14.2 可視化BERT注意力權重
現在馬上讓我們看看給定上面有 [MASK] 的句子,BERT會填入什么字。
14.2.1 BERT對MAKS字的預測
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | """ 導入已訓練好的masked語言模型并對有[MASK]的句子做預測 """ from transformers import BertForMaskedLM ? # 除了tokens 以外我們還需要辨別句子的segment ids tokens_tensor = torch.tensor([ids])??# (1, seq_len) segments_tensors = torch.zeros_like(tokens_tensor)??# (1, seq_len) maskedLM_model = BertForMaskedLM.from_pretrained(PRETRAINED_MODEL_NAME) clear_output() ? # 使用masked LM 估計[MASK]位置所代表的實際標識符(token) maskedLM_model.eval() with torch.no_grad(): ????outputs = maskedLM_model(tokens_tensor, segments_tensors) ????predictions = outputs[0] ????# (1, seq_len, num_hidden_units) del maskedLM_model ? # 將[MASK]位置的概率分布取前k個最有可能的標識符出來 masked_index = 5 k = 3 probs, indices = torch.topk(torch.softmax(predictions[0, masked_index], -1), k) predicted_tokens = tokenizer.convert_ids_to_tokens(indices.tolist()) ? # 顯示前k個最可能的字。一般取第一個作為預測值 print("輸入 tokens :", tokens[:10], '...') print('-' * 50) for i, (t, p) in enumerate(zip(predicted_tokens, probs), 1): ????tokens[masked_index] = t ????print("Top {} ({:2}%):{}".format(i, int(p.item() * 100), tokens[:10]), '...') |
運行結果:
輸入 tokens : ['[CLS]', '他', '移', '開', '這', '[MASK]', '桌', '子', ',', '就'] ...
--------------------------------------------------
Top 1 (83%):['[CLS]', '他', '移', '開', '這', '張', '桌', '子', ',', '就'] ...
Top 2 ( 7%):['[CLS]', '他', '移', '開', '這', '個', '桌', '子', ',', '就'] ...
Top 3 ( 0%):['[CLS]', '他', '移', '開', '這', '間', '桌', '子', ',', '就'] ...
BERT透過關注這桌這兩個字,從2萬多個wordpieces的可能性中選出"張"作為這個情境下[MASK] token的預測值,效果還是不錯的。
14.2.2 導入可視化需要的庫
1.導入需要的庫
?
| 1 2 | from transformers import BertTokenizer, BertModel from bertv_master.bertviz import head_view |
2.創建可視化使用html配置函數
?
| 1 2 3 4 5 6 | # 在 jupyter notebook 顯示visualzation def call_html(): ??import IPython ??display(IPython.core.display.HTML('''<script src="/static/components/requirejs/require.js"></script><script>// <![CDATA[ requirejs.config({ paths: { base: '/static/base', "d3": "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.8/d3.min", jquery: '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min', }, }); // ]]></script>''')) |
?
14.2.3 可視化
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 記得我們是使用中文 BERT model_version = 'bert-base-chinese' model = BertModel.from_pretrained(model_version, output_attentions=True) tokenizer = BertTokenizer.from_pretrained(model_version) ? # 情境 1 的句子 sentence_a = "老爸叫小宏去買醬油," sentence_b = "回來慢了就罵他。" ? # 得到tokens后輸入BERT模型獲取注意力權重(attention) inputs = tokenizer.encode_plus(sentence_a,sentence_b,return_tensors='pt', add_special_tokens=True) token_type_ids = inputs['token_type_ids'] input_ids = inputs['input_ids'] attention = model(input_ids, token_type_ids=token_type_ids)[-1] input_id_list = input_ids[0].tolist() # Batch index 0 tokens = tokenizer.convert_ids_to_tokens(input_id_list) call_html() ? # 用BertViz可視化 head_view(attention, tokens) |
運行結果:
圖14-1 某詞對其它詞注意力權重示意圖
這是BERT第 10 層 Encoder block 其中一個 head 的注意力結果,從改圖可以看出,左邊的這個他對右邊的“宏”字關注度較高。
格式圖像發布于2020年9月3日作者feiguyun分類人工智能
發表評論
總結
以上是生活随笔為你收集整理的第14章 用BERT实现中文语句分类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HuggingFace-transfor
- 下一篇: 调用预训练好的XLnet词向量