すぐに試せる日本語BERTのDocker Imageを作ってみた

はじめに

学習済みBERTを試しに触ってみたいんだけど、日本語使えるBERTの環境整えるの面倒!っていう人向けに、お試しでBERTを使える Docker Image 作ってみました。

BERT はTransformers(旧pytorch-transformers、旧pytorch-pretrained-bert) を使用。
黒橋・河原研究室のWEBサイトに掲載されている、日本語pretrainedモデルのWhole Word Masking版を使ってます。

Transformers — transformers 2.2.0 documentation
BERT日本語Pretrainedモデル - KUROHASHI-KAWAHARA LAB

f:id:YukoIshizaki:20191127014302p:plain

Docker Image

ここに置いてあります。
https://hub.docker.com/r/ishizakiyuko/japanese_bert_trial

(多分、重いです...。全然「すぐに試せる」じゃないかも。)

ファイル一式をGitHubにもあげてます。
https://github.com/yuko-i/japanese_bert_trial_dockerfile

----- 追記 -----
Imageがかなり大きいです。
GitHubからDockerfile一式をcloneしてbuildするのをお勧めします!!

言い訳

1. JUMAN++のv2がcmakeでコケるので、v1にしてます_:(´ཀ`」 ∠):_
2. CMDが想定通りに動かないのでdocker runのオプションで対応で _:(´ཀ`」 ∠):_
tag 1.0.1から、CMDを使って動きます。runオプションつけなくても大丈夫です。
3. Docker弱者なので、中身が諸々変かもです _:(´ཀ`」 ∠):_

起動

1. pull する

 docker pull ishizakiyuko/japanese_bert_trial:1.0.1

3. run する
docker run -p 8888:8888 -itd : /bin/sh /etc/jupyter/start-jupyter-notebook.sh

tag 1.0.1 からは sh 指定なしでjupyter動きます

docker run -p 8888:8888 -d ishizakiyuko/japanese_bert_trial:1.0.1

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.2.0 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] 

最終レイヤーを取る形にしました。

おわり

日本語BERTのImageって既にあるかも?と思いましたが、勉強なので何番煎じでも良いと思い書きました╭( ・ㅂ・)و ̑̑

Dockerfile、ここ直した方がいいよ!みたいなのがあったらコメントいただけたら嬉しいです!

Kaggle Days Tokyo のオフラインコンペがNLP疑惑もあり、Google QUEST Q&A Labeling コンペも面白いという噂ですし、 NLP 機運?

おわり2

こちらのブログ記事、上記の黒橋・河原研究室WEBサイトの「公開モデルを試していただいたサイト」欄に掲載してくださいました!!
ご関係者の方には感謝です!!٩(ˊᗜˋ*)و

f:id:YukoIshizaki:20191129114751p:plain:w400