「トークン数が足りなくなった」「日本語だとなんか料金が高い気がする……」──LLMを使い始めると、そんなモヤモヤが出てきますよね。その謎を解くカギが、トークナイザーです。今回はトカゲ先生とヤモリくんの会話を追いながら、AIが日本語をどう「読んで」いるのかをひも解いていきましょう。
そもそもトークナイザーとは? ─ AIは文章を「かたまり」に区切って読む
「ヤモリくん、ChatGPTとか使ってて『トークン』って言葉、見たことある?」

「あります! でも結局なんのことかよくわからなくて……AIって私が送った文章をそのまま読んでるんじゃないんですか?」
「実はそうじゃないんだよ。AIは文章を直接読んでいるわけじゃなくて、まず文章を細かいかたまりに分解して、それを数字の列に変換してから処理してるんだ。その『分解する仕組み』がトークナイザーだよ。」

「え、数字に変換? どういうことですか?」
「たとえば『I love cats』っていう英文を例にしよう。トークナイザーはこれを `[“I”, ” love”, ” cats”]` みたいなかたまり(=トークン)に分割して、それぞれに番号をつける。`I=40`、`love=1842`、`cats=11222` みたいにね。モデルが実際に見ているのは `[40, 1842, 11222]` っていう数字の列なんだ。」


「なるほど! 単語帳みたいに番号と言葉が対応してる感じですね。」
「そうそう、まさに! その番号表を語彙(ボキャブラリ)って呼ぶよ。で、トークンって単語そのものとは限らなくて、単語の一部だったり、記号だったり、いろんな形がある。その話が次のセクションのメインテーマ。」

英語の目安として、1トークンはおよそ4文字、単語でいうと約4分の3個分に相当します。100トークンで大体75単語、ちょうど短い段落ひとつ分くらいのイメージです。
サブワードという発想 ─ 単語でも文字でもない「ちょうどいい」区切り方
単語ごと・文字ごとに区切るとなぜ困るの?
「トークンって単語ごとに区切ればいいんじゃないですか? 一番わかりやすいし。」
「一見そう思うよね。でも単語単位には大きな弱点があって、`love`・`loving`・`loved` をぜんぶ別々のトークンとして登録しないといけないんだよ。英語だけでも単語の変化形は無限にあるから、必要な語彙が爆発的に増える。そして語彙にない単語が出てきたら『知らない単語』を意味する `` という特殊トークンに丸ごと置き換えるしかない。これが未知語問題ってやつだ。」

「じゃあ逆に、1文字ずつ区切れば語彙は小さくて済みますよね?」
「頭いい発想! でも今度は別の問題が出る。`love` を `l`・`o`・`v`・`e` の4つに分けたら、1文字1文字はほとんど意味を持たないよね。しかも文章全体のトークン数が一気に長くなって、AIにとって扱いにくくなる。精度も落ちやすい。」

「単語単位はデカすぎ、文字単位は細かすぎ……ちょうどいい中間がほしいですね。」
「そこで登場するのがサブワード方式。よく使う単語はそのまま1トークンにしつつ、めずらしい単語や知らない単語は意味のある部品に分解するアプローチだよ。たとえば `annoyingly` は `[“annoying”, “ly”]` や `[“annoy”, “ing”, “ly”]` みたいに分割される。」


BPEはどうやって区切りを覚えるの?
「その『どう分割するか』はどうやって決めるんですか?」
「代表的な手法が BPE(Byte Pair Encoding/バイトペアエンコーディング) だよ。もともとデータ圧縮の技術で、2016年の機械翻訳の研究でNLPに持ち込まれたんだ。仕組みはこんな感じ。」

- まず全文字をバラバラにする(
l,o,v,eのように) - 一番よく隣り合うペアをひとつのトークンにマージする(例:
l+o→lo) - 2を繰り返す。決めた語彙サイズに達するまでひたすら続ける
「つまり、たくさん出てくる組み合わせほど早く『合体』して、1つのトークンになるわけですね!」
「完璧な理解! 圧縮技術の転用って聞くとなるほど、って感じだよね。よく出るかたまりほど1つにまとめる、という発想がそのまま使われてるから。」

見たことのない単語(未知語)はどう扱われるの?
「GPT-2が使っているバイトレベルBPEはさらに巧妙で、アルファベット26文字じゃなくてコンピュータが扱う256種類のバイト値を出発点にするんだ。どんな文字もバイトに分解できるから、理論上どんな言葉でも `` を出さずにトークン化できる。語彙サイズは50,257個になってる。」

「256から始めて、マージを重ねて5万超え! 積み木を組み合わせるイメージですね。」
日本語ならではの難しさ ─ 空白がない言語をどう区切るか
日本語はなぜ英語より区切りにくいの?
「さて、ここからが本題の日本語の話だよ。」

