要点(先に結論)
- 同じ意味の文でも、日本語は英語より多くのトークンを消費する。技術文では最大3倍になるケースもあった。
- ただし「日本語は何倍」と一言で言うのは不正確で、文の種類とモデルで1.05〜3.00倍まで幅がある。
- そして「日本語が高い」のは自然法則ではなく、トークナイザの語彙設計の手当ての差。同じOpenAIでも
cl100k→o200kの世代交代で日本語の比率は3.00→1.67に縮んだ。 - 本記事はGPT系(tiktoken)・Mistral・多言語BERT・日本語特化BERTの5トークナイザに同じ文を通し、トークン数と分割のされ方を実測した記録。コードとサンプルは末尾に置く。
本記事では、実測にもとづく事実と筆者の仮説を区別する。仮説の箇所には文中で 🟠 を添える。それ以外は実測データにもとづく内容として読んでよい。
字数とトークン数は逆転する
まずこの2文を見てほしい。どちらも同じ内容の技術文で、GPT-4o(o200k)に通した結果だ。
- 英語:We performed cross-validation using a validation dataset to evaluate the accuracy of the machine learning model.
- 日本語:機械学習モデルの精度を評価するために、検証データセットを用いて交差検証を行った。
| 文字数 | トークン数 | |
|---|---|---|
| 英語 | 112字 | 18トークン |
| 日本語 | 40字 | 30トークン |
英語のほうが文字数は約3倍長いのに、トークン数は日本語のほうが多い。「長さの直感」が逆転している。
実際にどうトークンへ切られたかを見ると、理由がはっきりする。GPT-4o(o200k)に通した分割結果がこれだ(| が区切り)。
英語 (18トークン):
We | performed | cross | -validation | using | a | validation |
dataset | to | evaluate | the | accuracy | of | the | machine |
learning | model | .
日本語 (30トークン):
機 | 械 | 学 | 習 | モデル | の | 精 | 度 | を | 評価 | する | ため | に | 、 |
検 | 証 | デ | ータ | セット | を | 用 | いて | 交 | 差 | 検 | 証 | を | 行 | った | 。
英語は performed validation dataset のように単語まるごとが1トークンに収まっている(語頭の空白もトークンに含まれる)。一方の日本語は「機」「械」「学」「習」と漢字が1文字ずつに割れ、熟語の「機械学習」が4トークンを食っている。同じ意味でも、日本語のほうがトークンを多く必要とするのはこのためだ。
LLMの課金も処理も、文字数ではなくトークン数で動く。だから「短く書いたつもりの日本語プロンプトが、英語より高くつく」ということが普通に起きる。なぜこうなるのか、トークナイザを開けて確かめていく。
同じ技術文を「1トークンに何文字詰まっているか」で割り直すと、英語は約6.2文字/トークン、日本語は約1.3文字/トークン。英語は頻出語がまるごと1トークンに統合されているのに対し、日本語は1文字〜サブ文字単位までしか縮まっていない。これが消費量の差の正体だ。
なぜ日本語は食うのか
現代のトークナイザは、文字でも単語でもなく、その中間のサブワードを単位にする。よく使われる文字の並びをまとめて1トークンにする仕組みで、代表的なのがBPE(Byte Pair Encoding)だ。
ここで効くのが語彙が何で構築されたか。多くの主要トークナイザは英語中心のコーパスで語彙を作ったため、英語の頻出語・接辞は長いまとまりで1トークンに収まる。一方、日本語は語彙の割り当てが薄く、漢字仮名交じりで切れ目もない(分かち書きがない)ため、細かく割れてしまう。
さらに、UTF-8では日本語1文字は3バイト。語彙に載っていない文字は、文字ですらなくバイト単位に分解される。後で実例を見るが、珍しい漢字1文字が3トークンに散る、ということが起きる。
なぜAIごとにトークナイザが違うのか
トークナイザは各社が別々に設計する。主な違いは3つの軸に整理できる。
| モデル | アルゴリズム | おおよその語彙サイズ | 設計の方向 |
|---|---|---|---|
| GPT-3.5/4(cl100k) | バイトレベルBPE | 約10万 | 汎用・英語寄り |
| GPT-4o(o200k) | バイトレベルBPE | 約20万 | 汎用・多言語強化 |
| Mistral | BPE(SentencePiece) | 約3.2万 | 汎用・コンパクト |
| mBERT | WordPiece | 約12万 | 多言語 |
| BERT-ja(東北大) | WordPiece | 約3.2万 | 日本語特化 |
注:Mistralは本記事ではBPE系の代表として使った。Llamaの代替であり、語彙サイズが異なる(Llama 3は約12.8万)ため、ここの数値をそのままLlamaの値として読まないでほしい。
また、mBERTと日本語BERTは生成モデルではなくエンコーダ(BERT系)。「日本語を投げれば安い生成AI」ではなく、アルゴリズムと語彙設計を見るための対照群として並べている。
この設計差は、トークナイザが「知らない文字」をどう扱うかにそのまま出る。実測の分割境界には次の記号が現れた。記号そのものが設計思想の現れなので、先に読み方を示す。
| 記号 | 出すモデル | 意味 |
|---|---|---|
� (U+FFFD) |
tiktoken | マルチバイト文字の途中の1バイトを単独表示した印。文字化けではなく、バイト単位で割れている証拠 |
<0xEF> |
Mistral | バイトフォールバック。語彙にない文字を生のUTF-8バイトで吐く |
[UNK] |
mBERT / 東北大 | 未知語トークン。語彙になく分解もできない文字を1個にまとめて落とす(元が何か復元不可) |
▁ (U+2581) |
Mistral | 空白マーカー。語の先頭や空白位置を表す |
## |
mBERT / 東北大 | 継続サブワード印。前の片に空白なしでくっつくことを表す |
ここで大きな対比が見える。バイト系(tiktoken / Mistral)は未知文字を「頑健に劣化」させて情報を残すのに対し、WordPiece系は [UNK] で「情報ごと欠落」させる。設計思想が違うのだ。
実測:日本語は英語の何倍か
同じ意味の文を日英で用意し、トークン数比(日本語÷英語)を測った。1.0なら日英同数、大きいほど日本語が不利。
このグラフの作り方と前提(重要)
このグラフは、筆者が手で用意した日英対訳3ペア(技術文・日常会話・固有名詞を多く含む文)だけを測った結果だ。各ペアは「同じ内容を日本語と英語で書いた1文ずつ」で、トークン数比は(日本語文のトークン数)÷(同じ内容の英語文のトークン数)で計算している。使った文は記事末尾とリポジトリに全文を載せている。
つまりこれは網羅的なベンチマークではなく、ごく少数の実測例だ。値はあくまでこの3ペアでの結果であり、「あらゆる日本語文がこの倍率になる」ことを示すものではない。実際、次に見るように同じモデルでも文の種類で倍率は大きく動く。ここでは「おおよその傾向と、その幅の大きさ」を見るためのものと捉えてほしい。
この3ペアの範囲で、読み取れることがいくつかある。
第一に、この例では汎用モデルで日本語が一貫して不利だった。GPT-3.5/4(cl100k)の技術文は3.00倍、Mistralも2.58倍。
第二に、文の種類で差が大きい。同じcl100kでも、技術文3.00に対し日常会話は1.69。専門語・漢字が密な文ほど不利になる傾向が見えた。だから「日本語は◯倍」と単一の数字で語るのは正確でない。この「幅の大きさ」こそが、3ペアという少数のサンプルから言える一番確かなことだ。
第三に、この例では日本語特化の東北大BERTだけ比率が1を下回った(0.45〜0.63)。日本語が英語より安い。これは語彙に日本語を厚く積み、形態素解析と正規化を作り込んだ結果だと考えられる。逆に、このモデルは英語が割高になる。先ほどの英語の技術文を東北大BERTに通すと、なんと47トークン。同じ文をo200kは18トークンで済ませているので、約2.6倍だ。実際の分割を見ると刻まれ方がよく分かる。
■ 「We performed cross-validation ...(技術文・英語)」の分割
GPT-4o (o200k) (18): We | performed | cross | -validation | using | a | ...(ほぼ単語ごと)
BERT-ja (tohoku) (47): We | per | ##form | ##ed | c | ##ross | - | v | ##al | ##ida | ##t | ##ion | ...
東北大BERTは英語の語彙をほとんど持たないため、performed が per/##form/##ed、cross が c/##ross のように1〜数文字へ刻まれる。特化は諸刃の剣で、得意言語の外では激しく損をする。
実測:表記が変わるだけでトークン数が跳ねる
ここが「日本語の語彙がきれいに割り当てられていない」ことの最も分かりやすい証拠だ。同じ意味・同じ読みでも、表記を変えるとトークン数が2〜3倍に跳ねる。
まずトークン数だけを見る。入力したのは「コンピュータ」(全角カタカナ)と「コンピュータ」(半角カタカナ)の2つで、読みも意味も同じものだ。
入力A: コンピュータ (全角カタカナ・6文字)
入力B: コンピュータ (半角カタカナ・7文字)
モデル 入力A 入力B
GPT-4o (o200k) 4 12
GPT-3.5/4 (cl100k) 6 14
Mistral 7 22
mBERT 5 [UNK] 1個に集約
BERT-ja (tohoku) 1 1
半角カナにしただけで、汎用モデルはトークン数が2〜3倍に膨れ上がる。なぜこうなるのか、実際にどんなトークンが出てきたかを見てみる。以下は各トークナイザが返した生のトークン列で、| が区切りだ。
■ 入力B「コンピュータ」の生トークン列
GPT-4o (o200k) (12): � | � | � | � | � | � | ゚ | � | � | ー | � | �
cl100k (14): � | � | � | � | � | � | � | � | � | � | � | � | � | �
Mistral (22): ▁ | <0xEF> | <0xBD> | <0xBA> | <0xEF> | <0xBE> | <0x9D> | <0xEF> | <0xBE> | <0x8B> | ...(以下バイトが続く)
mBERT ( 1): [UNK]
BERT-ja (tohoku) ( 1): コンピュータ
ここに出てくる記号がそのまま各トークナイザの設計を表している。GPT系の � は「マルチバイト文字の途中の1バイトだけを表示しようとした印」、Mistralの <0xEF> は「語彙にない文字を生のUTF-8バイトに落とした印」だ。日本語1文字は3バイトなので、半角カナ1文字がバイト3つに散る。mBERTは分解すらできず [UNK](未知語)1個に丸めてしまい、元が何だったか復元できない。一方で東北大BERTだけは半角カナを正規化して「コンピュータ」1トークンに収めている。
生の列は読みにくいので、バイト断片を文字に畳んで「どの文字が何トークンに割れたか」を示したのが次の表示だ。‹n› は「この1文字がnトークンに分割された」ことを表す(トークン数自体は上の生の列と同じで、盛ってはいない)。
■ 入力B「コンピュータ」を読みやすく畳んだ表示
GPT-4o (o200k) (12): コ‹2› | ン‹2› | ヒ‹2› | ゚ | ュ‹2› | ー | タ‹2›
cl100k (14): コ‹2› | ン‹2› | ヒ‹2› | ゚‹2› | ュ‹2› | ー‹2› | タ‹2›
Mistral (22): ▁ | コ‹3› | ン‹3› | ヒ‹3› | ゚‹3› | ュ‹3› | ー‹3› | タ‹3›
mBERT ( 1): [UNK]
BERT-ja (tohoku) ( 1): コンピュータ
Mistralでは半角カナ1文字がすべて3トークン(=3バイト)に分解されているのが見て取れる。東北大BERTは半角カナを正規化して、全角でも半角でも1トークン。つまり「日本語が高いのは宿命」ではなく、正規化と語彙設計で手当てできる問題だと分かる。
実測:ふつうの一文で5モデルを並べる
掴みでは技術文をGPT-4oで見た。今度は特殊な表記を含まないふつうの一文を、5モデルすべてに通して並べてみる。設計思想の違いが一望できる。
入力:佐藤さんは横浜から東京の本社まで新幹線で通勤している。
‹n› は「この1文字がnトークンに割れた」ことを表す(バイト断片を文字に畳んで可視化したもので、トークン数自体は正確)。
GPT-4o (o200k) (22): 佐|藤|さん|は|横|浜|から|東京|の|本|社|まで|新|幹‹2›|線|で|通|勤|して|いる|。
cl100k (35): 佐‹2›|藤‹3›|さん|は|横‹3›|浜‹2›|から|東‹2›|京|の|本|社|ま|で|新|幹‹2›|線‹3›|で|通|勤‹2›|して|い|る|。
Mistral (34): ▁|佐|藤|さ|ん|は|横|浜‹3›|か|ら|東|京|の|本|社|ま|で|新|幹‹3›|線|で|通|勤‹3›|し|て|い|る|。
mBERT (21): 佐|藤|さん|##は|横|浜|から|東|京|の|本|社|まで|新|幹|線|で|通|勤|している|。
BERT-ja (tohoku) (17): 佐藤|さん|は|横浜|から|東京|の|本社|まで|新|幹線|で|通勤|し|て|いる|。
同じ一文が、cl100kでは35トークン、東北大BERTでは17トークンと2倍以上の開きがある。差の出どころは2つある。ひとつはバイト分解で、cl100kでは「藤」が3トークン、Mistralでも「浜」「幹」「勤」が3トークンに散っている。もうひとつはまとめる単位で、東北大BERTは「佐藤」「横浜」「東京」「新幹線」を形態素ごとに1〜2トークンへまとめている。固有名詞や熟語を語彙に持つかどうかが、ここで効いてくる。
実測:絵文字と珍しい漢字はどうなるか
語彙に載りにくいものを入れると、設計差がさらにはっきり出る。2例を見る。
入力1:最高の一日でした😄🎉
入力2:鬱蒼とした森を廣島で見た
■ 入力1「最高の一日でした😄🎉」
GPT-4o (o200k) ( 9): 最高 | の | 一 | 日 | でした | 😄‹2› | 🎉‹2›
cl100k (12): 最 | 高 | の | 一 | 日 | で | した | 😄‹2› | 🎉‹3›
Mistral (11): ▁ | 最 | 高 | の | 一 | 日 | で | し | た | 😄 | 🎉
mBERT ( 6): 最 | 高 | の | 一 | 日 | [UNK]
BERT-ja (tohoku) ( 6): 最高 | の | 一日 | でし | た | [UNK]
絵文字は、GPT系では2〜3トークン(バイトに分解)、Mistralでは1トークン(語彙に含む)に収まるが、BERT系2モデルでは絵文字が [UNK] になり、情報そのものが消える。「😄🎉」がどんな絵文字だったかは復元できない。生成より分類・解析に使うBERT系では絵文字を捨てる設計、と読める。
■ 入力2「鬱蒼とした森を廣島で見た」(鬱・廣は常用外)
GPT-4o (o200k) (13): 鬱‹2› | 蒼‹2› | と | した | 森 | を | 廣 | 島 | で | 見 | た
cl100k (19): 鬱‹3› | 蒼‹3› | と | した | 森‹2› | を | 廣‹2› | 島‹3› | で | 見 | た
Mistral (19): ▁ | 鬱‹3› | 蒼‹3› | と | し | た | 森 | を | 廣‹3› | 島 | で | 見 | た
mBERT (10): 鬱 | 蒼 | とした | 森 | を | 廣 | 島 | で | 見 | た
BERT-ja (tohoku) (12): 鬱 | ##蒼 | と | し | た | 森 | を | 廣 | ##島 | で | 見 | た
「鬱」「蒼」のような常用外の漢字は、GPT系・Mistralでバイトに散って2〜3トークンを食う。興味深いのは、この例ではmBERT(10トークン)が最も短いこと。mBERTは多言語の漢字を語彙に広く持つため、1文字1トークンで収められている。「どのモデルが常に効率的」とは言えず、入れる文字種で順位が入れ替わる。
世代で変わる:cl100k → o200k
注目したいのは、同じOpenAIでも世代で日本語の効率が大きく変わった点だ。
技術文の日英比は、cl100kの3.00からo200kの1.67へ縮んだ。o200k は語彙を約20万に拡張し、多言語のサブワードを増やした。その結果、日本語が以前より安く済むようになっている。
これは記事全体で一番言いたいことでもある。「日本語は高い」は固定された宿命ではなく、トークナイザの世代とともに縮みうる設計上の問題だということだ。
精度に効くのか(仮説)
🟠 ここからは筆者の仮説で、本記事では精度そのものは測っていない。
横‹3› のように1文字が3つのバイト断片に散ると、モデルは意味のまとまりではなく生バイトを見ることになる。直感的には、(a) 系列長が伸びて注意が薄まる、(b) 珍しい字ほど学習機会が少ない、といった理由で理解に不利が出てもおかしくない。ただし現代の大規模モデルは文脈から意味を復元する力が強く、「トークン化が粗い=精度が低い」と単純化はできない。
🟠 同様に、「日本語の語彙のきれいさ」は学習データの量と前処理の作り込みの写し鏡ではないか、とも考えている。東北大BERTだけが半角カナを正規化できるのは、日本語コーパスと前処理を作り込んだからだ。裏返せば、汎用モデルの日本語の粗さは、日本語データの相対的な薄さと前処理の省略の現れ、という見立てになる。いずれも仮説として置いておく。
まとめ:いま日本語プロンプトは実質何割増しか
- 日本語が英語よりトークンを食うのは事実。だが倍率は文とモデルで1.05〜3.00倍と幅があり、一律ではない。
- 最新世代のGPT-4o(o200k)では、技術文で1.67倍、日常会話なら1.07倍まで縮んでいる。従量課金で日本語が割高なのは本当だが、その差は世代ごとに着実に小さくなっている。
- 表記揺れ(半角カナ・全角)でトークン数が2〜3倍に跳ねるのは、語彙設計で手当て可能な問題。プロンプトを書くときは全角・正規化された表記を使うだけでトークンを節約できる余地がある。
トークナイザは普段 tokenizer(text) の一行で無意識に通り過ぎる部分だが、開けてみると各社の設計思想とコスト構造がそのまま詰まっている。日本語で書くときの「実質何割増し」を知っておくと、モデル選定にもプロンプト設計にも効いてくる。
付録:測定方法(再現用)
- 測定環境:Python 3.10 /
tiktoken/transformers/sentencepiece/fugashi+unidic-lite - 対象:GPT-4o(o200k_base)、GPT-3.5/4(cl100k_base)、Mistral-7B、mBERT(bert-base-multilingual-cased)、東北大BERT(cl-tohoku/bert-base-japanese-v3)
- トークン数は各トークナイザの
tokenize()/encode()の長さ。PyTorch本体は不要(トークナイザのみ使用)。 - サンプルは日英対訳3ペア+表記揺れ6+崩壊サンプル3の計15文。少数の実測例であり、網羅的ベンチマークではない点に注意。
Claudeのトークナイザは公開されていないため、本記事の比較からは外している(APIのトークンカウント機能を使えば数えられる)。
測定スクリプトとサンプルTSVはこちら。
→リポジトリ作成中
でき次第、ここにURLを追加します m(_ _)m


