MiniMind — 小さな言語モデルを最初から最後まで自分で学習する
MiniMind のトークナイザーで韓国語を測り、小さな辞書を自分で学習する
目標
MiniMindが配布する語彙6,400のトークナイザーで韓国語コーパスを切って、1文字あたりのトークン数を測り、同じ組み立て(バイトレベルBPE)で語彙1,024の辞書を学習して比べます。語彙サイズが圧縮率と埋め込みの割合をどう変えるかを、数字で残します。
なぜ重要なのか
トークン数は計算量で、コンテキストウィンドウの大きさです。同じ文章が2倍のトークンになれば、学習も推論も2倍高くなり、1回に見られる文章は半分になります。ところが、辞書はモデルと一体で、学習を始めたあとは変えられません。そのため、最初から学習する人だけが、この決定をします。 MiniMindの辞書は、中国語・英語のデータで学習されました。韓国語は、その辞書にとって、ほとんど初めて見る文字なので、バイト単位に切られます。このラボは、そのコストを測り、私たちのコーパスに合わせた辞書が、どれだけ短く切るかを確認します。小さなモデルで、語彙がパラメーターのどれだけを占めるかも一緒に見ます。MiniMindが6,400という小さな語彙を選んだ理由です。
ステップ
- MiniMindのトークナイザー(
/opt/minimind/model/tokenizer.json・tokenizer_config.json)の語彙サイズと、bos・eos・padのトークン、そのIDを書いてください(出力先: /root/mm/tok/minimind_vocab.json)。 - そのトークナイザーで検証コーパス(
/opt/mm/data/pretrain_val.jsonlのtext)を切り、文字・バイトあたりのトークン数を書いてください(出力先: /root/mm/tok/borrowed.json)。 /opt/mm/data/pretrain.jsonlのtextだけで、バイトレベルBPE(語彙1024、特殊トークン<|endoftext|>・<|im_start|>・<|im_end|>をこの順で)を学習し、保存してください(出力先: /root/mm/tok/tokenizer.json)。- 新しいトークナイザーで
<|im_start|>user\n안녕<|im_end|>\n(韓国語で「こんにちは」を意味する語を含みます)を切り、結果を書いてください(出力先: /root/mm/tok/chat_ids.json)。 - 新しいトークナイザーで、ステップ2と同じ測定を行い、書いてください(出力先: /root/mm/tok/own.json)。
- 語彙384・512・1024の文字/トークンを書いてください(出力先: /root/mm/tok/sweep.json)。
- MiniMind-3の基本設定・私たちの小さな設定・私たちの設定に語彙6400を入れたもの、3つのモデルの埋め込みの割合を書いてください(出力先: /root/mm/tok/embed_share.json)。
## 빌려 온 토크나이저## 우리 토크나이저## 어휘 크기와 임베딩の3つのセクションを書き、ステップ2・5の文字/トークンを入れてください(出力先: /root/mm/tok/report.md)。韓国語の見出しは、順に「借りてきたトークナイザー」「私たちのトークナイザー」「語彙サイズと埋め込み」という意味です。
参考
mm-infoを実行すると、イメージに入っているMiniMindのコピー・データ・基準となる出力物の場所が、一目でわかります。- トークナイザーは
from tokenizers import Tokenizer; Tokenizer.from_file(경로)で読み込み(プレースホルダーはパスです)、切るときはencode(글, add_special_tokens=False).idsを使います(プレースホルダーは文章です)。 - よくある間違い: 特殊トークンを除いて学習し、
<|im_start|>がバイト10個に切られること、検証ではなく学習コーパスで圧縮率を測ること、共有された埋め込みと出力層を2回数えることです。 - 出典: MiniMind train_tokenizer.py · HuggingFace tokenizers — BPE · ByteLevelの事前分割
MiniMindの辞書を開く
/opt/minimind/model/tokenizer.jsonとtokenizer_config.jsonを読み、語彙サイズと、bos・eos・padのトークン文字列とそのIDを、vocab_size・bos・eos・pad・bos_id・eos_id・pad_idとして書いてください(出力先: /root/mm/tok/minimind_vocab.json)。
語彙サイズはTokenizer.get_vocab_size()、IDはtoken_to_id()です。どのトークンがbos・eos・padなのかは、tokenizer.jsonではなく、tokenizer_config.jsonのbos_token・eos_token・pad_tokenにあります。
借りてきた辞書で韓国語を測る
MiniMindのトークナイザーで/opt/mm/data/pretrain_val.jsonlのすべてのtextを切り、全体の文字数÷トークン数と、全体のUTF-8バイト数÷トークン数を、tokens・chars_per_token・bytes_per_tokenとして書いてください(出力先: /root/mm/tok/borrowed.json)。
ハングル1文字は、UTF-8で3バイトです。文字/トークンが1より小さければ、1文字を複数の断片に切ったという意味です。測定スクリプトを、トークナイザーのパスと結果のパスを引数に取るように作っておけば、ステップ5でそのまま使い回せます。
語彙1024の辞書を学習する
スクリプト(/root/mm/tok/train_tok.py、引数: 語彙サイズ、保存パス)を書いて、/opt/mm/data/pretrain.jsonlのtextだけで、MiniMindと同じ組み立て(models.BPE + pre_tokenizers.ByteLevel(add_prefix_space=False) + decoders.ByteLevel)を、vocab_size=1024、特殊トークン<|endoftext|>・<|im_start|>・<|im_end|>の順で学習し、保存してください(出力先: /root/mm/tok/tokenizer.json)。
BpeTrainer(vocab_size=…, initial_alphabet=pre_tokenizers.ByteLevel.alphabet(), special_tokens=[…])です。特殊トークンは、書いた順にID 0・1・2になります。保存したあと、get_vocab_size()が1024より小さく出るのが正常です。まとめる組が先になくなったからです。
特殊トークンが1つの断片になるのを確かめる
新しいトークナイザーで、文字列<|im_start|>user\n안녕<|im_end|>\n(韓国語で「こんにちは」を意味する語を含みます)を切り、text(その文字列)とids(トークンIDのリスト)として書いてください(出力先: /root/mm/tok/chat_ids.json)。
特殊トークンとして登録された文字列は、バイトに切られず、IDが1つになります。最初のIDが1(<|im_start|>)で、2(<|im_end|>)が1回出てくる必要があります。SFTの損失のマスキングは、このIDを目印にします。
私たちの辞書で測り直す
ステップ2と同じ測定を、新しいトークナイザー(/root/mm/tok/tokenizer.json)で行い、tokens・chars_per_token・bytes_per_tokenとして書いてください(出力先: /root/mm/tok/own.json)。
同じ検証コーパス、同じ計算でなければ、2つの数字を比べられません。私たちのコーパスは単語の種類が少ないので、語彙700個ほどでも、単語1つが1トークンになります。実際の韓国語のコーパスなら、これよりはるかに長いロングテールが残ります。
語彙を増やすとどれだけ短くなるかを見る
同じ条件で、語彙384・512の辞書も学習し、3つの辞書(384・512・1024)の検証の文字/トークンを、{"384": 값, "512": 값, "1024": 값}の形で書いてください(プレースホルダーは値です。出力先: /root/mm/tok/sweep.json)。
語彙が増えるほど文字/トークンは増えますが、ある時点で止まります。1024を与えたのに実際の語彙が何個だったか(ステップ3)を思い出せば、なぜ止まるのかがわかります。1024の値は、ステップ5の値と同じになる必要があります。
語彙がパラメーターをどれだけ食うかを見る
mmkit.new_model(설정)(プレースホルダーは設定です)で、3つのモデル、すなわちMiniMind-3の基本(MiniMindConfig()のhidden_size・num_hidden_layers・vocab_size)、私たちの小さな設定(mmkit.SMALL)、私たちの設定にvocab_size 6400を入れたものを作り、埋め込みのパラメーター数・全体のパラメーター数・割合を、minimind3・ours・ours_vocab6400として書いてください(出力先: /root/mm/tok/embed_share.json)。
MiniMindは、埋め込みと出力層(lm_head)を共有します(tie_word_embeddings)。model.parameters()は、共有されたテンソルを1回しか返さないので、その合計が全体です。埋め込みはmodel.model.embed_tokens.weightです。
辞書を選んだ根拠を残す
## 빌려 온 토크나이저 ## 우리 토크나이저 ## 어휘 크기와 임베딩の3つのセクションを書き、ステップ2とステップ5の文字/トークン(chars_per_token)を数字で入れてください(出力先: /root/mm/tok/report.md)。韓国語の見出しは、順に「借りてきたトークナイザー」「私たちのトークナイザー」「語彙サイズと埋め込み」という意味です。
借りてきた辞書が、韓国語を何倍長く切るのか、その代償が学習・推論で何なのかを、1行ずつ書いてください。最後のセクションには、私たちの規模で語彙6400を使うと、埋め込みが全体の何%になるかを入れるとよいです。