アイソモカ

知の遊牧民の開発記録

開発記録 210916 BertJapaneseTokenizer をさわってみる

『Pytorch自然言語処理プログラミング』の1章が終わって、間を飛ばして5章をやってます。

book.impress.co.jp

東北大BERT cl-tohoku/bert-base-japanese · Hugging Face と BertJapaneseTokenizer を使って文を単語列に分割していきましょう。

...
ModuleNotFoundError: You need to install fugashi to use MecabTokenizer. See https://pypi.org/project/fugashi/ for installation.

お。

$ pip install fugashi

インストーりました。

fugashi GitHub - polm/fugashi: A Cython MeCab wrapper for fast, pythonic Japanese tokenization and morphological analysis.MeCabを使うためのwrapperなんですね、 mecab-python3 みたいなやつかな。

...
ModuleNotFoundError: The ipadic dictionary is not installed. See https://github.com/polm/ipadic-py for installation.

お。

$ pip install ipadic

はい、こっちもインストーりました。

IPA辞書の説明 GitHub - polm/ipadic-py: IPAdic packaged for easy use from Python. を見るとデカデカと You Shouldn’t Use This って書いてて、もう更新されていないから UniDic を使ってねらしいけど… bert-base-japanese の説明では

Tokenization The texts are first tokenized by MeCab morphological parser with the IPA dictionary and then split into subwords by the WordPiece algorithm. The vocabulary size is 32000.

ということなので、IPA辞書でいくのがいいってことで。

# bert2.py 
from transformers import BertJapaneseTokenizer
tknz = BertJapaneseTokenizer.from_pretrained("cl-tohoku/bert-base-japanese")
print(tknz.tokenize("関西国際空港駅に行きます"))
#['関西', '##国際空港', '駅', 'に', '行き', 'ます']

文を単語列に分割してはる。

ところで以前、係り受け解析がうまくいかないと思ったら形態素解析に使用する辞書の違いだったことがあり 開発記録 191013 Sun (CaboChaが文節を区切ってくれない件、一応解決) - アイソモカ開発記録191124 Sun (100本ノック#041, UniDic) - アイソモカ 、懲りたので、辞書を指定して動かしてみる。

まずはターミナルから MeCab を直接。

# unidic
$ mecab -d /usr/local/lib/mecab/dic/unidic-cwj-2.3.0/ -Owakati
関西国際空港駅に行きます
関西 国際 空港 駅 に 行き ます 

# ipadic
$ mecab -d /usr/local/lib/mecab/dic/ipadic/ -Owakati
関西国際空港駅に行きます
関西国際空港 駅 に 行き ます

次に、Python からIPA辞書を指定して fugashi を使ってみる。

辞書の指定方法は、.../.pyenv/versions/3.7.4/lib/python3.7/site-packages/transformers/models/bert_japanese/tokenization_bert_japanese.pyを参考にしました。

import os
from fugashi import GenericTagger
import ipadic

dic_dir = ipadic.DICDIR
mecab_option = "-Owakati"
mecabrc = os.path.join(dic_dir, "mecabrc")
print(mecabrc)
# .../.pyenv/versions/3.7.4/lib/python3.7/site-packages/ipadic/dicdir/mecabrc

mecab_option = f'-d "{dic_dir}" -r "{mecabrc}" ' + mecab_option
tagger = GenericTagger(mecab_option)

text = "関西国際空港駅に行く"
print(tagger.parse(text))
# 関西国際空港 駅 に 行く

大丈夫そ。

MeCab + ipadic で '関西国際空港', '駅', 'に', '行き', 'ます’分かち書きされ、 そこで未知語になった「関西国際空港」が WordPiece により分割されて '関西', '##国際空港', '駅', 'に', '行き', 'ます’ になるという理解です。 ## は、 WordPiece で分割する前にはこの前の単語にくっついてたよ〜の印っぽい。

# bert2.py 
from transformers import BertJapaneseTokenizer
tknz = BertJapaneseTokenizer.from_pretrained("cl-tohoku/bert-base-japanese")
print(tknz.tokenize("関西国際空港駅に行きます"))
print(tknz.encode("関西国際空港駅に行きます"))
print(tknz.decode(tknz.encode("関西国際空港駅に行きます")))

#$ python bert2.py 
#['関西', '##国際空港', '駅', 'に', '行き', 'ます']
#[2, 3013, 23783, 235, 7, 2609, 2610, 3]
#[CLS] 関西国際空港 駅 に 行き ます [SEP]

ははん、未知語は WordPiece によって分割されても、decode すると ## が消えて元に戻るんやね…