「日本語って何が難しいんですか? 難しい漢字が多いから?」
「文字の複雑さもあるけど、もっと根本的な問題がある。英語をよく見てみて。`I love cats` ──単語の間にスペースがあるよね。でも日本語の『わたしはねこがすき』にはスペースがない。BPEのような仕組みは『スペースで区切られている』ことを前提にしてるものが多いから、そのままでは日本語に使えないんだ。」

「言われてみれば……日本語って確かに全部くっついてますね!」

「形態素解析」ってなに?MeCabって何をする道具?
「そこで登場するのが形態素解析という技術。文を、意味を持つ最小単位の『形態素』に分割して、品詞なども判定するんだ。日本語の場合はまずここが必要になる。」

「形態素……むずかしそう。」
「『東京都に住んでいる』という文で考えてみよう。これは `東京/都/に/住ん/で/いる` みたいに分割できるよね。でも機械には最初から区切りが見えない。MeCabはそれを自動でやってくれる、代表的なオープンソースの形態素解析ツールだよ。」

「MeCabはどうやって区切り方を決めるんですか?」
「大規模な辞書と、確率的な学習モデルを使うんだ。たとえば『東京都』は『東京/都』と切るのが自然か、『東/京都』と切るのが自然か、辞書と確率で判断する。機械が最も確からしい区切り方を選ぶイメージだよ。」

「ああ、確かに『東/京都』だと『東と京都』になっちゃいますもんね(笑)」
SentencePieceは他のトークナイザーと何が違うの?
「MeCabみたいな形態素解析が先にないとトークン化できないなら、毎回準備が大変じゃないですか?」
「鋭い! そこでSentencePieceという別アプローチが生まれた。2018年の論文で発表されたツールで、事前の分かち書きなしに生のテキストから直接BPEやUnigramを適用できるんだ。」

「それのどこがすごいんですか?」
「空白を『`▁`(アンダースコアに似た記号)』という特殊なトークンとして語彙に含めちゃうんだよ。こうすることで空白があってもなくても同じように扱える。つまり言語に依存しないトークナイザーとして日本語でも中国語でもそのまま使えるし、トークンから元の文章に戻すことも正確にできる。」


「なるほど、スペースを『特別な文字』として取り込んじゃう発想か。頭いい……!」
ちなみにトークンに分解された各単語が持つ意味の表現については、【自然言語処理】単語ベクトルとは?で詳しく解説していますよ。
なぜ日本語はトークン数が多くなるのか ─ 英語との違いを実例で見る
同じ内容でも英語と日本語でトークン数はどれくらい違うの?
「先生、よく『日本語だとトークンをたくさん消費する』って聞くんですが、どのくらい違うんですか?」
「データで見るとかなりはっきりした差が出るよ。英語は1トークンあたり約4.75文字をカバーできる。でも日本語は1トークンあたり約1.41文字しかカバーできない。200万件以上の翻訳文を比較した分析では、日本語は英語のおよそ2.12倍のトークン数になるという結果が出てる。」

「2倍以上! 漢字とかなが混ざる文章だとさらに差が広がることもあるって聞きましたが……」
「そう、最大で約8倍になるケースも報告されてる。ちなみに同じアジア言語でも差があって、中国語は英語の約1.76倍、韓国語は約2.36倍だよ。」


なぜ漢字やかなはトークンを多く消費するの?
「なんでそんなに差が出るんですか?」
「理由は大きく2つあるよ。まずバイト数の違い。GPTのトークナイザーはバイトレベルで動いてるって話をしたよね。英語のアルファベットは1文字1バイトで済むけど、日本語の文字は1文字がおおむね3バイト必要なんだ。つまりスタート地点の細かさが違う。」

「ということは、日本語は最初から英語の3倍細かく分解された状態から始まるんですね。」
「そう! そしてもう一つの理由が語彙の偏り。GPT-3.5/4で使われるトークナイザーの語彙は約10万種類あるんだけど、Unicodeで定義されているCJK(漢字)の文字だけで9.7万字以上ある。漢字が語彙に入りきらなくて、多くがバイト列のまま細かく区切られてしまうんだ。」

「たとえばどのくらいバラつくんですか?」
「面白い例があって、漢字1文字でも全然違う。『猫』は3トークン、『三』は1トークン、『覚』は2トークンって感じで、文字によってバラバラなんだよ。」

「え、猫が3トークン!? 三が1トークンなのに!」
「笑えるよね。よく使われる漢字や単純な漢字はうまくマージされてトークンになってるけど、そうでない文字はバイトに分解されてしまうから差が出る。」

トークン数が多いと料金や使い勝手にどう響くの?
「APIはトークン単位で課金される。つまり同じ内容でも日本語で送るとコストが増えやすいということになる。日本語は最も『高くつく』言語の一つとも言われてるよ。」

