pytorch-transformersを触ってみる②
はじめに
前回はの入門ということで、QuikStartの内容を触ってみました。
kento1109.hatenablog.com
前回は英語でしたが、日本語のテキストを扱う場合はそのまま使うことは出来ません。
ということで、今回はpytorch-transformersで日本語のテキストを扱ってみようと思います。
Pretrained model
日本語でのPretrained modelとしては、京大の黒橋・河原研究室が公開しているものが有名です。
BERT日本語Pretrainedモデル - KUROHASHI-KAWAHARA LAB
このリソースを利用した既存のやってみたシリーズとしては以下などが参考となります。
Pytorchで日本語のbert学習済みモデルを動かすまで - Qiita
pytorchでBERTの日本語学習済みモデルを利用する - 文章埋め込み編 - Out-of-the-box
今回はこちらのリソースを活用して色々と触っていきます。
準備
ホームページより、Japanese_L-12_H-768_A-12_E-30_BPEをダウンロードします。
Pretrained modelは、Jumann++で形態素解析を行っているので、Jumann++をインストールしておきます。
また、Jumann++をpythonから利用するため、pyknp もインストールしておきます。
以下のようにpythonから形態素解析が出来れば準備OKです。
from pyknp import Juman jumanpp = Juman() text = "すもももももももものうち" result = jumanpp.analysis(text) print([mrph.midasi for mrph in result.mrph_list()]) # ['すもも', 'も', 'もも', 'も', 'もも', 'の', 'うち']
ただし、今回は触ってみることが目的なので、前処理の形態素解析は必ずしも必要でないです。
読み込み
前回は、以下のように文字列を指定してをロードしました。
model = BertModel.from_pretrained('bert-base-uncased')
今回は、ダウンロードしたpytorch_model.binを指定して読み込みます。
※学習は、BERT(BASE)と同じ設定 (12-layer, 768-hidden, 12-head)で行ったそうです。
model = BertModel.from_pretrained('bert-base-uncased') # UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
しかし、この場合UnicodeDecodeErrorが発生します。
上に挙げた記事はpytorch_pretrained_bertというライブラリを使っていたので、
少々勝手が異なるみたいです。
エラー箇所を見ると、
Constructs a `BertConfig` from a json file of parameters.
に関連する箇所が怪しかったので、以下のようにしてconfigclasssを引数に与えました。
config = BertConfig.from_json_file('Japanese_L-12_H-768_A-12_E-30_BPE/bert_config.json') model = BertModel.from_pretrained('Japanese_L-12_H-768_A-12_E-30_BPE/pytorch_model.bin', config=config)
とりあえず、エラーは出ていないので問題なさそうです。
bertForMaskedLM
前回同様に、文の一部をMASKする問題を試してみます。
model = BertForMaskedLM.from_pretrained('Japanese_L-12_H-768_A-12_E-30_BPE/pytorch_model.bin', config=config)
この場合の出力は単語数となります。modelの最後の層を見てみると、
(decoder): Linear(in_features=768, out_features=32006, bias=False)
となっているのが分かります。
では、「僕は友達とサッカーをすることが好きだ」の文を形態素解析します。
from pyknp import Juman jumanpp = Juman() text = "僕は友達とサッカーをすることが好きだ" result = jumanpp.analysis(text) tokenized_text = [mrph.midasi for mrph in result.mrph_list()] print(tokenized_text) # ['僕', 'は', '友達', 'と', 'サッカー', 'を', 'する', 'こと', 'が', '好きだ']
となります。
続いて、BERT用の入力に整形し、「サッカー」をMASKしてみます。
tokenized_text.insert(0, '[CLS]') tokenized_text.append('[SEP]') masked_index = 5 tokenized_text[masked_index] = '[MASK]' print(tokenized_text) ['[CLS]', '僕', 'は', '友達', 'と', '[MASK]', 'を', 'する', 'こと', 'が', '好きだ', '[SEP]']
日本語の辞書ファイルを読み込みます。
tokenizer = BertTokenizer("Japanese_L-12_H-768_A-12_E-30_BPE/vocab.txt", do_lower_case=False, do_basic_tokenize=False)
一応、入力系列を確認してみます。
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text) tokens_tensor = torch.tensor([indexed_tokens]) print(tokens_tensor) #tensor([[ 2, 5020, 9, 10729, 12, 4, 10, 22, 30, 11, 30808, 3]])
ここまで来れば、言語は関係ありません。
model.eval() tokens_tensor = tokens_tensor.to('cuda') model.to('cuda') 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) # ['話', '仕事', 'キス', 'ゲーム', 'サッカー']
5番目にサッカーと予測できています。その他も、MASKとして妥当そうです。
[SEP]は必要??
ここで、思ったのですが、単文の場合は末尾に[SEP]は必要なのでしょうか。
試しに[SEP]無しで予測した結果が以下の通りです。
print(predicted_tokens) ['話', '仕事', 'キス', '会話', 'セックス']
サッカーは挙がってきませんが、概ね結果は同じです。
(サッカーの代わりにセックスと予測したのはなかなか面白いですね。。)
ここから、[SEP]が予測結果に影響を与えていることが分かります。
また、[SEP]は付けておいた方が無難かと思います。(他では結果が異なるかもしれませんが・・)
おわりに
今回は、pytorch-transformersで日本語のテキストを触ってみました。
次回は、fine tuningで目的のタスクに応用してみたいと思います。