Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

2LM-Blackwell — ギリギリ話が通じるところまで鍛える (Windows + RTX 50)

前作 1LM-Blackwell は「1文字ずつ」予測する ミニ言語モデルでした。このリポジトリでは トークナイザをサブワード(SentencePiece)に変え、 コーパスを 441万文字から 948万文字へ増やして、日本語として読める返答が返ってくる ところまで鍛えます。モデル構造は1つも変えていません。変えたのはトークナイザとデータだけです。

  • フレームワーク: PyTorch 2.13 + CUDA 13.0(Blackwell / sm_120 対応 wheel)
  • モデル: ミニGPT / 6層 / 384次元 / 6ヘッド / 文脈 256トークン / 13.81M パラメータ
  • トークナイザ: SentencePiece unigram / 語彙 8,000 / byte_fallback あり
  • データ: 日本語会話 4データセット(すべて Apache-2.0)を整形した 58,092 会話 / 948万文字
  • 学習時間: RTX 5090 で 78.5 秒(3,600ステップ / 最終 val loss 3.6118)
  • 評価: bits/char 2.586 / 主題保持率 0.733 / 破綻率 0.000

Apple Silicon / MLX 版 2LM-MLX の移植です。 Mac の最終 val loss 3.6029 に対して 3.6118(差 +0.0089)、bits/char は 2.584 対 2.586 まで 一致しました。M1 Max の 1,620.2 秒に対して 78.5 秒なので 20.65 倍です。

チャットGUI

前作から何が変わったか

1LM-Blackwell 2LM-Blackwell
トークナイザ 文字レベル / 語彙 2,077 サブワード / 語彙 8,000
コーパス oasst1 のみ / 441万文字 4データセット / 948万文字
パラメータ 11.53M 13.81M(埋め込みが増えたぶん)
1トークンあたり 1 文字 2.00 文字(マーカーを除いた実測)
文脈 256 で入る量 256 文字 約 513 文字
評価 なし 4指標を固定条件で採点
モデル構造・学習設定 — 完全に同じ

「同じ 256 トークンで2倍の文章が入る」のがサブワード化の効きどころです。 学習設定を1つも変えていないので、結果の差はトークナイザとデータだけに帰属できます。

できること

インターフェース コマンド
環境診断(最初にこれ) python check_env.py
CLI チャット python src\chat_cli.py
Web GUI(Liquid Glass 風) python server.py --open
単発生成 python src\generate.py --prompt "こんにちは"
固定評価セットで採点 python eval\run.py --tag v1 --compare-mac
語彙サイズの実測 python tools\pick_vocab.py

セットアップ

必要なもの: Windows 10/11 / NVIDIA GPU / uv。 前作と同じ仮想環境を使えます(sentencepiece だけ増えます)。

# 1. 環境変数(ユーザースコープ。設定後にターミナルを開き直す)
setx PYTHONUTF8 1
setx PYTHONIOENCODING utf-8
setx HF_HOME E:\hf_cache             # 空きの大きいドライブへ
setx TORCHINDUCTOR_CACHE_DIR E:\ti_cache
setx CUDA_MODULE_LOADING LAZY

# 2. 仮想環境(3リポジトリで共用する想定なので親ディレクトリに置く)
uv venv C:\LLM\.venv-blackwell --python 3.13
C:\LLM\.venv-blackwell\Scripts\Activate.ps1

# 3. 依存(torch は PyPI ではなく NVIDIA のインデックスから)
uv pip install -r requirements.txt --extra-index-url https://download.pytorch.org/whl/cu130
uv pip install -r requirements-dev.txt   # 作図ツールを使う場合のみ

# 4. 診断
python check_env.py

check_env.py が 13 項目すべてを通してから先に進んでください。 torch.cuda.is_available() が True でも、wheel の CUDA が古いと最初の行列積で no kernel image is available for execution on the device になります。

Windows 固有の「エラーを出さずに 3 倍遅くなる」共有GPUメモリ対策 (CUDA - Sysmem Fallback Policy / runtime.configure() / MemoryGuard)については、 前作 1LM-Blackwell の README に 実測付きで書いてあります。runtime.py はそのまま持ち込んでいます。

1. コーパスを作る

python data\prepare.py --exclude eval\holdout.txt

--exclude は必須です。eval/holdout.txt は採点に使う固定検証セットなので、 これを訓練データに残すと「暗記したモデル」を高く採点してしまいます。

実測値(2026-08 時点)。

項目 実測 Mac 版
取得した会話 341,694 件 —
フィルタ後 58,092 会話 —
文字数 9,484,834 文字 949万文字
文字種 2,573 2,574

