LLMのトークン

LLM(大規模言語モデル)は文字ではなくトークン(token)というものを単位として処理します。トークンは単語に近いものですが、必ずしも単語とは一致しません。

文字列をトークンに分割するツールをトークナイザ(tokenizer)といいます。OpenAIのLLMでは tiktoken というトークナイザが使われます。tiktoken はいくつかのエンコーディング(トークン分割方式)に対応しており、古くは cl100k_base というエンコーディングが使われていましたが、GPT 4oからは o200k_base に変わりました。

pip install tiktoken して試してみましょう:

import tiktoken

enc = tiktoken.get_encoding("o200k_base")
# または enc = tiktoken.encoding_for_model("gpt-4o")

次の例で試してみましょう(山本義隆『熱学思想の史的展開』(現代数学社,1987年)より):

s = "「何人ものニュートンがいた(There were several Newtons)」と言ったのは,科学史家ハイルブロンである.同様にコーヘンは「ニュートンはつねに二つの貌を持っていた(Newton was always ambivalent)」と語っている."
e = enc.encode(s)
for i in e:
    c = enc.decode([i])
    if len(c) == 1 and ord(c) == 65533:  # 65533は「�」
        print(i, end="|")
    else:
        print(repr(c)[1:-1], end="|")
print()

次のように67トークンに分割されていることがわかります:

「|何|人|もの|ニュ|ート|ン|が|いた|(|There| were| several| Newton|s|)」|と言|った|の|は|,|科学|史|家|ハ|イル|ブ|ロン|で|ある|.|同|様|に|コ|ー�|246|ン|は|「|ニュ|ート|ン|は|つ|ね|に|二|つ|の|貌|を|持|って|いた|(|Newton| was| always| amb|ivalent|)」|と|語|って|いる|.|

日本語は必ずしもUTF-8の文字の境界で切れるわけではなく、上の「コーヘン」のように文字の途中で切れることもあります。

別の例です。これらは文字単位の処理が苦手なLLMの弱点を突くプロンプトとして有名なものです:

How| many| r|'s| are| in| strawberry|?|

「|いっぱい|」の|「|い|」を|「|お|」に|変|えて|ください|。|

このように、" strawberry" や "いっぱい" は1トークンになるので、その中にどういう文字が含まれるかは別に学習していないとこれらの問題は解けません。

日本語ではどんなものがトークンとして登録されているのでしょうか。ちょっと調べてみました。

import tiktoken
import re

enc = tiktoken.get_encoding("o200k_base")

japanese_re = re.compile('[\u3000-\u30ff\u4e00-\u9fff]')

# 3000-30ff CJK記号と句読点、ひらがな、カタカナ
# 4e00-9fff CJK統合漢字
## 31f0-31ff カタカナ追加
## 3220-325f, 3280-33ff CJK文字いろいろ
# 3400-4dbf CJK統合漢字拡張A
# f900-faff CJK互換漢字

words = {}
for i in range(199998):
    s = enc.decode([i])
    words[i] = s
sorted_words = sorted(words.items(), key=lambda item: len(item[1]), reverse=True)
# for i, s in sorted_words[:100]:
#     print(i, s)

for i, s in sorted_words[:100000]:
    if japanese_re.search(s):
        print(i, repr(s)[1:-1])

一部を抜き出します:

(前略)
113862 ありがとうございました
181081  微信公众号天天中彩票
185118 _日本毛片免费视频观看
93926 ありがとうございます
147058 VIPがお送りします
170996  微信上的天天中彩票
187716  微信里的天天中彩票
188394  天天中彩票大神推荐
46669  天天中彩票app
55935  彩神争霸大发快三
(中略)
77298 @お腹いっぱい
(中略)
87123 風吹けば名無し
(中略)
123086 がお送りします
(中略)
44948  名無しさん
(後略)

何を学習させたのか想像がついてしまいます。

中国語のものは微信(WeChat)の宝くじ関係のものが多いようですが、アダルト関係も混ざっているようです。

「_日本毛片免费视频观看」は「_日本ポルノ無料動画視聴」という意味ですが、不思議なことにChatGPTはこのトークンが読めないようです。「_日本毛片免费视频观看」を日本語に訳してくれと言っても何も見えないらしいのです。旧トークナイザでは「SolidGoldMagikarp」などがChatGPTが読めないトークンとして有名でしたが、新トークナイザでも同じようなことが起こっているのかもしれません。


大規模言語モデル PLaMo 2 のためのトークナイザ性能改善という記事を見て、PLaMo 2のトークナイザも試してみたくなりました。

from mlx_lm import load, generate

model, tokenizer = load("mlx-community/plamo-2-8b-bf16",
                        tokenizer_config={"trust_remote_code": True})
tokenizer.add_eos_token("<|plamo:bos|>")
s = "「何人ものニュートンがいた(There were several Newtons)」と言ったのは,科学史家ハイルブロンである.同様にコーヘンは「ニュートンはつねに二つの貌を持っていた(Newton was always ambivalent)」と語っている."

e = tokenizer.encode(s)
for i in e:
    c = tokenizer.decode(i)
    print(repr(c)[1:-1], end="|")
print()
<|plamo:bos|>|「|何人|も|の|ニュー|トン|がいた|(|There were| several| Newton|s|)」|と言った|の|は,|科学|史|家|ハイ|ル|ブロン|である|.|同様に|コー|ヘン|は「|ニュー|トン|は|つねに|二つ|の|貌|を持っていた|(|Newton| was always| |ambi|valent|)|」と|語っている|.|
words = {}
for i in range(100000):
    s = tokenizer.decode(i)
    words[i] = s
sorted_words = sorted(words.items(), key=lambda item: len(item[1]), reverse=True)
# for i, s in sorted_words[:100]:
#     print(i, s)
for i, s in sorted_words[:10000]:
    if japanese_re.search(s):
        print(i, repr(s)[1:-1])
58984 いただきありがとうございました。
68757 あけましておめでとうございます。
54912 いただきありがとうございます。
66397 明けましておめでとうございます
51379 お気軽にお問い合わせください
59328 本当にありがとうございました
63073 してみてはいかがでしょうか。
74903 いつもありがとうございます。
78833 いただきありがとうございます
81104 ことができるようになります。
85013 もいるのではないでしょうか。
50286 よろしくお願いいたします。
57225 させていただいております。
64602 お気軽にお問い合わせ下さい
70922 することをおすすめします。
75898 できるようになっています。
76399 というわけではありません。
85032 いつもありがとうございます
48448 ありがとうございました。
58809 できるようになりました。
58983 心よりお待ちしております
61422 みてはいかがでしょうか。
61959 するものではありません。
65294 よろしくお願いいたします
65859 お気軽にお問合せください
66682 頂きありがとうございます
68465 させていただきますので、
70634 をさせていただきました。
70678 YouTubeチャンネル
75441 させていただいています。
(後略)

OpenAIのものと比べて、とてもまともです。