File size: 16,429 Bytes
a270f6e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | # 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 は品質に効いている。
|