海老名日記

海老名での暮らしからキャンプ、子育てまで語る雑記ブログ

【技術】TransformersのAutoTokenizerにご注意を

海老名とかマンションの話をするとか言ってたそばから全然違う話を書くのもあれだけど、技術系の話を少ししたいと思う。 私はAIの中でも自然言語処理というところをメインにやっているので今日はその話。

BERTと呼ばれる自然言語処理の中でも強いとされてる手法があり、それを簡単に使えるPythonのライブラリにHuggingfaceのTransformersというのがある。その中のTokenizerのところでハマったところがあったのでメモ程度に残しておく。

【追記】2020年3月16日
Issueをあげてもらって解決したようです。
AutoTokenizer.from_pretrained doesn't work on newer models · Issue #24 · cl-tohoku/bert-japanese · GitHub

従来のモデルによるAutoTokenizerを使ったTokenize

BERTの事前学習モデルで割と有名なものに東北大の乾研究室が作っているものがあり、今回はそれに新しいモデルが追加されたというのをTwitterで見たので少し試してみようとしていた。Tokenizer自体は公式とかを参考にして次の感じで使う*1

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
tok_res = tokenizer.tokenize("神奈川県民が選ぶ県内の“住みたい街ランキング”において、2020年は海老名が横浜に次ぐ第2位にランクイン。")
print(tok_res)
['神奈川', '県民', 'が', '選ぶ', '県内', 'の', '“', '住み', 'たい', '街', 'ランキング', '”', 'において', '、', '2020', '年', 'は', '海老名', 'が', '横浜', 'に', '次ぐ', '第', '2', '位', 'に', 'ランク', 'イン', '。']

思ったより普通。悪くはなさそう。 上の例は単語分割を使った例だが、文字で分割したモデルも公開されていてそちらはこんな感じ。

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-char-whole-word-masking')
tok_res = tokenizer.tokenize("神奈川県民が選ぶ県内の“住みたい街ランキング”において、2020年は海老名が横浜に次ぐ第2位にランクイン。")
print(tok_res)
['神', '奈', '川', '県', '民', 'が', '選', 'ぶ', '県', '内', 'の', '“', '住', 'み', 'た', 'い', '街', 'ラ', 'ン', 'キ', 'ン', 'グ', '”', 'に', 'お', 'い', 'て', '、', '2', '0', '2', '0', '年', 'は', '海', '老', '名', 'が', '横', '浜', 'に', '次', 'ぐ', '第', '2', '位', 'に', 'ラ', 'ン', 'ク', 'イ', 'ン', '。']

当たり前だが普通に文字単位に分割されていている。 ここまではこれまでも使われていたモデルで問題はない。 問題があったのは新しく追加されたモデルである。新しく4つほどモデルが追加されているのだが、これらはモデルを作るのに使ったWikipediaのデータが2020年のものが含まれている、UniDicという辞書が使われるようになっているなどの変更点があったりする。

新しく公開されたモデルによるAutoTokenizerを使ったTokenize

実際に例を見ていく。

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-v2')
tok_res = tokenizer.tokenize("神奈川県民が選ぶ県内の“住みたい街ランキング”において、2020年は海老名が横浜に次ぐ第2位にランクイン。")
print(tok_res)
['神', '奈', '川', '県', '民', 'が', '選', 'ぶ', '県', '内', 'の', '“', '住', 'みたい', '街', 'ランキング', '”', 'に', '##お', '##い', '##て', '、', '2020', '年', 'は', '海', '老', '名', 'が', '横', '浜', 'に', '次', 'ぐ', '第', '2', '位', 'に', '##ラン', '##ク', '##イン', '。']

パッと見た感じではわかりづらいかもしれないが、「神奈川」、「横浜」が一文字ずつ分割されていておかしい。さらにおかしいのは文字分割の方。

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-char-v2')
tok_res = tokenizer.tokenize("神奈川県民が選ぶ県内の“住みたい街ランキング”において、2020年は海老名が横浜に次ぐ第2位にランクイン。")
print(tok_res)
['神', '奈', '川', '県', '民', 'が', '選', 'ぶ', '県', '内', 'の', '“', '住', '[UNK]', '街', '[UNK]', '”', '[UNK]', '、', '[UNK]', '年', 'は', '海', '老', '名', 'が', '横', '浜', 'に', '次', 'ぐ', '第', '2', '位', '[UNK]', '。']

単語の時と比べてこれは明らかにおかしい。上の単語の結果で1文字になってないところが全て[UNK]となっている。普通に文字に分割すれば良いはずなのにどうしてこんなことに、、、

解決方法

解決方法と言うほど大したことはない。AutoTokenizerではなく日本語用に用意されているだろうBertJapaneseTokenizerを使う。このあたり詳しい人たちは普通にこちらを使っているかもしれないが、これまでAutoTokenizerでも問題なかったので気にせずAutoTokenizerを使ってる人もいるかもしれない。 実際にBertJapaneseTokenizerを使うとこんな感じになる。

from transformers import BertJapaneseTokenizer

tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-v2')
tok_res = tokenizer.tokenize("神奈川県民が選ぶ県内の“住みたい街ランキング”において、2020年は海老名が横浜に次ぐ第2位にランクイン。")
print(tok_res)
['神奈川', '県民', 'が', '選ぶ', '県', '内', 'の', '“', '住み', 'たい', '街', 'ランキング', '”', 'に', 'おい', 'て', '、', '2020', '年', 'は', '海老', '##名', 'が', '横浜', 'に', '次ぐ', '第', '2', '位', 'に', 'ランク', 'イン', '。']

先ほどのおかしい結果に比べると普通になっていることがわかる。 ヤバヤバだった文字分割も次の感じになる。

from transformers import BertJapaneseTokenizer

tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-char-v2')
tok_res = tokenizer.tokenize("神奈川県民が選ぶ県内の“住みたい街ランキング”において、2020年は海老名が横浜に次ぐ第2位にランクイン。")
print(tok_res)
['神', '奈', '川', '県', '民', 'が', '選', 'ぶ', '県', '内', 'の', '“', '住', 'み', 'た', 'い', '街', 'ラ', 'ン', 'キ', 'ン', 'グ', '”', 'に', 'お', 'い', 'て', '、', '2', '0', '2', '0', '年', 'は', '海', '老', '名', 'が', '横', '浜', 'に', '次', 'ぐ', '第', '2', '位', 'に', 'ラ', 'ン', 'ク', 'イ', 'ン', '。']

文字単位で分割するだけでなんてことないはずなのに、ちゃんとした結果が出るだけでこんなに嬉しいとは笑。

おわりに

今回はTransformersのAutoTokenizerを使った場合に日本語の分割がおかしくなる問題を見てきた。東北大の以前から公開されているモデルではAutoTokenizerでも問題なく動いたが、新しいモデルではおかしくなってて気づかず使ってしまう人もいそうなので気をつけて欲しい。日本語の場合はAutoTokenizerよりBertJapaneseTokenizerを使うほうが良さそう!と言う話でした。

ではでは。

*1:例の文はセントガーデンのHPより