はじめに
学習済みBERTを試しに触ってみたいんだけど、日本語使えるBERTの環境整えるの面倒!っていう人向けに、お試しでBERTを使える Docker Image 作ってみました。
BERT はTransformers(旧pytorch-transformers、旧pytorch-pretrained-bert) を使用。
黒橋・河原研究室のWEBサイトに掲載されている、日本語pretrainedモデルのWhole Word Masking版を使ってます。
Transformers — transformers 2.1.1 documentation
BERT日本語Pretrainedモデル - KUROHASHI-KAWAHARA LAB
Docker Image
ここに置いてあります。
https://hub.docker.com/r/ishizakiyuko/japanese_bert_trial
(多分、重いです...。全然「すぐに試せる」じゃないかも。)
ファイル一式をGitHubにもあげてます。
https://github.com/yuko-i/japanese_bert_trial_dockerfile
(こっちからbuildした方が早いかも?)
言い訳
1. JUMAN++のv2がcmakeでコケるんでv1にしてます _:(´ཀ`」 ∠):_
2. CMDが想定通りに動かないのでdocker runのオプションで対応で _:(´ཀ`」 ∠):_
3. Docker弱者なので、中身が諸々変かもです _:(´ཀ`」 ∠):_
起動
1. pull する
docker pull ishizakiyuko/japanese_bert_trial
3. run する
docker run -p 8888:8888 -itd <image>:<tag> /bin/sh /etc/jupyter/start-jupyter-notebook.sh
4. log から token 調べる
docker logs <conteiner id>
5. ブラウザからjupyter にアクセスする(http://localhost:8888)
6. 4.で調べた token を入れてログイン
7. コードを書いて BERT を試してみる
コード例
おなじみのMask予測を動かしてみます。
日本語対応はこちらを参考にしました。
pytorch-transformersを触ってみる② - 機械学習・自然言語処理の勉強メモ
import os import torch from transformers import BertForMaskedLM, BertConfig, BertTokenizer from pyknp import Juman BASE_PATH = './Japanese_L-12_H-768_A-12_E-30_BPE_WWM_transformers' BERT_CONFIG = 'config.json' BERT_MODEL = 'pytorch_model.bin' VOCAVULARY_LIST = 'vocab.txt' jumanpp = Juman() # 形態素解析 text = 'どんなに勉強しても全然頭が良くならない' result = jumanpp.analysis(text) tokenized_text =[mrph.midasi for mrph in result.mrph_list()] print(tokenized_text)
>> ['どんなに', '勉強', 'して', 'も', '全然', '頭', 'が', '良く', 'なら', 'ない']
# Mask tokenized_text.insert(0, '[CLS]') tokenized_text.append('[SEP]') masked_index = 6 # Maskしたいtextのindex tokenized_text[masked_index] = '[MASK]' print(tokenized_text)
>> ['[CLS]', 'どんなに', '勉強', 'して', 'も', '全然', '[MASK]', 'が', '良く', 'なら', 'ない', '[SEP]']
# Bert model config = BertConfig.from_json_file(os.path.join(BASE_PATH, BERT_CONFIG)) model = BertForMaskedLM.from_pretrained(os.path.join(BASE_PATH, BERT_MODEL), config=config) tokenizer = BertTokenizer(os.path.join(BASE_PATH, VOCAVULARY_LIST), do_lower_case=False, do_basic_tokenize=False) # token化 indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text) tokens_tensor = torch.tensor([indexed_tokens]) print(tokens_tensor)
>> tensor( [ [ 2, 1, 6547, 19, 23, 1, 4, 11, 4161, 371, 46, 3 ] ] )
# 予測 model.eval() tokens_tensor = tokens_tensor.to('cpu') model.to('cpu') with torch.no_grad(): outputs = model(tokens_tensor) predictions = outputs[0] _, predicted_indexes = torch.topk(predictions[0, masked_index], k=5) predicted_tokens = tokenizer.convert_ids_to_tokens(predicted_indexes.tolist()) print(predicted_tokens)
>> ['成績', '頭', '気持ち', '方', '態度']
それらしいのが、出力されました。
Multi lingualも試してみる
Multi lingual が存在し、日本語も含まれる 104 の言語対応している学習済みモデルが使えます。
Multi-lingual models — transformers 2.1.1 documentation
黒橋・河原研究室版の方が良いはずですが、一応、どんなものか試してみたいと思います。
上記のコードのtokenizerとmodelの部分を変更。
tokenizer = BertTokenizer.from_pretrained("bert-base-multilingual-cased") model = BertForMaskedLM.from_pretrained('bert-base-multilingual-cased')
形態素解析の結果も違っているので、masked_indexを9にしました。
# 形態素解析 tokenized_text = tokenizer.tokenize(text) print(tokenized_text)
>> ['ど', '##んな', '##に', '勉', '強', 'しても', '全', '然', '頭', 'が', '良', 'く', '##な', '##らない']
masked_index = 9 tokenized_text[masked_index] = '[MASK]' print(tokenized_text)
>> ['[CLS]', 'ど', '##んな', '##に', '勉', '強', 'しても', '全', '然', '[MASK]', 'が', '良', 'く', '##な', '##らない', '[SEP]']
あとは同じで、
>> ['愛', '心', '気', '方', '日']
と出てきました。
やっぱり、黒橋・河原研究室版の方が良いですね!
文章埋め込み
BertModelから文章埋め込みベクトルを取得したいと思います。
config, juman++は上と同じ
from transformers import BertModel text = '今日は朝から犬の鳴き声がうるさい' result = jumanpp.analysis(text) tokenized_text =[mrph.midasi for mrph in result.mrph_list()] print(tokenized_text)
>> ['今日', 'は', '朝', 'から', '犬', 'の', '鳴き声', 'が', 'うるさい']
# token化 bert_tokenizer = BertTokenizer(os.path.join(BASE_PATH, VOCAVULARY_LIST), do_lower_case=False, do_basic_tokenize=False) bert_tokens = bert_tokenizer.tokenize(" ".join(tokenized_text)) ids = bert_tokenizer.convert_tokens_to_ids(["[CLS]"] + bert_tokens[:126] + ["[SEP]"]) tokens_tensor = torch.tensor(ids).reshape(1, -1) print(tokens_tensor)
>> tensor( [ [ 2, 2281, 9, 599, 27, 2099, 5, 21245, 11, 4274, 8823, 3 ] ] )
# embedding config = BertConfig.from_json_file(os.path.join(BASE_PATH, BERT_CONFIG)) model = BertModel.from_pretrained(os.path.join(BASE_PATH, BERT_MODEL), config=config) model.eval() with torch.no_grad(): all_encoder_layers, _ = model(tokens_tensor) embedding = all_encoder_layers.numpy()[0][-1] primt(embedding)
>> [ 6.78906918e-01 2.64199853e-01 4.61503953e-01 -3.25612813e-01 ..... -2.42455140e-01 -1.16255842e-02]
最終レイヤーを取る形にしました。