差は 0.1% 未満なので、Hugging Face 側のデータは動いていないと判断できます。 この照合を学習より先にやってください。 ここがずれていると val loss を比べても意味がありません。data/corpus.meta.json に自動で残ります。

使ったデータセット(python data\prepare.py --list-sources で確認できます)。

キー リポジトリ ライセンス
oasst1 kunishou/oasst1-89k-ja Apache-2.0
oasst2 llm-jp/oasst2-33k-ja Apache-2.0
magpie llm-jp/magpie-sft-v1.0 Apache-2.0
tanuki Aratako/Magpie-Tanuki-8B-97k Apache-2.0

ShareAlike(継承)条件を持つデータは意図的に入れていません。 学習した重みを 配布する前提なので、継承条件が重みに及ぶかが不明なデータは避けています。

2. トークナイザ(ここが今回の主題)

python tools\pick_vocab.py     # 語彙サイズを実測して選ぶ

語彙サイズの実測

948万文字のコーパスを実際に符号化して測った表です(外挿していません)。

語彙 文字/トークン 総トークン数 埋め込み 1語彙あたり出現回数
4,000 1.944 5,746,096 1.54M 1,437
8,000 2.358 4,736,014 3.07M 592
12,000 2.558 4,367,028 4.61M 364
16,000 2.691 4,151,039 6.14M 259
24,000 2.869 3,893,802 9.22M 162
32,000 2.986 3,740,009 12.29M 117

Mac 版は 24,000 まででしたが、5090 なら埋め込みが増えても VRAM は余るので 32,000 まで伸ばしました。結論は変わりませんでした。

  • 圧縮率の伸びは 24,000 → 32,000 で +0.117 だけ(4,000 → 8,000 は +0.414)
  • 埋め込みは +3.07M。増分だけで前作のモデル1個ぶんに近い
  • 1語彙あたりの平均出現回数は 117 回まで痩せる(4,000 のときの 12分の1)

足りないのは VRAM ではなくデータです。 語彙を増やせば1トークンあたりの 情報量は増えますが、同じコーパスでは1語彙あたりの学習回数が反比例で減ります。 GPU が強くなっても、この計算だけは変わりません。

注意: この表の「文字/トークン」はマーカー(<|user|> など)を含む ファイル長で割った値です。マーカーを除くと 8,000 で 2.003 文字/トークン に なります(src/train.py の起動時に表示されます)。Mac 版の資料が言う「約2.36文字」は前者です。 どちらを使うか決めて、混ぜないでください。

学習時のトークナイザは3通りに切り替えられます。

python src\train.py                          # 既定: data/tokenizer/tokenizer.model を流用
python src\train.py --tokenizer-model ""     # コーパスから語彙8,000を学習し直す
python src\train.py --vocab-size 0           # 文字レベル(前作と同じ方式。対照実験用)

既定が「流用」なのは意図的です。 語彙を学習し直すとトークンIDの対応が変わり、 同梱のチェックポイントが読めなくなり、Mac 版との loss 比較も成立しません。

3. 学習する

python src\train.py                      # 3,600ステップ(RTX 5090 で約80秒)
python src\train.py --equivalence-run    # Mac 版と同条件で回して差を出す
python src\train.py --tokens 30_000_000  # トークン予算で止める
python src\train.py --resume             # 中断したところから再開
python src\train.py --init-from checkpoints\final   # 追加学習(次作で使う)

学習ログ

学習曲線

実測値(Windows 11 / RTX 5090 / torch 2.13.0+cu130)。

項目 値
学習時間 78.5 秒 / 3,600ステップ
消費トークン 59.0M(4.69M トークンを約12.6周)
最終 train loss 3.4325(直近50ステップ平均)
最終 val loss 3.6118
定常スループット 1,027k tok/s
専用VRAM ピーク 1.88 GB
共有GPUメモリ増分ピーク +0.01 GB

学習終了時のまとめ

Mac (M1 Max / MLX) との比較

MacとWindowsの比較

項目 M1 Max (MLX) RTX 5090 (PyTorch)
3,600ステップ 1,620.2 秒 77.9 秒(20.80 倍)
最終 train loss 3.4205 3.4325(差 +0.0120)
最終 val loss 3.6029 3.6118(差 +0.0089)
python tools\compare_mac.py --mac-csv E:\ref\2LM-MLX\runs\loss.csv

秒数が上の表(78.5 秒)と 0.6 秒違うのは、図が runs/loss.csv の最終ステップ時点、 本文が train.py の合計値(最後のサンプル生成を含む)だからです。