「うわ、知らなかった……でも、それって解決できないんですか?」
「ちゃんと希望はあるよ! これは永遠の宿命じゃなくて、日本語データを十分に使って学習・最適化されたトークナイザーを使えば、日本語のトークン数は大幅に減らせる。実際に日本語特化モデルの中には、日本語を英語より少ないトークン数で処理できるものもある。」

「専用設計って大事なんですね。」
トークン数と上手につきあう ─ 数え方と実践のコツ
自分の文章のトークン数はどうやって確認するの?
「じゃあ実際に自分の文章が何トークンか確認したいときはどうすればいいですか?」
「2つ方法があるよ。1つ目はOpenAIのTokenizerツール(platform.openai.com/tokenizer)で、ブラウザから文章をペーストするだけでトークンを色分け表示してくれる。直感的でわかりやすい。2つ目はPythonライブラリの`tiktoken`を使う方法で、コードの中でトークン数を正確に数えられるから、自動化したいときに便利だよ。」


「プログラムがわからなくてもブラウザツールで試せるのは安心ですね。」
コンテキストウィンドウとトークン数はどう関係するの?
「トークン数はコスト以外にも影響があるって聞きましたが……」
「そう、もう一つ大事なのがコンテキストウィンドウだよ。モデルが一度に扱える入力+出力のトークン数の上限のことで、これを超えると古い会話が押し出されたり、エラーになったりする。長い文章を送るほど、この上限を早く消費するわけ。」

「つまりトークン数は『使えるお金(コスト)』と『使える紙のスペース(ウィンドウ)』の両方を削っていくんですね!」
「完璧な比喩! そのイメージで覚えておいて。」

なお、モデルが情報を処理するとき「どこに注目するか」という仕組みについては 注意機構(Attention)を5分で掴む ─ 翻訳タスクで追う「どこを見るか」の仕組み で詳しく解説しています。コンテキストウィンドウ内のトークンを「どう読むか」が気になった方はぜひ。
トークンを意識すると何が変わる?(まとめ)
最後に、今日のポイントをまとめておきましょう。
「トークナイザーについて一番覚えてほしいことを3つにまとめると?」
「こんな感じだよ。」

| ポイント | 内容 |
|---|---|
| ① AIは数字を読んでいる | 文章はトークンに分解され、整数IDに変換されてから処理される |
| ② サブワードがちょうどいい | 単語単位・文字単位の弱点を補う「いいとこ取り」の方式がBPEなどで実現 |
| ③ 日本語は英語より消費が多い | バイト数の多さと語彙の偏りで、同じ内容でも日本語は英語の約2倍以上のトークンを使う |
「あと一つ気をつけてほしいのが、『1トークン≒4文字』『1トークン≒0.75単語』っていう目安は英語向けの話だってこと。日本語・コード・JSONなどでは全然当てにならない。正確に知りたいときは思い込まずに、ツールで実際に数えてみてね。」

「日本語でLLMを使うなら、まず自分の文章のトークン数を見てみることから始めればいいんですね!」
「そう! 数えてみると、意外なところでトークンを食ってることに気づくと思う。そこから工夫が始まるよ。」

トークナイザーは地味に見えて、LLMの性能・コスト・使い勝手に直結する重要な仕組みです。「なんとなく使っていた」から「仕組みを知って使う」に一歩進むだけで、プロンプトの組み立て方も変わってきます。ぜひ今日のうちにTokenizerツールで自分の文章を試してみてください!
参考文献
- https://huggingface.co/docs/transformers/en/tokenizer_summary
- https://learn.microsoft.com/en-us/dotnet/ai/conceptual/understanding-tokens
- https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them
- https://huggingface.co/papers/1508.07909
- https://mbrenndoerfer.com/writing/byte-pair-encoding-subword-tokenization-guide
- https://www.tech-teacher.jp/blog/mecab/
- https://www.kikagaku.co.jp/personal/blog/morphological-analysis
- https://dblp.org/rec/conf/emnlp/KudoR18.html
- https://tonybaloney.github.io/posts/cjk-chinese-japanese-korean-llm-ai-best-practices.html
- https://towardsdatascience.com/why-openais-api-is-more-expensive-for-non-english-languages-553da4a1eecc/
- https://dylancastillo.co/til/counting-tokens.html
- https://platform.openai.com/tokenizer
関連記事

【自然言語処理】単語ベクトルとは?
こんにちは! zhackです!私は過去に自然言語処理を用いたプロジェクトに参画していました。その時に学んだことをアウトプットしたいなぁと思ったので、今回から自然言語処理について記事を投稿していきます。現状、以下の記事を想定してます。 単語ベ...

注意機構(Attention)を5分で掴む ─ 翻訳タスクで追う「どこを見るか」の仕組み
注意機構(Attention)とは、翻訳などのタスクで「入力文のどの単語に、どれだけ注目するか」をスコアで数値化し、その割合に応じて情報を混ぜ合わせることで、必要な文脈を動的に参照できる仕組みである。従来のRNNが固定サイズのカバンに情報を...

コメント