| # SSMoELM Technical Notes |
|
|
| ## 2026-05-27 |
|
|
| ### アーキテクチャ確定 |
|
|
| **モデル仕様** |
| - 47.04M total params / 25.80M active per token |
| - d_model=768, L=6, n_heads=12 (kv_heads=3), head_dim=64 |
| - MoE: 8 routed experts + 1 shared, top-2 routing, d_ff=256 |
| - vocab=8192, context=2048, tie_word_embeddings=True |
| |
| **パラメータ内訳** |
| |
| | Component | Params | Active | |
| |---|---|---| |
| | Embedding | 6.29M | 6.29M | |
| | Attention (全6層) | 8.85M | 8.85M | |
| | Shared Expert (全6層) | 3.54M | 3.54M | |
| | Routed Experts (top-2/8) | 28.31M | 7.08M | |
| | Norm 等 | 0.05M | 0.05M | |
| | **合計** | **47.04M** | **25.80M** | |
| |
| **ストレージ(sb3)** |
| |
| | Component | Precision | Size | |
| |---|---|---| |
| | Embedding | 4-bit | 3.00 MB | |
| | Attention L1+L6 | 4-bit | 1.41 MB | |
| | Attention L2-5 | 1-bit | 0.70 MB | |
| | Shared Expert | 4-bit | 1.69 MB | |
| | Routed Experts | 1-bit | 3.38 MB | |
| | Router+Norm | bf16 | 0.09 MB | |
| | **合計** | | **10.27 MB binary / ~9.04 MB sb3** | |
| |
| --- |
| |
| ### 学習方針 |
| |
| **QAT (BitNet b1.58 スタイル)** |
| - master weights を bf16 で保持 |
| - forward pass で layer-wise 精度に量子化(STE) |
| - bf16 勾配で master weights を更新 |
| - 推論時に master weights を破棄 |
| |
| **STE 実装** |
| ```python |
| # 1-bit: binary * row_scale, gradient は w をそのまま通す |
| return w + mx.stop_gradient(quantized - w) |
| ``` |
| |
| **選定理由**: Bonsai (PTQ) は 8B モデルの冗長性を活用する手法。 |
| 47M params / Chinchilla 付近では冗長性が不足し PTQ 品質が壊滅的になるリスクがある。 |
| Bonsai スケール別の性能低下: 8B→-11%, 4B→-19%, 1.7B→-26%(スケール縮小で加速)。 |
| |
| --- |
| |
| ### 訓練環境 |
| |
| - M2 Mac MLX (mlx==0.31.2) |
| - Python 3.12 |
| - uv でパッケージ管理 |
| |
| --- |
| |
| ### データセット |
| |
| **Pretraining: 1B tokens**(当初 4B 予定→ M2 Mac 16日超のため 1B に変更、約 4.2日) |
| - FineWeb-Edu sample-10BT: 60% (2.4B tokens) |
| - FineWeb sample-10BT: 40% (1.6B tokens) |
| - 保存形式: jsonl.gz, 100M tokens/shard × 40 shards, 6.3 GB |
| - シャッフル: 訓練時に shard 単位でランダム順 + shard 内シャッフル(方式 B) |
| |
| **Tokenizer 訓練データ** |
| - 全 40 shard から均一サンプリング (25M tokens/shard × 40 = 1B tokens) |
| - 目的: 時代的偏りを排除 |
| |
| --- |
| |
| ### Tokenizer |
| |
| **仕様** |
| - BPE, byte-level, vocab=8192 |
| - ライブラリ: HuggingFace `tokenizers` |
| - 保存先: `tokenizer/tokenizer.json` |
| |
| **特殊トークン** |
| |
| | Token | ID | 用途 | |
| |---|---|---| |
| | `<bos>` | 0 | 系列開始 | |
| | `<eos>` | 1 | 会話全体の終了 | |
| | `<pad>` | 2 | バッチパディング | |
| | `<\|system\|>` | 3 | system ターン開始 | |
| | `<\|user\|>` | 4 | user ターン開始 | |
| | `<\|assistant\|>` | 5 | assistant ターン開始 | |
| | `<\|eot\|>` | 6 | ターン終了 (end of turn) | |
| |
| **Chat template** |
| ``` |
| <bos><|system|> |
| {system}<|eot|> |
| <|user|> |
| {user}<|eot|> |
| <|assistant|> |
| {response}<|eot|><eos> |
| ``` |
| |
| **効率測定 (Wikipedia EN + UltraChat 各5M chars)** |
| |
| | Tokenizer | chars/tok (wiki) | chars/tok (chat) | tok/word (wiki) | |
| |---|---|---|---| |
| | SSMoELM (8,192) | 3.667 | 3.925 | 1.738 | |
| | GPT-2 (50,257) | 4.570 | 4.670 | 1.394 | |
| |
| - GPT-2 比 +19〜25% 多くトークンを消費(vocab 縮小の代償) |
| - 会話テキストの方が効率が良い(訓練データに会話を含めた効果) |
| - context 2048 tokens ≈ 会話で ~1300 words(許容範囲) |
| |
| --- |
| |
| ### Ablation 計画(スキップ・2026-05-27) |
| |
| 当初 A/B/C の 3 run ablation を予定していたが、下記理由でスキップして Run C 構成で直接本番学習へ。 |
| |
| - Q/K 4-bit は安全側の設定(1-bit Q/K より精度劣化リスクが低い) |
| - MoE > Dense は文献上ほぼ確実(routing overhead が Dense より有利) |
| - ablation 3 run × 50M = 150M tokens ≈ 7.5時間 の節約 |
| |
| **採用構成: attn-4bit-qk**(L2-5 の Q/K を 4-bit、V/O は 1-bit のまま) |
| |
| ストレージ影響: binary +1.06 MB → sb3 推定 ~9.97 MB(10 MB ギリギリ) |
| |
| ### 本番学習設定(2026-05-27 開始) |
| |
| ``` |
| run = attn-4bit-qk |
| tokens = 1B |
| batch_size = 2, grad_accum = 16 |
| eff_batch = 65,536 tokens/step |
| total_steps ≈ 15,259 |
| warmup_frac = 5% ≈ 763 steps |
| lr_max = 3e-4 → lr_min = 1e-5 (cosine) |
| router_lr = 1e-4 |
| optimizer = AdamW (β=0.9/0.95, wd=0.1, clip=1.0) |
| save_every = 1526 steps ≈ 100M tokens |
| 速度見込み ≈ 2800 tok/s → 約 4.2 日 |
| ``` |
| |
| --- |
| |
| ### ファイル構成 |
| |
| ``` |
| SSMoELM/ |
| specification.md 仕様書 |
| technical_notes.md ← 本ファイル |
| pyproject.toml |
| data/ |
| prepare_dataset.py 4B tokens 収集(完了) |
| make_tokenizer_sample.py 均一サンプリング(完了) |
| raw/ shard_000〜039.jsonl.gz (6.3 GB) |
| tokenizer_sample/ sample.jsonl.gz (1.4 GB) |
| tokenizer/ |
| train_tokenizer.py 訓練スクリプト(完了) |
| eval_tokenizer.py 効率測定(完了) |
| tokenizer.json 訓練済み tokenizer |
| train/ |
| config.py ModelConfig / DenseConfig |
| quantize.py QAT (1-bit, 4-bit, STE) |
| model.py SSMoELM / Dense (MLX) |
| data.py (未実装) |
| train.py (未実装) |
| tasks/ |
| ablation.md Ablation 計画 |
| ``` |
| |
| --- |
|
|
| ### 未解決の設計リスク(Ablation で検証予定) |
|
|
| 1. **1-bit Attention (L2-5)**: Q/K が precision-sensitive かどうか未検証 |
| 2. **MoE vs Dense at 47M**: routing overhead がペイするか未検証 |
| 3. **d_ff=256 (0.33×d_model)**: 通常の 2.67×〜4× より大幅に小さい |
|
|
| --- |
|
|
| --- |
|
|
| ## 2026-06-03 |
|
|
| ### Pretraining 完了 |
|
|
| - **採用チェックポイント**: step 013734(900M tokens)— 800M・900M・1B比較で900Mが最良 |
| - **学習スループット**: ~3,000 tok/s(Flash Attention + QAT cache) |
| - **実際のパックサイズ**: 12.1 MB(理論 11.49 MB、safetensors形式) |
|
|
| **ベンチマーク結果(0-shot, 500 samples)** |
|
|
| | Task | Metric | Score | Random | |
| |---|---|---|---| |
| | HellaSwag | acc_norm | 33.4% | 25% | |
| | LAMBADA | acc | 13.8% | N/A | |
| | PIQA | acc_norm | 53.2% | 50% | |
| | WinoGrande | acc | 49.6% | 50% | |
| | ARC-Easy | acc_norm | 35.0% | 25% | |
| | ARC-Challenge | acc_norm | 21.0% | 25% | |
| | BoolQ | acc | 36.2% | 50% | |
| | MMLU | acc avg (57 tasks, ≤500/task) | 23.4% | 25% | |
|
|
| --- |
|
|
| ### 推論コード(PyTorch) |
|
|
| - **ファイル**: `inference_pytorch.py`(スタンドアロン、MLX依存なし) |
| - **dtype**: fp32(CPU/MPS)— MPS の bf16 は PyTorch で未対応 |
| - **メモリ**: ディスク 12.1 MB → ランタイム ~188 MB(fp32展開) |
| - **正確な推論には packed safetensors が必須**(raw bf16 master weights は QAT適用前の値のため不正確、LAMBADA ppl: 606 vs 6366) |
| - **PyTorch/MLX スコア差**: ±1%以内(サンプリング誤差範囲) |
|
|
| --- |
|
|
| ### MoEルーティング統計(136トークン) |
|
|
| | Layer | CV | 特徴 | |
| |---|---|---| |
| | 0 | 0.32 | E4/E5偏り | |
| | 1 | 0.15 | 最も均一 | |
| | 2 | 0.22 | E1偏り | |
| | 3 | 0.42 | 最も不均一、E6突出(22.8%) | |
| | 4 | 0.25 | E5偏り | |
| | 5 | 0.25 | E0偏り | |
|
|
| エキスパート崩壊なし。aux loss + z-loss が効果的。 |
|
|
| --- |
|
|
| ### SFT計画 |
|
|
| - **データセット**: `databricks/databricks-dolly-15k`(CC BY-SA 3.0、商用利用可) |
| - **選定理由**: 商用OK、人手アノテーション15K件、短い入出力、英語のみ |
| - **除外候補**: |
| - LIMA: CC BY-NC-SA(非商用のみ) |
| - Alpaca: OpenAI ToS制約あり |
|
|
| --- |
|
|
| ### TurboWarp 速度推定 |
|
|
| Barynsor-1 実測 (21M dense, 8 tok/s) から逆算: ~151M MACs/sec |
|
|
| SSMoELM active compute per token: |
| - Attention: ~402M MACs/token |
| - Shared Expert: ~9.4M MACs/token |
| - Routed (top-2): ~18.9M MACs/token |
| - **推定 ~7.8 tok/s**(Barynsor-1 の 151M MACs/sec 基準) |
|
|
| --- |
|
|
| ## 2026-06-08 |
|
|
| ### 公開モデル |
|
|
| - **Base**: `hf.co/brulee-1/SSMoELM-Base` |
| - **Instruct**: `hf.co/brulee-1/SSMoELM-it` |
|
|
| `hf` CLI で取得確認済み: |
|
|
| ```bash |
| hf models info brulee-1/SSMoELM-Base |
| hf models info brulee-1/SSMoELM-it |
| hf download brulee-1/SSMoELM-Base README.md --local-dir /tmp/ssmoelm-hf/base |
| hf download brulee-1/SSMoELM-it README.md --local-dir /tmp/ssmoelm-hf/it |
| ``` |
|
|
| 公開先のモデルカードは `checkpoints/README.md`(Base)と `checkpoints/README_it.md`(Instruct)を元に管理する。 |
| HuggingFace 上では packed safetensors の `uint8` 配列を直接読むため、約12M params / 8-bit のように表示される場合があるが、実際は **47.04M params の 1-bit + 4-bit mixed precision MoE**。 |
|
|
| --- |
|
|
| ### SFT 完了 |
|
|
| - **Base checkpoint**: `checkpoints/attn-4bit-qk_step013734.safetensors` |
| - **Final SFT checkpoint**: `checkpoints/sft_final.safetensors` |
| - **Packed Instruct weights**: |
| - `checkpoints/ssmoelm_it_packed.safetensors` (~12 MB) |
| - `checkpoints/ssmoelm_it_packed.npz` (~11 MB) |
| - **SFT log**: `checkpoints/sft2_log.csv` |
| - **最終ログ行**: step 177050, loss 1.592862, 2636.6 tok/s |
|
|
| SFT 実装は `train/sft.py`。assistant tokens only loss を使い、user/prompt 部分と特殊トークンの prefix は loss mask で無視する。 |
|
|
| **SFT データセット** |
|
|
| | Dataset | 用途 | 備考 | |
| |---|---|---| |
| | `databricks/databricks-dolly-15k` | instruction following | CC BY-SA 3.0 | |
| | `OpenAssistant/oasst1` EN | dialogue / assistant response | Apache 2.0 | |
| | `trivia_qa` | QA factuality | CLI 既定に含む | |
| | `natural_questions` | short-answer QA | streaming | |
| | `Open-Orca/FLAN` | instruction mixture | streaming, default limit 70K | |
|
|
| `checkpoints/README_it.md` の公開モデルカードでは Dolly-15k + oasst1 EN を主要 SFT データとして記載している。追加 QA/FLAN 系は `train/sft.py` の CLI 既定データセットに含まれるため、再現性メモとして本ファイルに残す。 |
|
|
| --- |
|
|
| ### Instruct 評価結果 |
|
|
| `SSMoELM-it` は SFT 後も base の知識ベンチを概ね維持し、BoolQ / ARC-Challenge / PIQA が改善。 |
|
|
| | Task | Base | Instruct | Δ | |
| |---|---:|---:|---:| |
| | HellaSwag acc_norm | 33.4% | 33.2% | -0.2% | |
| | LAMBADA acc | 13.8% | 14.8% | +1.0% | |
| | PIQA acc_norm | 53.2% | 55.4% | +2.2% | |
| | WinoGrande acc | 49.6% | 49.6% | 0.0% | |
| | ARC-Easy acc_norm | 35.0% | 35.2% | +0.2% | |
| | ARC-Challenge acc_norm | 21.0% | 24.0% | +3.0% | |
| | BoolQ acc | 36.2% | 44.4% | +8.2% | |
| | MMLU avg | 23.4% | 23.2% | -0.2% | |
|
|
| --- |
|
|
| ### 現在のファイル構成 |
|
|
| ```text |
| SSMoELM/ |
| specification.md 最終仕様 |
| technical_notes.md 実装・実験ログ |
| pyproject.toml uv project dependencies |
| data/ |
| prepare_dataset.py FineWeb/FineWeb-Edu shard 作成 |
| make_tokenizer_sample.py tokenizer 訓練サンプル作成 |
| raw/ shard_000〜039.jsonl.gz(git ignore) |
| tokenizer_sample/ sample.jsonl.gz(git ignore) |
| tokenizer/ |
| train_tokenizer.py tokenizer 訓練 |
| eval_tokenizer.py tokenizer 効率測定 |
| tokenizer.json 訓練済み tokenizer |
| train/ |
| config.py ModelConfig / DenseConfig |
| quantize.py QAT 1-bit/4-bit + STE |
| model.py SSMoELM / Dense MLX model |
| data.py pretraining data loader |
| train.py pretraining script |
| lm_eval_adapter.py lm-eval adapter |
| sft.py supervised fine-tuning script |
| inference.py packed PyTorch inference / chat entrypoint |
| inference_packed.py packed inference implementation |
| inference_pytorch.py PyTorch reference inference |
| chat.py interactive chat helper |
| export_packed.py pack/export weights |
| eval_benchmark.py MLX evaluation |
| eval_benchmark_pt.py PyTorch evaluation |
| eval_mmlu.py MMLU evaluation |
| analyze_routing.py MoE routing analysis |
| checkpoints/ |
| README.md Base model card |
| README_it.md Instruct model card |
| ssmoelm_it_packed.safetensors Instruct packed weights(git ignore) |
| ssmoelm_it_packed.npz Instruct packed weights(git ignore) |
| ``` |
|
|
| --- |
|
|
| ### TurboWarp / goboscript MVP |
|
|
| `goboscript` で開ける `.sb3` artifact を生成開始。 |
|
|
| - **生成スクリプト**: `scripts/generate_turbowarp_demo.py` |
| - **goboscript source**: `turbowarp/ssmoelm_mvp/main.gs`, `stage.gs` |
| - **生成済み sb3**: `turbowarp/ssmoelm_mvp/ssmoelm_mvp.sb3` |
| - **検証テスト**: `tests/test_goboscript_export.py` |
|
|
| 現時点の sb3 は UI/input loop + deterministic response shell + quantized dot-product kernel + packed `uint8` decoder を含む。 |
|
|
| **実装済み Scratch kernels** |
|
|
| | Kernel | goboscript proc | 入力 | 出力 | |
| |---|---|---|---| |
| | 1-bit dot | `dot1_kernel` | activation list, bit list, scale | `dot1_result` | |
| | 4-bit dot | `dot4_kernel` | activation list, signed nibble list, scale | `dot4_result` | |
| | packed 1-bit decode | `get_packed1_bit` | uint8 list, 0-based bit index | `packed1_bit` | |
| | packed signed int4 decode | `get_packed4_signed` | uint8 list, 0-based nibble index | `packed4_value` | |
| | packed 1-bit dot | `dot1_packed_kernel` | activation list, packed uint8 bit list, scale | `dot1_packed_result` | |
| | packed 4-bit dot | `dot4_packed_kernel` | activation list, packed uint8 nibble list, scale | `dot4_packed_result` | |
| | self test | `kernel_self_test`, `packed_decode_self_test`, `packed_kernel_self_test` | toy vectors / bytes | `kernel_status`, `decode_status`, `packed_kernel_status` | |
|
|
| Self-test toy values: |
|
|
| - 1-bit dot: raw dot `-1.5`, scale `0.25` → `-0.375` |
| - 4-bit dot: raw dot `9`, scale `0.125` → `1.125` |
| - packed 1-bit decode: byte `13` (`0b00001101`, LSB-first) → bits `1,0,1,1` |
| - packed int4 decode: bytes `195` (`0xC3`) and `47` (`0x2F`), low-nibble-first → signed nibbles `3,-4,-1,2` |
| - packed 1-bit / 4-bit dot は decoder 経由で pre-expanded dot と同じ toy result(`-0.375`, `1.125`)を返す。 |
|
|
| Build command: |
|
|
| ```bash |
| /Users/scratchbrulee/.cargo/bin/goboscript build turbowarp/ssmoelm_mvp |
| ``` |
|
|
| Build result: |
|
|
| ```text |
| Emitted 361 blocks |
| ssmoelm_mvp.sb3 size: 7348 bytes |
| main sprite: 292 blocks |
| lists: packed1_bytes, k1_x, k1_w_bits, packed4_bytes, k4_w_nibbles, k4_x |
| ``` |
|
|
| Next step is 1-token projection verification: compare a tiny Scratch packed matmul/projection fixture against the Python reference, then extend it toward attention and MoE expert rows. |
|
|
| --- |
|
|
| ### Decoding sweep(2026-06-08) |
|
|
| SSMoELM-it の生成設定を小規模 prompt sweep と手動確認で比較。 |
|
|
| | temperature | top_k | repetition_penalty | 傾向 | |
| |---:|---:|---:|---| |
| | 0.0 | 1 | 1.0 | 最も文法的だが同じ句を繰り返しやすい | |
| | 0.0 | 1 | 1.15 | 反復は減るが bullet/list 系で引用符反復が残る | |
| | **0.0** | **1** | **1.3** | 低品質語の混入を抑えつつ反復も比較的少ないため採用 | |
| | 0.55 | 20 | 1.15 | 多様だが固有名詞・断片語の混入が増える | |
| | 0.7 | 30 | 1.15 | ヒューリスティックスコアは高いが意味崩壊が多い | |
|
|
| 採用値: |
|
|
| ```text |
| temperature = 0.0 |
| top_k = 1 |
| top_p = 0.9 |
| repetition_penalty = 1.3 |
| ``` |
|
|
| 理由: 47M / 8k vocab の小型モデルでは sampling の多様性より greedy + 強め repetition penalty の方が破綻しにくい。`top_p` は `temperature=0.0` では実質未使用だが、sampling に切り替える場合の互換既定として 0.9 を維持。 |
|
|
| --- |
|
|
| ### Shared expert only ablation(2026-06-08) |
|
|
| PyTorch lm-eval adapter に `--shared-only` を追加し、routed experts を無効化して shared expert だけで短い 0-shot ベンチを実行。 |
|
|
| Command: |
|
|
| ```bash |
| /etc/profiles/per-user/scratchbrulee/bin/uv run python eval_benchmark_pt.py \ |
| --ckpt checkpoints/ssmoelm_it_packed.safetensors \ |
| --tasks hellaswag,lambada_openai \ |
| --limit 100 |
| |
| /etc/profiles/per-user/scratchbrulee/bin/uv run python eval_benchmark_pt.py \ |
| --ckpt checkpoints/ssmoelm_it_packed.safetensors \ |
| --tasks hellaswag,lambada_openai \ |
| --limit 100 \ |
| --shared-only |
| ``` |
|
|
| | Mode | HellaSwag acc_norm | HellaSwag acc | LAMBADA acc | LAMBADA ppl | |
| |---|---:|---:|---:|---:| |
| | Full MoE | 40.0% | 35.0% | 11.0% | 732.20 | |
| | Shared only | 30.0% | 31.0% | 7.0% | 2742.00 | |
| |
| 結論: shared expert だけでも HellaSwag は random を少し超えるが、LAMBADA perplexity が大きく悪化。routed experts は品質に効いている。 |
| |