前作は 31.4 倍でしたが今回は 20.80 倍です。Windows 側が遅くなったのではありません。 5090 側は語彙が 3.85 倍になって1ステップ 14.4ms → 16.0ms(11% 悪化)に留まっており、 比が縮んだのは主に Mac 側の所要時間が 1,907 秒 → 1,620 秒と短いためです。 詳しくは docs/notes.md の第5節に書きました。

4. 採点する(「賢くなった気がする」を潰す)

python eval\run.py --tag v1 --compare-mac

サンプリング条件は Mac 版と同一に固定しています (temperature 0.8 / top_k 40 / repetition_penalty 1.15 / seed 777)。

指標 Windows (RTX 5090) Mac (M1 Max) 見方
bits/char 2.586 2.584 低いほど良い。乱数を使わないので直接比較できる
主題保持率 0.733 0.733 高いほど良い。質問の主題語が返答に出た割合
反復率 0.150 0.100 低いほど良い。設問1問で 0.05 動く
破綻率 0.000 0.000 低いほど良い
平均返答長 60.2 64.1 —

反復率の +0.05 を「悪化した」と読んではいけません。 設問は20問しかないので 1問の当たり外れで 0.05 動きます。同じシードを指定しても MLX と PyTorch では 乱数生成器が別物なので、同じ文章は出ません。乱数に依存する指標を、 乱数生成器が違う環境間で比べても意味がないというのがここでの教訓です。

比較の土台に使えるのは bits/char です。分母を文字数にしてあるので、 文字レベル(前作)とサブワード(今回)を並べられます。

語彙 bits/char
Mac 対照実験A(405万文字・文字レベル) 2,578 3.282
Mac 対照実験B(949万文字・文字レベル) 2,578 2.809
本リポジトリ(948万文字・サブワード) 8,000 2.586

nats/token のまま比べてはいけません。語彙が違うと単位が違います (ランダム予測の損失が ln(2578)=7.85 と ln(8000)=8.99 で別物)。

5. 会話する

CLI

python src\chat_cli.py

CLIチャット

生成速度は CLI と Web GUI の実測で 411〜1,015 文字/秒(Mac 版は 320〜800 文字/秒)。 1回目の生成だけは CUDA カーネルの初期化を含むので遅く出ます(上の画像の 20 文字/秒)。 KVキャッシュは実装していないので、文脈が伸びるほど遅くなります。 --max-new-tokens 200 は「200文字」ではなく 約400文字ぶんです。

Web GUI

python server.py --open

ウェルカム画面

生成設定

サブワードでは1トークンが複数バイトに割れることがあるため、 ストリーミングは1トークンずつ復号してはいけません。累積したトークン列を 毎回まとめて復号し、末尾が U+FFFD のときは次を待ちます (src/generate.py の decode_incrementally)。 これを忘れると絵文字や一部の漢字が ???? に化けます。

同値確認(移植が正しいかを数字で言う)

python tools\equivalence.py            # 段階0〜2
python src\train.py --equivalence-run  # 段階3
段 見るもの 判定基準 実測
0 Mac のチェックポイントを strict=True で読む 64テンソルの形が一致 通過
1 fp32 の logits を NumPy の独立実装と比較 max|diff| < 1e-4 1.98e-05
1c トークナイザ(マーカー / 往復 / byte_fallback) 3つすべて 通過
2 step 0 の loss ln(8000) + 0.5 = 9.487 ± 0.1 9.474
3 3,600ステップ後の val loss 3.6029 ± 0.05 3.6118

段階 1c は前作には無かった段です。今回は「同じ文章が同じIDに割れること」が 比較の土台なので、ここを確かめずに先へ進むと、loss の水準そのものが変わって しまい原因を切り分けられません。

移植でずれる場所(埋め込み初期化 / AdamW のバイアス補正 / erf GELU / bias の非対称 / step 0 の loss が ln(V)+0.5 になる理由)は前作と同じです。 src/model.py と src/optim.py のコメントに全部書いてあります。

到達点の診断(次作への出発点)

13.81M パラメータに対して、経験則(Chinchilla の D ≈ 20N)が求めるトークン数は 2億7,600万。手元にあるのは 469万トークン、つまり 1.7% です。

値
非埋め込みパラメータ 10.64M
全パラメータ 13.81M
経験則が求めるトークン数 276M
実際の学習トークン 4.69M(1.7%)
学習で消費したトークン 59.0M(同じデータを12.6周)

