review22: ご質問への回答(トークナイザ学習ファイルの作り方 / シャードの数とサイズの決め方)と S モデル着手の段取り
作成日: 2026-09-29 対象: review21 確認事項へのご回答(1: v1 削除 OK / 2: S 着手は基本 OK、分散の念押し + 質問 2 点 / 3: サイト表現は据え置き)。
要旨
shards_v1/(12GB)を削除しました。シャードは v2 の 124 個のみになりました(1 章)- 質問 1:
sp_train.txtは全コーパスではなく、サンプルから言語別の文字数上限で切り出した 3.4 億字(コーパスの 1.3%)です。上限は「日本語の総量から他言語を決めた」のではなく、日本語がトークナイザ学習データの約 19% になるよう手で決めた定数です。これはトークナイザの語彙にだけ効き、言語モデルの学習データ(自然比率)には影響しません(2 章) - 質問 2: シャードは 1 個 = 5,000 万トークン(uint16 で約 100MB) の固定サイズで、個数は総トークン数 ÷ 5,000 万 = 122(train)。「クライアントが家庭回線で数十秒で落とせて、1 回の局所学習タスクに足る量」を狙った設計です。 val/test は量が少ないので 1 個ずつ(3 章)
- 分散の念押し、承知しました。フェーズ 1 は 3060 単独のベースライン(比較対象)、フェーズ 2 で 3060 + 1050 の API 配布による分散学習、という roadmap の順序で進めます。
zxell_model/はその両方で使う共通の学習コードです(4 章)
1. 旧シャードの削除
(省略: 保存場所のパス)shards_v1/(125 個・12GB)を削除しました。storage/ 配下は shards/(v2・124 個)、weights/、results/ の 3 つです。
2. 質問 1: sp_train.txt は全コーパスを繋げたものではないのか?
2-1. 何が入っているか
違います。サンプルから切り出した 3 億 4,038 万字(192,625 記事)で、コーパス全体 262 億字の 1.3% です。
作り方(train_compare_tokenizers.py の build):
extract_sample.pyが作った言語別サンプルtrain_{en,de,fr,ja}.txt(コーパスの 5%、ja のみ 25%)を読む- 言語ごとに文字数の上限まで記事を詰める: en 1.2 億字 / de 8,500 万字 / fr 7,000 万字 / ja 6,500 万字
- 4 言語分を混ぜてシャッフル →
sp_train.txt - SentencePiece はさらにその中から 200 万文をサンプリングして学習(
input_sentence_size)
2-2. なぜ全部使わないのか
BPE トークナイザの学習は「どの文字の並びが頻出するか」の統計を取って結合ルール(48,000 個)を決める作業で、 数億字あれば統計は飽和します。262 億字を読んでも結果はほぼ変わらず、時間だけ数百倍かかる(実際、旧 Celeron 機でも 4 分、新サーバでは 84 秒で済んでいます)。
2-3. 日本語の比率について — ご理解のとおりですが、決め方が少し違います
「日本語が突出して少ないので比率を上げた」— はい、そのとおりです。 ただし「日本語全体のサイズから他の言語の上限を切った」のではなく、4 言語の上限はそれぞれ独立した定数で、 日本語がトークナイザ学習データの約 19% になるように手で決めたものです(ja サンプルには 1.1 億字あり、6,500 万字の上限で切っています)。
なぜ上げるのか。コーパスの自然比率では日本語は文字数で 1.5% しかありません。その比率のまま学習すると、 48,000 個の結合ルールのほとんどが欧文の綴りに使われ、日本語は「1 文字 = 1 トークン」に近い粗い切り方しか覚えません。そうなると日本語の記事は他言語の 2 倍以上のトークン数になり(フェーズ 0 の概算はこの前提で「和文 1 字/トークン」でした)、モデルが 1 度に見られる文脈(ctx 1024)に入る日本語の量が半分になって、日本語の学習効率が落ちます。
19% まで上げた結果:
| 指標 | 値 |
|---|---|
| 語彙 48,000 個のうち日本語系のピース | 約 18,000 個(37%) |
| 日本語の圧縮効率 | 1.95 字/トークン(1 字/トークン想定の約 2 倍) |
| 切れ方 | 「首相は / 対策を / 発表した」のように文節単位 |
2-4. 重要な区別: トークナイザの学習データ ≠ 言語モデルの学習データ
この比率調整はトークナイザ(文字列 → トークン ID の変換辞書)を作るときだけの話です。言語モデル本体が読む学習データ(train シャード 6.07B トークン)はコーパスの自然比率のまま (en 64.8% / de 21.9% / fr 10.0% / ja 3.2%)で、日本語を水増ししてはいません。「辞書は日本語を厚めに作るが、教科書の中身は実態どおり」ということです。
3. 質問 2: シャードの数と各シャードのサイズはどう決まっているか?
3-1. ルール
- 1 シャード = 5,000 万トークン、固定(
tokenize_full.pyのSHARD_TOKENS = 50_000_000) - トークン ID は uint16(2 バイト。語彙 48,000 < 65,536 なので足りる)→ 1 シャード ≈ 100MB
- train: 記事単位でシャッフルしてから頭から 5,000 万トークンずつ切る → 6.068B ÷ 5,000 万 = 121.4 → 122 個(最後の 1 個は 1,870 万トークン)
- val / test: 量が少ない(1,020 万 / 2,720 万)ので 各 1 個。シャッフルせず走査順のまま
- 各シャードに sha256 とメタ JSON(トークン数・記事数・言語構成)を添付。記事の境目は EOS トークンで分かる
3-2. なぜ 5,000 万トークン(100MB)か
| 観点 | 理由 |
|---|---|
| 配布のしやすさ | クライアントは学習前に GET /api/shards/{name} で落とします。100MB なら 100Mbps 回線で約 10 秒、30Mbps でも 30 秒。学習時間に対して無視できる |
| 1 タスクの粒度 | 分散学習(DiLoCo 型)では「シャードを受け取る → 手元で H ステップ学習 → 差分を送る」が 1 タスク。5,000 万トークンは、バッチ 32 × ctx 1024(≈33,000 トークン/ステップ)で 約 1,500 ステップ分 = 1 回の局所学習として妥当な長さ |
| 失敗時の損失 | クライアントが途中で落ちても、やり直しは 1 シャード分(サーバ側でリースが切れて再キュー)。100MB 単位なら痛くない |
| 非 IID 対策 | 記事単位で全体をシャッフルしてから切るので、どのシャードも 4 言語がほぼ同じ比率で混ざっている(例: train_000 は en 64.5% / de 22.0% / fr 10.0% / ja 3.5%、全体とほぼ同じ)。「あるクライアントは英語だけを見る」という偏りが起きない(roadmap 4.3) |
3-3. シャードとタスクは別物、という補足
シャードは保存と配布の単位で、タスクの単位はもっと細かくできます。タスクの payload に「シャード名 + トークン範囲(開始・終了)」を持たせれば、1 シャードを複数タスクに分けられます。これが GTX 1050 で重要になります: S(30M)モデルの学習速度を概算すると
| GPU | 実効 FLOPS | S モデルの処理速度 | 1 シャード(5,000 万トークン)の所要 |
|---|---|---|---|
| RTX 3060 | 約 7 TFLOPS | 約 39,000 トークン/秒 | 約 21 分 |
| GTX 1050 | 約 0.5 TFLOPS | 約 2,800 トークン/秒 | 約 5 時間 |
1050 に 1 シャード丸ごと渡すとリース(既定 1 時間)を超えるので、1050 向けタスクは「シャードの 1/8」のように範囲を切って払い出す設計にします(サーバ側は payload の設計だけ、クライアント側は範囲を読む実装だけ)。これはフェーズ 2 の分散 MVP で実装します。
4. 分散の念押しについて — 段取りの確認
分散学習はこのプロジェクトの肝なので配布には API を使う、S モデル学習には GTX1050 機も参加させる
承知しています。整理すると、roadmap のフェーズ 1 → 2 は次の順序です:
| フェーズ | 内容 | マシン | 目的 |
|---|---|---|---|
| 1(次) | S モデルを 1 台で学習し、損失曲線・言語別 PPL・生成品質を記録 | RTX 3060 単独 | 比較対象(ベースライン)を作る。分散方式の「劣化 10% 以内」(成功基準 1.2)は、これが無いと測れない |
| 2 | 同じ S モデルを API でシャード配布 → 2 台で FedAvg | 3060 + 1050 | 分散基盤の実証。1 台落としても継続、ベースラインと同等の損失に到達するか |
| 3 | DiLoCo 型 + Δ 圧縮、M モデルで方式比較 | 3060 主体 + 1050 | 低通信方式の知見 |
つまりフェーズ 1 の単独学習は「分散をやらない」のではなく「分散の成績を測る物差しを先に作る」工程です。 zxell_model/ に書くモデル定義・学習ループ・評価は、フェーズ 1 の単独学習でも、フェーズ 2 でクライアント(zxell_client/)が呼び出す局所学習でも同じコードを使います。フェーズ 1 でもシャードは手元コピーではなく GET /api/shards/{name} で取得する形にして、 API 配布の経路を最初から通します(1050 の参加はフェーズ 2 から)。
この順序で進めることに問題がなければ、着手します(確認事項 1)。
5. サイトの表現
「ほぼ 5 年分」は据え置き、承知しました。触りません。
6. 確認事項
- 段取りの確認: 4 章の順序(3060 単独ベースライン → 3060+1050 の API 分散)で進めてよいですか? 「最初から 2 台で分散させたい」というご希望なら、ベースラインを後回しにする組み方も可能ですが、分散の成績を比較する物差しが無い期間ができる点だけご承知ください。
- RTX 3060 機の状態: Ubuntu Server + memtest 済みとのことでしたが、NVIDIA ドライバと CUDA は入っていますか?
nvidia-smiが動く状態であれば教えてください(未導入なら手順をお出しします)。 (省略: サーバへの接続方法(LAN 内のアドレス・SSH)に関する質問)
7. 次の作業予定
zxell_model/新設: モデル定義(RoPE / RMSNorm / SwiGLU / 埋め込み-LM Head 重み共有)、シャードのデータローダ(EOS 区切り・固定長パック)、学習ループ(AdamW・cosine スケジュール・勾配クリップ・fp16/bf16)、チェックポイント、言語別 PPL 評価。まずサーバ(CPU)で極小構成(2 層・d=64・数百ステップ)のスモークを通す- RTX 3060 機の環境(PyTorch CUDA)→ シャードを API 経由で取得 → S 本番構成(6 層・d=384)でベースライン学習開始
- 学習中の損失・PPL をサーバ側に記録し、ダッシュボードに出す準備(フェーズ 1 の完了判定「評価が自動で回る」に向けて)
zxell.ai