フェーズ 1: クライアント(RTX 3060 機)側のファイル構成とデータ仕様
作成日: 2026-10-11 対象: S ベースライン本番(2026-10-10 起動、90,000 ステップ完走)の過程でクライアント機に生成・配置されたファイル。数値・構造はサーバに複製した実物から採取した。フェーズ 2 でクライアント v1 を本物の局所学習に置き換える際の、入出力仕様の土台でもある。
1. ディレクトリ構成(3060 機、ユーザーのホーム配下)
~/
├── zxell/ git clone(公開リポジトリ zxell-ai/zxell、main)
│ └── zxell_model/
│ ├── .venv/ Python venv(torch cu128、numpy、sentencepiece)
│ ├── config.py, model.py, data.py, train.py, eval.py, sample.py, fetch_shards.py
│ └── runs/ 学習の出力(gitignore 対象)
│ ├── S_check/ 動作確認 30 ステップ分(使い捨て。config.json / log.jsonl / ckpt_last.pt / ckpt_final.pt)
│ └── S_baseline/ ★ 本番
│ ├── config.json モデル構成 + 学習引数 + 使ったシャード一覧
│ ├── log.jsonl 学習ログ(9,181 行)
│ ├── stdout.log 標準出力の tee(log.jsonl と同内容 + 先頭の preset 行 + done 行)
│ ├── ckpt_last.pt 1,000 ステップごとに上書きされた再開用チェックポイント(最終的に step 90000)
│ └── ckpt_final.pt 終了時のチェックポイント(349MB)★ 成果物
├── zxell-shards/ ★ 学習データ(API 経由で取得、231 ファイル、約 11GB)
│ ├── shards_index.json 一覧・境界・メタ
│ ├── train_000.bin … train_111.bin 学習シャード(112 個、各 ≤ 5,000 万トークン ≈ 100MB)
│ ├── train_NNN.bin.json 各シャードのメタ(112 個)
│ ├── val_000.bin / val_000.bin.json / val_000.langs.u8 検証(11,129 記事)
│ └── test_000.bin / test_000.bin.json / test_000.langs.u8 テスト(30,171 記事)
└── sp_bpe_48k.model トークナイザ(1,035KB。sample.py の復号用。サーバから scp)
2. 学習データ(~/zxell-shards/)
2-1. shards_index.json — 一覧とメタ
tokenize_full.py の shard 工程が出力。fetch_shards.py はまずこれを取り、記載の sha256 で各シャードを照合する。
{
"tokenizer": "sp_bpe_48k.model",
"dtype": "uint16",
"shard_tokens": 50000000,
"seed": 20260813,
"created": "2026-10-10T01:05:23",
"boundaries": { "mode": "per_lang_count", "test_docs": 8000, "val_docs": 3000,
"computed_at": "...", "snapshot": "...",
"langs": { "en": { "docs": 3978459, "max_pub_date": "2023-05-28T02:30:00",
"test_start": "2023-05-24T11:32:11", "val_start": "2023-05-23T08:00:00",
"test_docs_actual": 8000, "val_docs_actual": 3007 }, "de": {...}, "fr": {...}, "ja": {...} },
"_meta": {...} },
"shards": [ { "name": "train_000.bin", "split": "train", "tokens": 49999705, "docs": 66538,
"sha256": "caa2269f…", "langs": { "en": 30775576, "de": 12011218, "fr": 5450408, "ja": 1762503 } },
... 114 件 ... ]
}
| キー | 意味 |
|---|---|
tokenizer | トークン ID を生成した SentencePiece モデル名(語彙 48,000) |
dtype / shard_tokens | .bin の要素型 / 1 シャードの上限トークン数 |
seed | train の記事シャッフルに使った乱数シード(再現用) |
boundaries | 言語別の train/val/test 時系列境界(compute_boundaries.py の出力をそのまま埋め込み) |
shards[] | 各シャードのメタ(2-3 と同じ内容) |
2-2. <split>_NNN.bin — トークン列本体
- 形式: ヘッダなしの生配列。uint16 リトルエンディアン、1 要素 = 1 トークン ID(0〜47,999)。語彙 48,000 < 65,536 なので 2 バイトで足りる
- 記事の区切り: 各記事の末尾に EOS(ID 2) が 1 個入る。i 番目の記事は i 番目の EOS で終わる(BOS は入れていない。特殊トークンは
<unk>=0、<s>=1、</s>=2) - 並び: train は記事単位で全体をシャッフルしてから 5,000 万トークンずつ記事境界で切る(1 記事が 2 シャードにまたがらない)。どのシャードも 4 言語がコーパス全体とほぼ同じ比率で混ざる(例: train_000 は en 61.6% / de 24.0% / fr 10.9% / ja 3.5%)。 val / test はシャッフルせず走査順(スナップショットの id 順。同じチャンネルの記事が固まる)
- サイズ: train_000.bin = 99,999,410 バイト(49,999,705 トークン)。val 11.4MB、test 31.6MB
- 先頭 8 トークンの例(train_000):
27104 1625 38641 576 263 10258 307 37908
読み方(Python): np.memmap(path, dtype=np.uint16, mode="r")(data.py の load_shard)。学習サンプラは「トークン数に比例してシャードを選び、ランダム位置から ctx+1 個を切り出す」(記事境界は跨いでよい)。
2-3. <split>_NNN.bin.json — シャードのメタ
{ "name": "train_000.bin", "split": "train", "tokens": 49999705, "docs": 66538,
"sha256": "caa2269fbc07182deffdb94f05d39ccd7771e93cd07de971aa3dd50e6548b7d2",
"langs": { "en": 30775576, "de": 12011218, "fr": 5450408, "ja": 1762503 } }
sha256 は .bin 全体のハッシュ(書き込み直後の読み直し検証にも、取得後の照合にも使う)。langs は言語別トークン数(EOS 込み)。
2-4. val_000.langs.u8 / test_000.langs.u8 — 記事ごとの言語
- 形式: 1 記事 1 バイト、走査順。値は
0=en, 1=de, 2=fr, 3=ja(config.LANGSの順) - サイズ = 記事数: val 11,129 バイト、test 30,171 バイト(.bin.json の
docsと一致することを配置時に検証) - 用途: 言語別パープレキシティ。評価時にトークン列の EOS を累積して「このトークンは何番目の記事か」を求め、この配列で言語を引く(
data.token_langs)。train には無い(train は言語別評価をしない) - 先頭 16 バイトの例(val):
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2(val の先頭はフランス語記事が連続)
3. 学習の出力(runs/S_baseline/)
3-1. config.json — 再現に必要な設定一式
train.py が起動時に書く。
{
"model": { "n_layer": 6, "d_model": 384, "n_head": 6, "ctx": 1024, "vocab_size": 48000,
"rope_theta": 10000.0, "tie_embeddings": true },
"args": { "preset": "S", "shards": "<ホーム>/zxell-shards", "out": "runs/S_baseline",
"steps": 90000, "batch": 32, "grad_accum": 1, "ctx": 0,
"lr": 0.0006, "min_lr_ratio": 0.1, "warmup": 500, "wd": 0.1, "clip": 1.0,
"eval_every": 500, "eval_tokens": 1000000, "final_eval_tokens": 0,
"ckpt_every": 1000, "log_every": 10, "device": "auto", "seed": 20260929,
"resume": "", "max_train_shards": 0 },
"train_shards": [ "train_000.bin", ... , "train_111.bin" ]
}
| 項目 | 意味 |
|---|---|
model | config.ModelConfig。ctx: 0 は「preset の ctx(1024)を使う」 |
args.steps × batch × ctx | 総学習トークン = 90,000 × 32 × 1,024 = 2,949,120,000 |
lr → lr × min_lr_ratio | 線形 warmup(500 ステップ)→ cosine で 6e-4 から 6e-5 へ |
eval_every / eval_tokens | 500 ステップごとに val から等間隔に 100 万トークン分の窓を取って言語別 PPL |
final_eval_tokens: 0 | 最終評価は val 全量 |
seed | モデル初期化と学習サンプラの乱数(再現用) |
train_shards | 実際に使ったシャード名(112 個)。max_train_shards で絞った場合はその一覧 |
3-2. log.jsonl — 学習ログ(1 行 1 JSON、2 種類のレコード)
学習レコード(10 ステップごと + step 1。9,001 行):
{"step": 1, "loss": 10.864, "lr": 1.2e-06, "grad_norm": 3.206, "tok_per_s": 33597, "elapsed_s": 1}
{"step": 90000, "loss": 3.1602, "lr": 6.000000016633646e-05, "grad_norm": 1.516, "tok_per_s": 38298, "elapsed_s": 77004}
| フィールド | 意味 |
|---|---|
step | 最適化ステップ番号(1 始まり) |
loss | そのステップのバッチ平均 cross-entropy(nat)。PPL = exp(loss) |
lr | そのステップに適用した学習率 |
grad_norm | クリップ前の勾配ノルム(clip 1.0) |
tok_per_s | 起動からの累積スループット(トークン/秒) |
elapsed_s | 起動からの経過秒 |
評価レコード(500 ステップごと + 終了時。180 行):
{"step": 500, "eval": "val", "all": 384.59, "en": 186.68, "de": 367.7, "fr": 221.68, "ja": 2635.84}
{"step": 90000, "eval": "val_full", "all": 32.33, "en": 20.18, "de": 28.88, "fr": 21.8, "ja": 132.2}
| フィールド | 意味 |
|---|---|
eval | val = val から等間隔 100 万トークンのサンプル / val_full = val 全量(終了時) |
all / en / de / fr / ja | パープレキシティ(小数 2 桁)。言語別は .langs.u8 でトークンを記事の言語に帰属させて集計 |
再開(--resume)したときは同じファイルに追記される(今回は再開なし。10/9 の 2 回の起動分は rm -rf で消してから始めた)。
3-3. ckpt_last.pt / ckpt_final.pt — チェックポイント(349MB)
torch.save した dict。eval.load_model() / train.py --resume が読む。
| キー | 内容 | サイズ |
|---|---|---|
model | state_dict(45 テンソル、fp32)。tok_emb.weight(48000×384)と lm_head.weight は同じストレージを共有(重み共有)。学習パラメータは 29.1M | 116MB(保存上。重複分は 1 回しか書かれない) |
optimizer | AdamW の状態: 44 パラメータ分の exp_avg / exp_avg_sq(fp32)+ param_groups(weight decay 0.1 の群と 0 の群) | 232MB |
sampler | 学習サンプラの乱数状態(numpy PCG64)。再開時に同じ系列を続ける | 数百バイト |
step | 保存時のステップ(90000) | |
config | 3-1 の model と同じ dict | |
args | 3-1 の args と同じ dict |
model のテンソル名(1 ブロック分の例):
tok_emb.weight (48000, 384)
blocks.0.norm1.weight (384,)
blocks.0.attn.qkv.weight (1152, 384) ← Q/K/V を 1 本の行列に
blocks.0.attn.proj.weight (384, 384)
blocks.0.norm2.weight (384,)
blocks.0.ffn.w_gate.weight (1024, 384) ← SwiGLU(hidden 1024)
blocks.0.ffn.w_up.weight (1024, 384)
blocks.0.ffn.w_down.weight (384, 1024)
... blocks.1〜5 同様 ...
norm_f.weight (384,)
lm_head.weight (48000, 384) ← tok_emb.weight と共有
ckpt_last.ptは 1,000 ステップごとに上書き(中断への備え)。ckpt_final.ptは終了時に別名で保存- フェーズ 2 の重み配布・Δ 送受信で扱うのは
modelの state_dict だけ(optimizer 状態は各クライアントのローカル)。 fp32 で 116MB、bf16 に落とせば 58MB。重み共有のためlm_head.weightは送らなくてよい(受信側でtok_emb.weightに結び直す)
3-4. stdout.log
tee で取った標準出力。内容は log.jsonl と同じ行に加えて、先頭の preset S: 29.1M params (10.6M non-embedding), ctx 1024, device cuda と末尾の done: 90000 steps, 2949.12M tokens, 77050s。障害時のトレースバックもここに残る(OOM のときがそうだった — review24)。
4. サイズ一覧
| 対象 | サイズ |
|---|---|
~/zxell-shards/(231 ファイル) | 約 11GB(train 112 × ≈100MB、val 11.4MB、test 31.6MB、メタ・副ファイル < 1MB) |
runs/S_baseline/ckpt_final.pt | 349MB |
runs/S_baseline/ckpt_last.pt | 349MB(final と同じ step 90000) |
runs/S_baseline/log.jsonl | 1.1MB |
runs/S_check/(動作確認) | 約 700MB(ckpt 2 個)。削除してよい |
~/sp_bpe_48k.model | 1.0MB |
.venv/(torch cu128 含む) | 数 GB |
5. 何から何が再現できるか
| 欲しいもの | 必要なファイル |
|---|---|
| 言語別 PPL(val / test) | ckpt_final.pt + ~/zxell-shards/{val,test}_000.* → eval.py |
| 生成サンプル | ckpt_final.pt + sp_bpe_48k.model → sample.py |
| 損失曲線・PPL 推移の図 | log.jsonl(開発記録 review26 の図はこれから作図) |
| 学習の続き(例: 90k → 180k ステップ) | ckpt_last.pt + シャード + --resume(config.json の args を踏襲) |
| 同条件の再学習(ゼロから) | シャード + config.json の args(seed 固定。GPU の非決定性があるので bit 一致はしない) |
| 分散学習(フェーズ 2)の初期重み | 使わない(ベースラインと同じく「ゼロから」始める。ckpt_final.pt は比較対象) |
6. 保全状況
- 3060 機:
runs/S_baseline/一式 +~/zxell-shards/(残置。review26 確認事項 4 で合意) - サーバ: 成果物の複製(
config.json/log.jsonl/ckpt_final.pt)と、シャードの配布元 - チェックポイントは公開対象外(確定事項 6)。GitHub には
runs/と*.ptが入らないよう.gitignore済み
zxell.ai