文字数で数えると 948万文字あるので「4%」と言えてしまいますが、 モデルが何回の予測を経験したかで数えるなら 1.7% が正しい見方です。 「データが2桁足りない」——これがこのモデルの限界の正体で、 5090 に替えても解決しません。GPU が速くなって解決するのは待ち時間だけです。

次作 2LM-Blackwell-GAL では、 足りないデータを自分で作る側に回ります。

仕組み

flowchart LR
    A["文字列<br/>おすすめの本は"] --> B["トークナイザ<br/>SentencePiece unigram<br/>1トークン≒2文字"]
    B --> C[埋め込み<br/>+ 位置埋め込み]
    C --> D["Transformer Block x6<br/>因果マスク付き自己注意 + MLP"]
    D --> E["次の1トークンの確率<br/>語彙8,000次元"]
    E --> F[サンプリング<br/>temperature / top_k]
    F --> A
Loading
ファイル 役割
check_env.py 環境診断13項目。最初に通す
runtime.py VRAM / 共有GPUメモリの監視、バッチ計画、MemoryGuard
src/tokenizer.py 文字レベルとサブワードの2種。同じインタフェースで差し替えられる
src/model.py ミニGPT本体。前作から変更なし
src/optim.py MLX と同じ挙動の AdamW(バイアス補正なし)
src/train.py 学習ループ。--init-from で追加学習もできる
src/generate.py サンプリングと、byte_fallback を壊さない逐次復号
src/chat_cli.py CLIチャット
server.py / web/ FastAPI + SSE / Liquid Glass 風 GUI
data/prepare.py コーパス整形(4データセット対応)
eval/run.py 固定評価セットで4指標を採点
tools/pick_vocab.py 語彙サイズの実測
tools/equivalence.py 同値確認(段階0〜2)
tools/check_bytefallback.py 絵文字・珍しい漢字がストリーミングで化けないかの確認
tools/compare_runs.py 複数の学習ログを重ねて比べる

つまずきポイント

前作で書いたもの(cu130 / torch.compile の 260 文字 / 共有GPUメモリ / \r 混入 / PowerShell の文字化け)に加えて、今回だけのものを挙げます。

  1. 語彙を学習し直したら同梱モデルが読めなくなった → --tokenizer-model を空にすると SentencePiece を再学習します。IDの対応が変わるので、既存の重みとは噛み合いません。 data/tokenizer/tokenizer.model を消さないでください。
  2. 絵文字や一部の漢字が ???? になる → 1トークンずつ復号している。 byte_fallback は未知の文字をバイト列に割るので、まとめて復号する必要があります (🐱 は4トークン = 4バイト)。python tools/check_bytefallback.py で確認できます。
  3. cosine の分母を前作と同じ 4,300 にした → 今回の Mac 版は 3,600 を走り切っている (ログ最終行の lr が下限 3e-5 まで落ちている)ので、分母は 3,600 です。 前作は時間打ち切りで途中終了していたため 4,300 でした。ログの lr から逆算できます。
  4. Set-Content -Encoding utf8 でソースを書き換えた → PowerShell 5.1 の utf8 は BOM 付きです。Python は BOM 付きソースを受け付けてしまうのでエラーが出ず、 diff が全行変更になって初めて気付きます。ソースの編集にリダイレクトを使わないこと。
  5. 評価指標が Mac と一致しない → bits/char 以外は乱数に依存します。 MLX と PyTorch では同じシードでも別の文章が出ます。
  6. 「文字/トークン」が 2.358 と 2.003 の2つある → マーカーを含めるかどうかの違いです。 誤記ではありません。

学習済みモデルについて

checkpoints/final/ に学習済みの重みを同梱しています(13.81M パラメータ fp32 で約56MB)。 クローンすればすぐ会話できます。

checkpoints/final/
├── model.safetensors   # 重み
├── config.json         # モデル構成
├── tokenizer.model     # SentencePiece の語彙(再学習しないこと)
└── tokenizer.json      # 方式と語彙数

ライセンス / クレジット

コードは MIT License です(LICENSE)。 Apple Silicon / MLX 版 2LM-MLX の移植で、 原典も同じ作者・同じ MIT License です。

同梱の学習済みモデルは、上記4つの Apache-2.0 データセットから学習しています。 同梱のトークナイザは 2LM-MLX が学習したものをそのまま流用しています(再学習していません)。 前処理で加えた変更点とライセンス全文は NOTICE と licenses/Apache-2.0.txt にあります。

生成される文章は、コーパスの統計から次の1トークンを予測し続けた結果にすぎません。 事実性は一切保証されず、実在の人物や団体について誤った内容を出力することがあります。 出力を公開の場に掲載する場合は、機械生成物である旨を明記してください。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages