🌐

BertJapaneseTokenizerを使った日本語事前学習済みALBERTモデルをつくった

2022/04/21に公開

どうもこんばんは。
ご無沙汰してます。
またALBERTの日本語事前学習済みモデルをつくったのですが、今回はそのトークナイザーに BertJapaneseTokenizer を使ってみたというポエムです。

albert-base-japanese-v1-with-japanese-tokenizer

https://huggingface.co/ken11/albert-base-japanese-v1-with-japanese-tokenizer
こちらがそのモデルです。
名前が冗長なのが気になりますが目をそらしましょう。
よければ使ってみてください。

なぜBertJapaneseTokenizerを使ったのか

前回のモデルでは通常通り?SentencePieceを使ってモデルをつくったわけですが、そもそも「BERTの代わりにALBERTを使いたい」となったときに、いま使っているBERTが広く知られた東北大BERTだったりするとMeCabベースのトークナイザーなので、BERTとALBERTというモデルの差異よりも結局トークナイザーの差異に苦労しそうだなと感じました。

で、「じゃあAlbertJapaneseTokenizer的なSomethingをつくればええな?」と思って書こうとしたんですが、「いや、これ結局BertJapaneseTokenizerと同じものになるな」「だったら流用してみたらええのでは?」と思ったのが今回これをつくった動機です。

transformersのPRでは他にも別のモデルで同じようなことを試みた履歴を見かけたので、たぶんBertJapaneseTokenizerクラスはJapaneseTokenizerクラスとかに切り出したほうがいいと思う、各モデル固有の実装はそれを継承して書く、みたいな。

やったこと

今回の学習データにはWikipedia全文だけです。
ALBERTのアーキテクチャ的には今回もbase-v1です。
学習回数は今回いろいろあって、というのもAWSのg5インスタンス使ってみたかったのでそれ試したりとかで…
前回に倣うなら、バッチサイズ128換算で350万ステップくらいですかね、だいぶ多いです。

肝心のBertJapaneseTokenizerを用意するにあたって、最初に vocab.txt 作らないといけないこともあって、前回より少し学習データの前処理をがんばって、だいぶキレイキレイしました←

こだわった点として、1文の切れ方で、句点で切るのはそうなんですがカギカッコ「」内の句点をどうするかは悩んで、今回は無視するように処理しました。
というのも

Aは「hogehogeですね。fugafuga。」と言った。

みたいな文章があったとき、素直に句点で切ると

["Aは「hogehogeですね。", "fugafuga。", "」と言った。"]

になるわけですが、さすがにちょっと違和感というか。

"」と言った。"

をトークナイズして学習されても、困る…と思いません?
このままだとたぶん

["」", "と", "言っ", "た", "。"]

みたいになって、 "」" は文頭に出現するみたいな変な特徴を学習する気がするんですよね。
この問題ってみんなどうしてるんだろうと思いながら、今回は

["Aは「hogehogeですね。fugafuga。」と言った。"]

となるように処理しました。

学習したモデルはどうなったのか

まあ使いやすいですよね、MeCabベースなので。
日本語的に直感的にわかりよいというか。
前回のモデルはトークナイザーがいろいろあって面倒だったので、その点は今回の方が使いやすいかなと思ってみてます。

from transformers import (
    AutoModelForMaskedLM, AutoTokenizer
)


tokenizer = AutoTokenizer.from_pretrained("ken11/albert-base-japanese-v1-with-japanese-tokenizer")
model = AutoModelForMaskedLM.from_pretrained("ken11/albert-base-japanese-v1-with-japanese-tokenizer")

text = "明日は明日の[MASK]が吹く"
tokens = tokenizer(text, return_tensors="pt")
mask_index = tokens["input_ids"][0].tolist().index(tokenizer.mask_token_id)
predict = model(**tokens)[0]
_, result = predict[0, mask_index].topk(5)

print(tokenizer.convert_ids_to_tokens(result.tolist()))

こんな感じでサクッと使えます。
もちろんちゃんと使うにはファインチューニングして各タスクにあわせていくのがよいです。

あと、AutoModelやAutoTokenizerで呼んでパッと使えるので、実際にModelHub上で試してもらうこともできます。

最後に、一応前回と同じルールでNERタスクを解かせて精度を見てみました。

ルール
いずれも同じデータセットを使って10エポック程度学習(ファインチューニング)し、その後同じテストデータを使って結果を比較
データはストックマーク株式会社が公開しているner-wikipedia-datasetを使用
テストには全体の8%のデータを使用
結果はseqevalを使ってmode=defaultscheme=IOB2で評価
Tokenizerがそれぞれ異なるので、support数は若干ズレるが大きな問題ではないので無視する

似たような結果ですね。悪くはなさそうです、とりあえず実用できそうな雰囲気はわかりました。

まとめ

というわけで懲りずに日本語事前学習済みALBERTをつくったという話でした。
SentencePiece派の方も、MeCab派の方も、どちらもお楽しみいただけるようラインナップを拡充させた次第です!←

https://huggingface.co/ken11/albert-base-japanese-v1-with-japanese-tokenizer
https://huggingface.co/ken11/albert-base-japanese-v1

お気軽にお試しいただけたらなによりです。

Discussion