MiniMind — 小さな言語モデルを最初から最後まで自分で学習する
パディングが捨てる計算を測り、コーパスをパッキングする
目標
基準のトークナイザーで事前学習コーパスを切り、MiniMind式のパディングが計算の何%を捨てているかを測り、文書を[bos] … [eos]で包んでつなげたuint16配列(パッキング)で、学習・検証データを作ります。検証の漏れ、文書の境界、学習予算まで、数字で確認します。
なぜ重要なのか
行列の積は、パディングかどうかを知りません。パディング位置は損失から外れるだけで、計算はそのまま行います。文書が短いのに最大長を長くすると、GPUは忙しいのに学習するトークンは少なくなります。パッキングは、その無駄をなくす代わりに、1つのウィンドウに複数の文書を入れて、後ろの文書が前の文書を見られるようにします。ほとんどの事前学習が受け入れているトレードオフです。 データを準備する段階の間違いは、学習が終わってはじめて表に出ます。検証文書が学習側に混ざると検証損失が嘘をつき、1学習ステップのトークン数を知らないと損失曲線を読めません。そのため、学習の前に数字で確認しておきます。
ステップ
/opt/mm/ref/tokenizer.jsonで/opt/mm/data/pretrain.jsonlの文書ごとにトークン数を数え、docs・mean・max・p95として書いてください(出力先: /root/mm/data/lengths.json)。- MiniMindの
PretrainDatasetのように、文書1つを[bos] + 토큰(최대 126) + [eos]の形で包み(プレースホルダーはトークンで、最大126個です)、長さ128までパディングすると、パディングがいくつになるかを、max_len・rows・pad_tokens・pad_fraction・truncated_docsとして書いてください(出力先: /root/mm/data/padding.json)。 - 学習コーパスを、文書ごとに
[bos](1) 토큰들 [eos](2)の形で包んでつなげた(プレースホルダーはトークン列です)uint16配列を保存してください(出力先: /root/mm/data/train.npy)。 - 同じ方法で
/opt/mm/data/pretrain_val.jsonlを保存してください(出力先: /root/mm/data/val.npy)。 - 検証文書のうち、学習コーパスにそのまま含まれているものの数を、
val_docs・dup_in_trainとして書いてください(出力先: /root/mm/data/leak.json)。 train.npyを128トークンずつ重ならないように切ったウィンドウで、ウィンドウ1つあたりの平均文書数と、前の文書を見られるトークンの割合を、seq_len・windows・docs_per_window・cross_doc_fractionとして書いてください(出力先: /root/mm/data/windows.json)。- バッチ16・長さ128のとき、1学習ステップのトークン数と、1エポックの学習ステップ数を、
train_tokens・tokens_per_step・steps_per_epochとして書いてください(出力先: /root/mm/data/budget.json)。 ## 패딩## 패킹## 검증 분리の3つのセクションを書き、ステップ2のpad_fractionとステップ6のcross_doc_fractionを数字で入れてください(出力先: /root/mm/data/report.md)。韓国語の見出しは、順に「パディング」「パッキング」「検証データの分離」という意味です。
参考
- 切るときは
tok.encode(글, add_special_tokens=False).ids、保存はnumpy.save(경로, numpy.array(ids, dtype=numpy.uint16))です(プレースホルダーは、順に文章とパスです)。 - よくある間違い: 文書をつなげる前に
[bos]と[eos]を付け忘れること(採点ツールが作り直した配列と長さが変わります)、int64で保存すること、ウィンドウの最初のトークンが[bos]の場合まで「境界を越えた」と数えること。 - 原典: MiniMind lm_dataset.py — PretrainDataset · numpy.save
文書が何トークンかを測る
/opt/mm/ref/tokenizer.jsonで/opt/mm/data/pretrain.jsonlのtextを文書ごとに切り、文書数・平均・最大値・95パーセンタイル(numpy.percentile(길이, 95))を、docs・mean・max・p95として書いてください(プレースホルダーは長さです。出力先: /root/mm/data/lengths.json)。
平均と最大値だけを見ても、パディングがどれだけ生まれるかの見当がつきます。最大長を最大値に合わせれば何も切れませんが、平均の長さの文書は、残りをすべてパディングで埋めることになります。
MiniMind式のパディングが捨てる割合
文書1つを[bos] + 토큰(126개까지 자름) + [eos]の形で包み(プレースホルダーはトークンで、126個までに切ります)、長さ128までパディングするとき、パディングトークンの数と、全体(文書数×128)に占める割合、切られた文書の数を、max_len・rows・pad_tokens・pad_fraction・truncated_docsとして書いてください(出力先: /root/mm/data/padding.json)。
MiniMindのPretrainDataset.__getitem__が、この計算をそのまま行います。max_length - 2で切り、bosとeosを付けて、残りをpadで埋めます。パディングは損失から外れますが(-100)、行列の積は、その位置まで計算します。
学習コーパスを1本につなげる
スクリプト(/root/mm/data/pack.py、引数: 入力jsonl、保存先の.npy)を書いて、/opt/mm/data/pretrain.jsonlの文書をファイルの順に1(bos) 토큰들 2(eos)の形で包んでつなげ(プレースホルダーはトークン列です)、uint16の配列として保存してください(出力先: /root/mm/data/train.npy)。
ファイルの順を変えたりシャッフルしたりすると、採点ツールが作り直した配列と変わってしまいます。シャッフルは、学習ループがバッチを選ぶときに行います。語彙が1024なのでuint16(最大65535)で十分で、int64の4分の1の大きさです。
検証コーパスも同じ方法で作る
/opt/mm/data/pretrain_val.jsonlを、ステップ3と同じ方法でパッキングして保存してください(出力先: /root/mm/data/val.npy)。
検証データは、学習と同じ方式で作らないと、2つの損失を比べられません。ステップ3のスクリプトが入力・出力のパスを引数で受け取るようにしておけば、1行で済みます。
検証文書が漏れていないか確かめる
検証文書(pretrain_val.jsonlのtext)のうち、学習コーパス(pretrain.jsonl)にそのまま含まれているものの数を、val_docs・dup_in_trainとして書いてください(出力先: /root/mm/data/leak.json)。
学習側のtextを集合(set)にしておけば、1回ずつ探すだけで済みます。0でなければ、その文書の検証損失は、暗記したものを測っています。
1つのウィンドウの文書数と、境界を越えるトークンの割合を見る
train.npyを128トークンずつ重ならないように切り(余った末尾は捨てます)、ウィンドウ1つあたりの平均[bos]の数(docs_per_window)と、前の文書を見られるトークンの割合(cross_doc_fraction)を、seq_len・windowsとあわせて書いてください(出力先: /root/mm/data/windows.json)。あるトークンが「前の文書を見られる」とは、ウィンドウの中で、そのトークンの位置まで(自分を含む)に、文書の開始([bos])が1回以上あったという意味です。ただし、ウィンドウの最初のトークンが[bos]なら、その文書はウィンドウの中で最初に始まったものなので、数えません。
numpy.cumsum(창 == 1, axis=1)で、位置ごとにそこまでに出てきたbosの数を求め(プレースホルダーはウィンドウです)、最初の位置がbosのウィンドウは1を引けばよいです。因果マスクは前のすべてのトークンを見せるので、文書ごとのマスクを別に作らないかぎり、これらのトークンは前の文書を見ます。
1エポックが何学習ステップかを計算する
バッチ16・長さ128で学習するとき、1学習ステップが処理するトークン数と、train.npy全体を1回見るのに必要な学習ステップ数(切り上げ)を、train_tokens・tokens_per_step・steps_per_epochとして書いてください(出力先: /root/mm/data/budget.json)。
次のモジュールで数百学習ステップを学習すると、コーパスを何周するかが、この数字でわかります。同じデータを何周も見ると、学習損失が検証損失より速く下がりはじめます。
データを準備した根拠を残す
## 패딩 ## 패킹 ## 검증 분리の3つのセクションを書き、ステップ2のpad_fractionとステップ6のcross_doc_fractionを、数字(小数またはパーセント)で入れてください(出力先: /root/mm/data/report.md)。韓国語の見出しは、順に「パディング」「パッキング」「検証データの分離」という意味です。
パディングとパッキングのどちらを選ぶのか、その代わりに何を受け入れるのかを、1行ずつ書いてください。検証データの分離のセクションには、ステップ5の結果を入れるとよいです。