概要
こんにちは、SB Intuitions株式会社インターンの塚越です。 先日、日本語・英語合わせて約4.4T tokensを学習した日本語ModernBERTというモデルを構築・公開しました。
公開したモデルがどんなモデル、どんな性能なのかについては日本語ModernBERTの開発: 開発と評価編 (1/3)を、開発過程で加えた工夫や分析については日本語ModernBERTの開発: 分析編 (2/3)をご覧ください。
さて、日本語自然言語処理をする上で、トークナイザや分かち書き器をどう扱うかは、多くの方が頭を悩ませたことがある話題だと思います。 本記事では、これらが日本語ModernBERTの性能にどのような影響を与えるかについて分析した結果を紹介します。
今回の記事は以下のようにまとめられます:
- 日本語ModernBERTは事後的にトークナイザを改造することで形態素解析器を簡単に追加できる
- 日本語ModernBERTは事後的にトークナイザを差し替えても性能への影響が小さい
- 日本語ModernBERTは固有表現認識タスクでも高い性能を達成する
日本語モデルのトークナイザ
近年の自然言語処理において、入力テキストはサブワードと呼ばれる単位に分割されます。 我々が公開した日本語ModernBERTは、SentencePieceというツールを用いて、入力文を直接サブワードへ分割します。 これは近年のLLMが一般的に採用している方法であり、トークン化効率が良く(テキストをトークンに分割した時に、その系列長が短い)、言語依存の処理が入らないので汎用性が高いという利点があります。
一方で、既存の日本語に特化したエンコーダ型モデルは、形態素解析器による分かち書きをサブワード化の前にあらかじめ行う場合があります。
具体的な例として東北大BERT-base v3についてみてみると、入力テキストは
(1)形態素解析器MeCabとUnidic辞書を用いて入力テキストを形態素ごと(≒単語単位)に分かち書き
(2)単語それぞれに対してWordPieceという手法でサブワードに分割
という手順を踏みます。
ここで、WordPieceとはオリジナルのBERTで用いられていたサブワード化手法の名前です。
対して、SentencePieceはソフトウェアの名前であり、アルゴリズムの名前ではない点に注意が必要です。
実際に、ある文を
(a) MeCabで分かち書きした場合
(b) SentencePieceで直接サブワード列に分割した場合
(c) MeCabによる分かち書きで得られた形態素列に対して、さらにそれぞれをSentencePieceでサブワード分割した場合
にどのようなトークンが出現するかを示したものです。
この時、SentencePieceでの分割にはsbintuitions/modernbert-ja-130mのモデルを利用しています 1。
図から、(b)と(c)で得られるサブワードの列が異なっていることがわかります。 特に(b)では「を抜け」「ると」のように、単語の境界として自然な位置を気にせずサブワードへ分割しているのに対し、(c)では「を」「抜ける」「と」という人間から見ても自然なサブワードに分割されていることがわかります。
「を抜けると」というフレーズが「を」「抜ける」「と」に分割されるか、「を抜け」「ると」に分割されるかでは、単語単位でラベルを割り振るような後段のタスクにおける処理のしやすさが格段に異なってきます。 特に、品詞タグ付け(POS tagging)や固有表現認識(Named Entity Recognition: NER)のように、トークンに対して分類ラベルを割り当てるようなタスクを解く場合は、形態素境界を意識したトークナイズが有用になります。
今まで数多くの日本語に特化したエンコーダ型モデルが公開されてきていますが、それらはそれぞれ異なる形態素解析器・辞書・サブワード化手法を用いていました。 一部のモデルについて、それらをまとめたのが以下の表です。
モデル | 形態素解析器 | 辞書 | サブワード化 |
---|---|---|---|
tohoku-nlp/bert-base-japanese | MeCab | IPA辞書 | WordPiece |
tohoku-nlp/bert-base-japanese-v3 | MeCab | Unidic Lite | WordPiece |
line-corporation/line-distilbert-base-japanese | MeCab | Unidic Lite | SentencePiece (BPE) |
nlp-waseda/roberta-base-japanese | Juman++ | JumanDIC | SentencePiece (Unigram) |
studio-ousia/luke-japanese-base-lite | - | - | SentencePiece (BPE) |
sbintuitions/modernbert-ja-130m | - | - | SentencePiece (Unigram) |
同じ入力文・同じ語彙を用いていても、形態素解析器・辞書・サブワード化手法が異なると、トークナイズ後のサブワード列が異なるものになる場合があります。 事後的にトークナイザを変更してしまうと、性能にどのような影響があるかは予想できません。 そのため、下流タスクで特定の形態素解析器・辞書が求められている場合には、その形態素解析器・辞書を用いたトークナイザで、モデルを構築するか、もしくは、トークナイザを変更した上で継続事前学習することが一般的でした。
ところで、我々が公開した日本語ModernBERTは、約4.4T tokensと、日本語のエンコーダ型モデルとしては2025年4月時点で過去最大規模のサイズのコーパスを用いて事前学習を行っています。 訓練コーパスが大規模であるほど、さまざまなノイズを含んだ文章を訓練データとして用いていると予想でき、多様な入力にモデルが頑健になっていると期待できます。 そこで今回は、我々が公開した日本語ModernBERTのトークナイザを事後的に改造することで、形態素解析器を事前に入れ込みたいユースケースに対応できるかどうかを検証しました。
日本語ModernBERTのトークナイザの改造
既存の日本語に特化したエンコーダ型モデルは、HuggingFace/TransformersにおけるBertJapaneseTokenizer
というクラスをトークナイザ用のクラスとして用いていました。
このクラスは、トークナイズ時に先述の(1)形態素解析器による分かち書き→(2)SentencePieceによるサブワード分割、を一度に行なってくれるクラスです。
このクラスがあることで、日本語に特化した煩雑な前処理が不要になっています。
また幸いなことに、このクラスは主要な日本語形態素解析器であるMeCab, Juman++, Sudachiの3つが簡単に扱えるようにサポートされています。 さらに、オリジナルのBERTから発展して、WordPieceだけでなく、SentencePieceもサブワード化の手法として利用することができるようになっています。
これらの機能は以下のPRで実現されたものです:
- Add sudachi and jumanpp tokenizers for bert_japanese #19043
- Add sentencepiece to BertJapaneseTokenizer #19769
今回はこれらの機能を用いて、日本語ModernBERTのトークナイザを、形態素解析器を事前に入れ込むものに改造します。
具体的には以下のコードで、SentencePieceのモデルファイルを直接指定し、種々のトークナイザの設定を引き継いだBertJapaneseTokenizer
を定義します。
from huggingface_hub import hf_hub_download from transformers import AutoModelForMaskedLM, AutoTokenizer from transformers.models.bert_japanese.tokenization_bert_japanese import BertJapaneseTokenizer tokenizer_orig = AutoTokenizer.from_pretrained("sbintuitions/modernbert-ja-130m") spm_file_path: str = hf_hub_download("sbintuitions/modernbert-ja-130m", "tokenizer.model") tokenizer_mecab = BertJapaneseTokenizer( vocab_file=None, spm_file=spm_file_path, do_word_tokenize=True, word_tokenizer_type="mecab", do_subword_tokenize=True, subword_tokenizer_type="sentencepiece", unk_token=tokenizer_orig.unk_token, sep_token=tokenizer_orig.sep_token, pad_token=tokenizer_orig.pad_token, cls_token=tokenizer_orig.bos_token, mask_token=tokenizer_orig.mask_token, mecab_kwargs={ "normalize_text": False, }, )
Juman++, Sudachiの場合の実装例
# Juman++ tokenizer_jumanpp = BertJapaneseTokenizer( vocab_file=None, spm_file=spm_file_path, do_word_tokenize=True, word_tokenizer_type="jumanpp", do_subword_tokenize=True, subword_tokenizer_type="sentencepiece", unk_token=tokenizer_orig.unk_token, sep_token=tokenizer_orig.sep_token, pad_token=tokenizer_orig.pad_token, cls_token=tokenizer_orig.bos_token, mask_token=tokenizer_orig.mask_token, jumanpp_kwargs={ "normalize_text": False, }, ) # Sudachi tokenizer_sudachi = BertJapaneseTokenizer( vocab_file=None, spm_file=spm_file_path, do_word_tokenize=True, word_tokenizer_type="sudachi", do_subword_tokenize=True, subword_tokenizer_type="sentencepiece", unk_token=tokenizer_orig.unk_token, sep_token=tokenizer_orig.sep_token, pad_token=tokenizer_orig.pad_token, cls_token=tokenizer_orig.bos_token, mask_token=tokenizer_orig.mask_token, sudachi_kwargs={ "normalize_text": False, }, )
なお、形態素解析器を用いる際には、本来であれば辞書を設定する必要があります(今回はデフォルトの辞書のパスが用いられるようになっています)。
BertJapaneseTokenizer
クラスへの引数でシステム上の辞書のパスを設定することにより、異なる辞書に基づくトークナイザをインスタンス化することも可能です。
トークナイザごとにどのようなオプションが用意されているかは、以下の実装を読みに行くのが早いでしょう。
以上によって作成したトークナイザを適用することで、(1)形態素解析器による分かち書き→(2)SentencePieceによるサブワード分割という一連の処理を、シンプルに実装することができます。
実際に、先述の例で示したトークナイズを実現するコードは以下のようになります。
上述のBertJapaneseTokenizer
を用いることで、事前の明示的な分かち書きなどをせずとも実装可能です。
print(tokenizer_mecab.word_tokenizer.tokenize(text)) # ['国境', 'の', '長い', 'トンネル', 'を', '抜ける', 'と', '雪国', 'で', 'あっ', 'た', '。'] print(tokenizer_orig(text).input_ids) # [1, 36837, 48491, 18803, 65211, 3123, 3275, 1608, 4774, 278, 2] print(tokenizer_orig.tokenize(text)) # ['国境', 'の長い', 'トンネル', 'を抜け', 'ると', '雪', '国', 'であった', '。'] print(tokenizer_mecab(text).input_ids) # [1, 36837, 291, 5782, 18803, 315, 29446, 317, 3275, 1608, 311, 12051, 577, 278, 4] print(tokenizer_mecab.tokenize(text)) # ['国境', 'の', '長い', 'トンネル', 'を', '抜ける', 'と', '雪', '国', 'で', 'あっ', 'た', '。']
トークナイザを改造したモデルの性能評価
一般に、トークナイザを事後的に改造することは、必要でない限り避けるべきと考えられています。
形態素解析器を事前に適用する場合は、文を単語単位に分割してからそれぞれの単語をサブワードに分割するため、文章がより多くの箇所で分割されることになり、最終的な系列長が増加することが多くなります。 実際、上図の例でもSentencePieceを直接適用する場合は系列長が9であるのに対して、MeCabを事前に適用する場合は系列長が13になっており、4も系列長が伸びています。 このように系列長が長くなることで訓練・推論コストが増大してしまいます。
また、形態素境界を意識したサブワード分割が性能に良い影響を与えるとも限りません。 特に、事前学習時のトークナイズの方式を下流タスクで変更することは、見たことのないトークンを入力に増やしてしまうという点でモデルの性能を悪化させてしまう可能性があります。
しかし、実際のところどのくらい性能が低下するのでしょうか?
そこで、MeCab, Juman++, Sudachiという3つの日本語形態素解析器に対応したトークナイザを用意し、これらを用いた場合に下流タスクでの性能がどのように変化するかを調査しました。 つまり、形態素解析器を事後的に差し込むことが、どの程度性能に影響を与えるかを調査しました。 もし事後的に形態素解析器を差し込むことで大きく性能が低下するならば、形態素解析器を事前適用した上での継続事前学習など特別な手続きが必要なのかもしれません。 逆に、事後的に形態素解析器を差し込んでも性能があまり低下しないならば、単一のモデルを様々な形態素解析器と継続事前学習なしに組み合わせて使える可能性があり、モデルの適用範囲が広がって嬉しいです。
以下で、実験設定と実験結果について紹介します。
実験設定
評価タスクには、前回・前々回の記事と同じく、以下の12種類データセットを用いました。
- 知識ベースのタスク: JCommonsenseQA(JComQA)、RCQA
- 日本語の統語的評価タスク: JCoLA
- 自然言語推論(NLI)タスク: JNLI、JSICK、JSNLI、京大 RTE(KU RTE)
- 意味的テキスト類似性(STS)タスク: JSTS
- 各種分類タスク: Livedoor ニュースコーパス(Livedoor)、LLM-jp Toxicity(Toxicity)、MARC-ja、WRIME v2(WRIME)
評価タスクは、以前までと同じものを利用しているため、品詞タグ付けや固有表現認識などトークン分類系タスクが含まれていない点にはご注意ください。 これらのタスクでの評価は、トークナイザの改造がモデルの振る舞いにどれほど影響を与えるかを測るために行っています。
評価方法も以前までの記事と同様です。 まず、トークナイザを差し替えたモデルを用意し、訓練データセットを用いてそのモデルをfine-tuningします。 その後、開発セットでの性能が最良のハイパーパラメータの組み合わせ・モデルを用いてテストセットでの評価を行います。 ハイパーパラメータは以前までと同様以下の範囲で探索しました:
- 学習率: {5e-6, 1e-5, 2e-5, 3e-5, 5e-5, 1e-4}
- エポック数:
- 事例数の多いタスク:{1, 2}
- 事例数の少ないタスク:{3, 5, 10}
実験対象モデルとして、我々が公開した日本語ModernBERTの4種類のサイズのモデルを用いました:
- sbintuitions/modernbert-ja-30m
- sbintuitions/modernbert-ja-70m
- sbintuitions/modernbert-ja-130m
- sbintuitions/modernbert-ja-310m
これらのモデルを、それぞれMeCab, Juman++, Sudachiという3つの異なる形態素解析器を用いるトークナイザに差し替えた上でfine-tuningを行います。 MeCabの辞書にはUnidic Liteを、Juman++の辞書にはJumanDICを用いました。 Sudachiには3種類の辞書が存在し、分割モードとしてA(最も細かい)、B(AとCの中間)、C (固有名詞など分割せずに収録)が用意されています。 今回はHuggingFace/Transformersのデフォルト値であったAを用いました。
実験結果
12タスクの平均性能を以下のグラフに示します。
トークナイザを改造した場合の性能の表
モデル | トークナイザ | Avg. | JCommonsenseQA (acc) | RCQA (acc) | JCoLA (acc) | JNLI (acc) | JSICK (acc) | JSNLI (acc) | 京大RTE (acc) | JSTS (Spearman) | ライブドア (acc) | LLMjp Toxicity (acc) | MARC-ja (acc) | WRIME (acc) |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
30m | MeCab→SentencePiece | 85.06 | 78.18 | 81.84 | 76.50 | 88.72 | 84.55 | 92.33 | 59.92 | 85.77 | 96.77 | 90.22 | 95.49 | 90.49 |
Juman++→SentencePiece | 85.30 | 79.66 | 81.96 | 78.45 | 88.31 | 84.44 | 91.04 | 60.48 | 86.01 | 96.58 | 90.56 | 95.67 | 90.38 | |
Sudachi→SentencePiece | 84.92 | 78.46 | 82.02 | 76.94 | 88.38 | 84.36 | 91.97 | 60.48 | 85.78 | 96.85 | 88.09 | 95.42 | 90.33 | |
SentencePiece | 85.67 | 80.95 | 82.35 | 78.85 | 88.69 | 84.39 | 91.79 | 61.13 | 85.94 | 97.20 | 89.33 | 95.87 | 91.61 | |
70m | MeCab→SentencePiece | 86.50 | 84.56 | 83.54 | 79.50 | 90.00 | 85.32 | 92.88 | 60.56 | 87.21 | 95.63 | 91.12 | 95.85 | 91.87 |
Juman++→SentencePiece | 86.54 | 84.56 | 83.17 | 80.35 | 90.21 | 84.96 | 92.69 | 60.48 | 87.86 | 95.52 | 90.67 | 96.12 | 91.89 | |
Sudachi→SentencePiece | 86.42 | 84.58 | 83.36 | 79.24 | 90.12 | 84.79 | 92.67 | 60.89 | 88.12 | 95.22 | 91.01 | 95.91 | 91.18 | |
SentencePiece | 86.77 | 85.65 | 83.51 | 80.26 | 90.33 | 85.01 | 92.73 | 60.08 | 87.59 | 96.34 | 91.01 | 96.13 | 92.59 | |
130m | MeCab→SentencePiece | 88.38 | 89.22 | 84.65 | 81.49 | 92.05 | 86.39 | 93.84 | 64.19 | 88.97 | 97.48 | 92.36 | 96.36 | 93.59 |
Juman++→SentencePiece | 88.53 | 89.85 | 84.62 | 82.27 | 92.06 | 86.30 | 93.93 | 64.19 | 89.40 | 97.67 | 92.13 | 96.38 | 93.53 | |
Sudachi→SentencePiece | 88.28 | 89.72 | 84.85 | 81.45 | 91.65 | 86.11 | 94.09 | 63.39 | 89.19 | 97.34 | 91.91 | 96.40 | 93.32 | |
SentencePiece | 88.95 | 91.01 | 85.28 | 84.18 | 92.03 | 86.61 | 94.01 | 65.56 | 89.20 | 97.42 | 91.57 | 96.48 | 93.99 | |
310m | MeCab→SentencePiece | 89.72 | 92.33 | 85.73 | 84.37 | 92.99 | 86.84 | 94.46 | 69.52 | 90.47 | 97.69 | 92.25 | 96.29 | 93.75 |
Juman++→SentencePiece | 89.89 | 93.33 | 85.76 | 85.11 | 92.96 | 87.00 | 94.32 | 69.76 | 90.50 | 97.18 | 92.02 | 96.36 | 94.35 | |
Sudachi→SentencePiece | 89.69 | 93.12 | 85.95 | 84.02 | 92.83 | 87.09 | 94.28 | 68.55 | 90.60 | 97.15 | 91.69 | 96.35 | 94.68 | |
SentencePiece | 89.83 | 93.53 | 86.18 | 84.81 | 92.93 | 86.87 | 94.48 | 68.79 | 90.53 | 96.99 | 91.24 | 96.39 | 95.23 | |
tohoku-nlp/bert-base-japanese-v3 | MeCab→WordPiece | 86.74 | 82.82 | 83.65 | 81.50 | 89.68 | 84.96 | 92.32 | 60.56 | 87.31 | 96.91 | 93.15 | 96.13 | 91.91 |
tohoku-nlp/bert-large-japanese-v2 | MeCab→WordPiece | 88.36 | 86.93 | 84.81 | 82.89 | 92.05 | 85.33 | 93.32 | 64.60 | 89.11 | 97.64 | 94.38 | 96.46 | 92.77 |
studio-ousia/luke-japanese-base-lite | SentencePiece | 87.15 | 82.95 | 83.53 | 82.39 | 90.36 | 85.26 | 92.78 | 60.89 | 86.68 | 97.12 | 93.48 | 96.30 | 94.05 |
studio-ousia/luke-japanese-large-lite | SentencePiece | 88.94 | 88.01 | 84.84 | 84.34 | 92.37 | 86.14 | 94.32 | 64.68 | 89.30 | 97.53 | 93.71 | 96.49 | 95.59 |
上図において、SPはSentencePieceを用いたサブワード分割を表し、"MeCab→SP"のような表記は、該当の形態素解析器による分かち書きをおこなってから、それぞれの単語に対してSentencePieceによるサブワード分割を行う場合を表しています。
図から、事前に形態素解析器による分かち書きを適用する場合においては、若干の性能低下が見られることが多いものの、どの形態素解析器でもそれほど性能低下具合が大きくないことがわかります。 また、どの形態素解析器を用いても、性能差が大きくないこともわかります。
特に、東北大BERT-base v3と同じように、Unidic Lite辞書を用いたMeCabによる分かち書きを行うように130mモデルのトークナイザを置き換えた場合の性能が、東北大BERT-base v3よりも高い値になっていることが確認できました。
310Mモデルに関しては、トークナイザを差し替えた後の方が性能がわずかに高い(89.89 vs 89.83)という結果が得られました。 乱数によるブレの可能性もあるため、同等程度であると考えるのが良さそうですが、トークナイザの事後的な(いわば無理矢理な)改造が与える性能への影響が非常に小さい場合もあることがわかりました。
これら評価はあくまで文分類・回帰タスクにおける性能であり、品詞タグ付けや固有表現認識などトークン分類系タスクとは異なるタスクで評価を行っている点には注意が必要です。 ただ、少なくともトークナイザを事後的に差し替えることによる性能への影響は相対的には小さいと言え、継続事前学習など特別な手続きをせずとも十分実用的な性能が得られるということがわかります。 総じて、トークナイザの変化に対し、文分類・回帰タスクにおいて、日本語ModernBERTはかなり頑健であることが確認できました。
固有表現認識タスクでの評価
これまでの評価は文分類・回帰タスクで行ってきました。 次に、トークン分類系タスク、特に固有表現認識タスクでの評価を行いました。
評価データセットとしてUD Japanese GSD v2.6を用いました。 このデータセットでは、あらかじめ入力テキストがMeCab(辞書はUnidic)により形態素単位に分かち書きされており、形態素ごとに固有表現かどうかのラベルが割り当てられています。
タスクを解くにあたり、この分かち書き済みの単語列をさらにサブワードに分割し、サブワード単位でラベルを当てられるかを評価します。 また、モデル訓練時にMeCab & Unidicでの分かち書き→サブワード化を行っているモデルは、入力形式が変化しないため、若干有利である可能性があります。
ハイパーパラメータの探索方法や評価手順は先述のものと同様です。 また、評価にはseqevalを用いました。
実験の結果を以下に示します。
モデル | パラメータ数 | 事前訓練時の 形態素解析器と辞書 |
Precision | Recall | F1 |
---|---|---|---|---|---|
sbintuitions/modernbert-ja-30m | 37M | - | 73.08 | 77.83 | 75.37 |
sbintuitions/modernbert-ja-70m | 70M | - | 78.10 | 82.30 | 80.14 |
sbintuitions/modernbert-ja-130m | 132M | - | 82.83 | 86.45 | 84.60 |
sbintuitions/modernbert-ja-310m | 315M | - | 83.82 | 85.62 | 84.71 |
line-corporation/line-distilbert-base-japanese | 68M | MeCab & Unidic Lite |
76.81 | 82.43 | 79.50 |
tohoku-nlp/bert-base-japanese-v3 | 111M | MeCab & Unidic Lite |
80.68 | 84.70 | 82.64 |
ku-nlp/deberta-v3-base-japanese | 160M | - | 79.90 | 85.98 | 82.83 |
FacebookAI/xlm-roberta-base | 278M | - | 72.88 | 80.59 | 76.54 |
cl-tohoku/bert-large-japanese-char-v2 | 311M | - | 79.10 | 84.93 | 81.89 |
tohoku-nlp/bert-large-japanese-v2 | 337M | MeCab & Unidic Lite |
83.55 | 86.57 | 85.03 |
FacebookAI/xlm-roberta-large | 560M | - | 78.86 | 82.59 | 80.67 |
結果としては、日本語ModernBERTの130Mモデル、および、310Mモデルが、F値において東北大BERT-large v2に近い性能を示しました。 惜しくも東北大BERT-large v2の性能には及びませんでしたが、事前学習時と異なる入力形式にも適切に対応し、高い性能を達成できることが確認できました。
そのほかの実装例
MeCabやJuman++といったHuggingFace/Transformersで公式にサポートされている形態素解析器を用いる場合は、先述のBertJapaneseTokenizer
クラスの作り方で問題ありませんが、異なる形態素解析器・独自の辞書を利用したい場合には問題があるかもしれません。
そこで、分かち書きした後のテキストをトークナイザへの入力とすることで、同様の処理を実現できます。
たとえば、事前に何らかの方法で分かち書きを行って単語の列を得ている場合に、その単語列に対してサブワード分割を行うには以下のようにすればよいです。
from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained(f"sbintuitions/modernbert-ja-130m") texts = [ ["国境", "の", "長い", "トンネル", "を", "抜ける", "と", "雪国", "であった", "。"], ["更級日記", "は", "平安時代中期", "に", "書かれた", "回想録です。"] ] inputs = tokenizer(texts, is_split_into_words=True) print(tokenizer.convert_ids_to_tokens(inputs.input_ids[0])) # ['<s>', '国境', 'の', '長い', 'トンネル', 'を', '抜ける', 'と', '雪', '国', 'であった', '。', '</s>'] print(tokenizer.convert_ids_to_tokens(inputs.input_ids[1])) # ['<s>', '更', '級', '日記', 'は', '平安時代', '中期', 'に', '書かれた', '回想', '録', 'です', '。', '</s>']
このようにtokenize(..., is_split_into_words=True)
のように設定することで、単語の列をさらにサブワードに分割する処理を自動的に行うことができます。
これまで紹介してきた方法と異なり、前処理として分かち書きが必要になってしまいますが、このようにすれば柔軟に対応できます。
また、品詞タグ付や固有表現認識を行う場合は、単語列とサブワードのid列との対応付けを取る必要があります。 これは、spacy-alignmentsというライブラリを用いて以下のように実現できます。
import spacy_alignments as tokenizations from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("sbintuitions/modernbert-ja-130m") words: list[str] = ["更級日記", "は", "回想録です。"] input_ids: list[int] = tokenizer(words, is_split_into_words=True).input_ids subwords: list[str] = tokenizer.convert_ids_to_tokens(input_ids) words2subwords, subwords2words = tokenizations.get_alignments(words, subwords) print(words, subwords) # ['更級日記', 'は', '回想録です。'] # ['<s>', '更', '級', '日記', 'は', '回想', '録', 'です', '。', '</s>'] print(words2subwords) # [[1, 2, 3], [4], [5, 6, 7, 8]] print(subwords2words) # [[], [0], [0], [0], [1], [2], [2], [2], [2], []] # 0番目の単語「更級日記」を構成するサブワードのid・文字列のリストを取得する例 print([input_ids[idx] for idx in words2subwords[0]]) # [19857, 3906, 5445] print([subwords[idx] for idx in words2subwords[0]]) # ['更', '級', '日記']
これらのようにすることで、独自の形態素解析器・辞書を所有している組織でも、日本語ModernBERTを独自の入出力粒度で扱うことができるようになります。 また、先ほどの実験結果でも示したとおり、独自粒度のトークナイザとともに所望のタスクでfine-tuningを行えば、十分な性能に達すると期待できます。
おわりに
特に我々が公開した日本語ModernBERTはトークナイザの違いにもある程度頑健であり、事後的にトークナイザを差し替えても高い性能を維持できることがわかりました。 また、固有表現認識タスクでの評価では、事前訓練時と異なる入力形式であっても、日本語ModernBERTが高い性能を持つことを示しました。
本記事で取り上げた日本語ModernBERTの各モデルは、商用利用も可能なMITライセンスで公開しております。 ぜひ商用システム開発や研究開発、OSSモデルの学習などに利用していただければ幸いです。
- なお、我々が公開した日本語ModernBERTのトークナイザは、モデルサイズによらず全て同じものです。↩