「3元(約60円)と2時間で、64Bパラメータではなく64Mパラメータの言語モデルをゼロから作れる」——minimind はそう掲げる教材リポジトリで、GitHub star は 59,466(2026-09-08 実測)に達している。LLMを「使う」記事は無数にあるが「作る」側の日本語情報は薄く、llm 自作 や LLM 作り方 で検索しても上位はQiita・Zenn・note の個人記事と書籍が並ぶだけだ。
そこで、READMEの数字をそのまま紹介する代わりに、「2時間」が何を指しているのかをREADME自身の費用表と突き合わせ、さらに16GB MacBook Air (M2) で実際に学習を回してどこまで到達できるかを測った。モデルの仕組みや選び方そのものから押さえたい場合は、LLMとは?仕組み・主要モデル比較・ローカル実行・量子化を一気にまとめる2026年版 を先に読むと本記事の位置づけが掴みやすい。
- 「2時間」の定義がREADME内で食い違う。脚注は「SFT段階の1エポック」と書くが、費用表のSFTは1.10h。約2時間に一致するのは事前学習1.21h+SFT 1.10h=2.31hのほうだった。
- 2時間はRTX 3090の話。ここから逆算すると3090は1ステップ0.110秒で回している計算になる。
- Macには経路が無い。
--device既定は「cudaが無ければcpu」でMPSを選ぶ分岐が存在しない。強制すると学習1ステップがCPUの21倍遅い(107秒 vs 5.06秒)。 - 16GB M2でのCPU学習は1エポック11.5〜24日の外挿。3090の1.21hに対して229〜476倍。完走はしていない(どこで止めたかを本文に明記)。
- データは約3.0GB(1.24GB+1.74GB)。事前学習コーパスは99.77%が中国語で、かなを含む文書は0.23%。
- Python 3.14では動かない。
datasets==3.6.0が dill 経由で落ちる。3.12.9 で解消。
minimindとは——LLM 自作の教材として何を提供しているのか
minimind は jingyaogong 氏が2024年7月に公開したリポジトリで、約64Mパラメータの言語モデルを事前学習からRLHFまで一通り自分で回せることを目的にしている。特徴は「PyTorch のネイティブ実装だけで書かれている」点だ。transformers や trl の高レベルAPIに任せず、モデル定義・学習ループ・DPO/PPO/GRPO までリポジトリ内のコードで完結している。
| 項目 | 実測値(2026-09-08) |
|---|---|
| GitHub star | 59,466 |
| fork | 7,711 |
| 公開日 | 2024-07-27 |
| 最終 push | 2026-09-04 |
| ライセンス | Apache-2.0 |
| 既定ブランチ | master |
| モデルパラメータ数 | 63.91M(実測。READMEの「約64M」と一致) |
| 学習スクリプト | 11本(pretrain / full_sft / lora / dpo / ppo / grpo / distillation / agent ほか) |
パラメータ数は実際にモデルを構築して数えた。hidden_size=768 / num_hidden_layers=8 の既定設定で 63,912,000 前後、READMEの「64M」表記と齟齬は無い。看板の数字が実体と合っているOSSは意外と少ないので、ここは素直に評価できる。
対象読者は明確で、READMEにも「レゴで飛行機を組み立てるほうが、ファーストクラスに座っているより面白い」という趣旨の一文がある。動くLLMが欲しい人ではなく、学習パイプラインの各段を自分の手で通したい人のための教材だ。
64Mに収まっている理由——語彙6,400、学習ループ172行
「64Mパラメータ」がどう成立しているかを実際のファイルから確認しておくと、この教材の設計思想が分かりやすい。
同梱トークナイザの語彙数を数えると 6,400 だった。一般的なLLMの語彙は3万〜15万なので、1桁以上小さい。語彙が小さければ埋め込み層と出力層のパラメータが激減する(6,400 × 768 ≒ 490万)。隠れ層768・8層という本体の小ささと合わせて、64Mという数字はここから来ている。裏を返せば、語彙6,400では表現できる語が限られるということでもあり、教材としての割り切りがはっきり出ている部分だ。
そしてコード量も小さい。学習スクリプト9本の行数を数えるとこうなる。
| スクリプト | 行数 | 段階 |
|---|---|---|
| train_pretrain.py | 172 | 事前学習 |
| train_full_sft.py | 173 | 教師ありファインチューニング |
| train_lora.py | 185 | LoRA |
| train_dpo.py | 227 | DPO |
| train_distillation.py | 247 | 蒸留 |
| train_grpo.py | 333 | GRPO |
| train_ppo.py | 450 | PPO |
| train_agent.py | 492 | Agentic RL |
| train_tokenizer.py | 168 | トークナイザ学習 |
事前学習の全体が172行に収まっている。データ読み込み・学習率スケジュール・勾配累積・チェックポイント保存まで含めてこの行数なので、腰を据えれば1日で読み切れる。transformers の Trainer を使えば同じことが10行で書けるが、その10行の内側で何が起きているかは見えない。この172行を読むことが minimind の主目的であって、学習を完走させることは必ずしも目的ではない——この点は後の判断にも効いてくる。
「2時間」の中身を数え直す——READMEの脚注と費用表が食い違う
まず言葉の定義から確認する。READMEの冒頭にはこうある(要旨)。
約3元のコストと2時間の学習時間だけで、約64Mパラメータの超小型言語モデル MiniMind をゼロから訓練することを目指す。
そして少し下の脚注が、この「2時間」を次のように限定している(要旨)。
「2時間」とは、SFT段階を単体のNVIDIA 3090で1エポック回したときの実測時間を指す。「3元」はその時間帯のGPUレンタル費用。
ここまで読むと「SFTだけで2時間」と理解する。ところが同じREADMEの Experiments 節にある費用表は、別のことを言っている。
| モデル | パラメータ | pretrain_t2t_mini | sft_t2t_mini |
|---|---|---|---|
| minimind-3 | 64M | ≈1.21h(≈1.57元) | ≈1.10h(≈1.43元) |
| minimind-3-moe | 198M-A64M | ≈1.69h(≈2.20元) | ≈1.54h(≈2.00元) |
SFT は1.10時間である。2時間ではない。そして表の直下には「事前学習+SFTで1エポックあたり約2.31時間、費用約3.0元」と明記され、さらに別の箇所には「単体3090で2時間で再現できる」とも書かれている。
つまり、約2時間・約3元という看板の数字に対応するのは「事前学習+SFTの合計(2.31h・3.0元)」であって、脚注が言う「SFT単体」ではない。脚注のほうが誤っている、と読むのが自然だ。実際、費用「3元」も表の1.57+1.43=3.00元と正確に一致する。
これは重箱の隅ではない。「SFTだけで2時間」と理解すると、事前学習にかかる1.21時間を見落とすからだ。ゼロから作ると言っている以上、事前学習は省略できない工程である。日本語で minimind に言及している記事の中には、この脚注の表現をそのまま引き写して「SFT段階、単独3090での1エポック」と紹介しているものもあるが、費用表と突き合わせた形跡は見当たらなかった。
3090は1ステップ0.110秒で回している
以降の比較のために、READMEの数字を1ステップあたりに直しておく。事前学習データ pretrain_t2t_mini.jsonl は実測 1,270,238件、学習スクリプトの既定 batch_size=32 なので、1エポックは 39,695ステップになる。
READMEの1.21時間(4,356秒)をこれで割ると、3090の1ステップは約0.110秒という計算になる。この数字が、以降のMac側の実測と比べる基準線になる。
なお学習スクリプトの --epochs の既定値は 2 である。README の見積もりは1エポック基準なので、何も指定せずに走らせると単純に倍かかる点も押さえておきたい。
実測①:導入でつまずく2点——Python 3.14 とデータ3.0GB
実際に手元で動かす。環境は MacBook Air (M2) / メモリ16GB / SSD 256GB、CUDA GPUは無い。
datasets==3.6.0 は Python 3.14 で動かない
Homebrew の既定 Python(3.14.4)で requirements の指定どおり導入し、学習を起動すると、モデル構築までは進んでからデータ読み込みで落ちた。
TypeError: Pickler._batch_setitems() takes 2 positional arguments but 3 were given
datasets が内部で使う dill が、Python 3.14 で変更された標準ライブラリ pickle のシグネチャに追随していないために起きる。リポジトリ側の問題ではないが、2026年時点で最新のPythonを入れている人はここで必ず止まる。Python 3.12.9 で仮想環境を作り直したところ解消した。以降の計測はすべて 3.12.9 / PyTorch 2.14.0 で行っている。
「mini」データセットで約3.0GB
READMEは「まず pretrain_t2t_mini.jsonl と sft_t2t_mini.jsonl だけ落とせば速く再現できる」と案内している。この2つのサイズを実測した。
| ファイル | バイト数 | 件数 |
|---|---|---|
| pretrain_t2t_mini.jsonl | 1,241,043,656(約1.24GB) | 1,270,238 |
| sft_t2t_mini.jsonl | 1,739,201,170(約1.74GB) | — |
| 合計 | 約3.0GB | — |
「mini」と付いていても3GBある。しかも SFT用のほうが事前学習用より大きい(1.74GB > 1.24GB)。加えて HuggingFace datasets は読み込み時に Arrow 形式のキャッシュを別途作るので、実効的には倍の空き容量を見ておくべきだ。SSD 256GBのMacBook Airでは無視できない。
作ったモデルは日本語を話さない
日本語圏の読者に効いてくる点を1つ。事前学習コーパスの先頭40,000件を取り出して文字種を数えた。
| 分類 | 件数 | 割合 |
|---|---|---|
| 漢字のみ(中国語とみなす) | 39,908 | 99.77% |
| かなを含む | 92 | 0.23% |
| ラテン文字のみ | 0 | 0.00% |
99.77%が中国語である。minimind は中国語コーパスで学ぶ教材なので、これは欠陥ではなく仕様だ。ただし「LLMを自作する」動機が「日本語で動く小さなモデルが欲しい」ことにある場合、このリポジトリをそのまま回しても目的は達成できない。学べるのはパイプラインであって、成果物のモデルではない。データ差し替えが前提になる。
実測②:Apple SiliconにはMPSの経路が用意されていない
ここが本記事の中心だ。MacBook で学習を回そうとすると、まずデバイス指定の壁に当たる。trainer/train_pretrain.py の該当行はこうなっている。
parser.add_argument("--device", type=str,
default="cuda:0" if torch.cuda.is_available() else "cpu")
...
device_type = "cuda" if "cuda" in args.device else "cpu"
autocast_ctx = nullcontext() if device_type == "cpu" else torch.cuda.amp.autocast(dtype=dtype)
読み取れることは2つある。
・既定は「CUDAがあれば cuda:0、無ければ cpu」。Apple Silicon の MPS は選択肢に入っていない
・--device mps と明示すれば .to("mps") は通るが、device_type の判定が文字列 “cuda” の有無なので mps は cpu 扱いになる。結果として、CUDA経路では有効になる混合精度(autocast)が無効のまま実行される
つまり MPS は「非対応」ではなく「想定されていない」。動くには動くが、CUDA向けに書かれた最適化経路からは外れる。
GPU自体は健全で、CPUの32倍速い
ここで公平を期すために、MPSというハードウェアそのものが遅いのかを切り分けた。モデルと無関係な2048角の行列積で両デバイスを比べる。
| デバイス | 1回あたり | 実効性能 |
|---|---|---|
| CPU | 761.86 ms | 23 GFLOP/s |
| MPS | 23.37 ms | 735 GFLOP/s |
MPSはCPUの約32倍速い。GPUは正常に働いている。モデルの順伝播だけを比べても同じ傾向で、バッチ2・系列長340で CPU 0.44秒に対し MPS 0.13秒と、MPS が3.4倍速かった。
したがって「Macだから学習できない」のではない。使えるGPUがそこにあるのに、学習スクリプトがそれを使う前提で書かれていない、というのが正確な状況だ。この差を埋めるにはコード側に mps 分岐を足す改修が要る。
実測③:どこまで到達できたか——1エポックは日単位になる
正直に書く。1エポックは完走していない。 完走できないと判断した根拠が以下である。
計測は16GB MacBook Air (M2) のCPU学習で、1ステップ(順伝播+逆伝播+オプティマイザ更新)の所要時間を複数回サンプリングした。このマシンは計測中ずっと他プロセスと共有されており、load average は13〜56の範囲で変動していた(8コア機なので過負荷状態)。したがって以下の数値は上振れした上限側の値であり、専有環境ならもっと速い。最も速かったサンプルを「最良ケース」として併記する。
| 条件 | 1ステップ | 1サンプルあたり | 計測時のload |
|---|---|---|---|
| CPU / batch 2 | 5.06 秒 | 2.53 秒 | 23 |
| CPU / batch 8(最良サンプル) | 6.28 秒 | 0.785 秒 | 約27 |
| CPU / batch 8(典型) | 13.07 秒 | 1.634 秒 | 約27 |
| CPU / batch 8(高負荷時) | 24.4〜88.4 秒 | 3.05〜11.0 秒 | 46〜56 |
この1サンプルあたりの秒数を、事前学習1エポック分の 1,270,238件に掛けたのが下の外挿だ。
| 前提 | 1エポックの外挿 | 3090(1.21h)比 |
|---|---|---|
| 最良サンプル(0.785 秒/件) | 約277時間=11.5日 | 約229倍 |
| 典型サンプル(1.634 秒/件) | 約577時間=24.0日 | 約476倍 |
最良ケースを採っても11.5日である。しかもこれは事前学習1エポックのみで、SFTは含まない。既定の --epochs 2 ならさらに倍になる。3GBのデータを1周させる計算量に対して、CPUの23 GFLOP/s では桁が足りない、というだけの話だ。
そして --device mps を強制した場合が、この記事でいちばん奇妙な数字になった。順伝播・逆伝播・オプティマイザ更新を含む1ステップまるごとを、同じバッチ2で両デバイス比較する。
| 条件(バッチ2・系列長340) | 1ステップ | 計測時のload |
|---|---|---|
| CPU | 5.06 秒 | 23 |
| MPS 強制 | 107.21 秒 | 39 |
MPSのほうが21倍遅い。 行列積で32倍速く、順伝播だけなら3.4倍速かったはずのGPUが、学習1ステップになると20倍以上遅くなる。loadが23と39で違うが、この差では21倍は説明できない。
順伝播が速くて学習ステップが遅い以上、逆伝播かオプティマイザ更新のどこかがMPS上で極端に遅いことになる。ただしどの演算が原因かの特定までは行っていない——ここは未検証として残す。いずれにせよ実用上の結論は変わらず、--device mps は現状の minimind では選ぶ意味が無い。
参考:この規模の学習に何が要るか
比較のため、READMEが挙げる環境と手元の環境を並べる。
| 環境 | 事前学習1エポック | 備考 |
|---|---|---|
| NVIDIA 3090 ×1 | 1.21時間 | READMEの値。レンタル約1.57元 |
| 8×H100 | 数分 | READMEの記述(当サイト未検証) |
| MacBook Air M2 / 16GB(CPU) | 11.5〜24日(外挿) | 本記事の実測。完走せず |
| MacBook Air M2 / 16GB(MPS強制) | CPUより21倍遅い | 学習1ステップがバッチ2で107秒(CPUは5.06秒) |
ローカルでモデルを動かすこと自体はMacでも十分現実的で、Apple Silicon 向けの実行環境はomlx徹底解説|Apple Silicon専用・OpenAI/Anthropic両対応のローカルLLMサーバーを実測にまとめている。メモリを超える規模のモデルをMacで動かす工夫としてはslotstream実測|104GBのMoEを48GB Macで動かすSSDストリーミングの仕組みと限界も対照になる。「動かす」と「学習する」で必要な計算資源は桁が違うという、当たり前だが混同されやすい線引きが、今回の数字ではっきり出た。
1,270,238件"] --> B["事前学習
39,695ステップ / epoch"] B --> C["SFT
sft_t2t_mini"] C --> D["RLAIF / DPO / GRPO
任意"] B -.->|"3090: 1.21h"| E["GPU経路
autocast 有効"] B -.->|"M2 CPU: 11.5〜24日"| F["CPU経路
autocast 無効"] B -.->|"mps: 測定不能"| F
結局どう使うべきか——LLM 自作の入口として見たときの向き不向き
実測を踏まえた判断はこうなる。
| 状況 | 判断 | 理由 |
|---|---|---|
| CUDA GPU(3090級以上)がある | ◎ そのまま進める | READMEの2.31時間はこの前提の値。素直に再現できる |
| GPUは無いがクラウドを借りられる | ◎ 3元=約60円で済む | 学習だけレンタルGPUに出すのが最も合理的。これがREADMEの想定 |
| Macでコードを読んで理解したい | ○ 目的は達成できる | ネイティブPyTorch実装なので読む価値は高い。学習は回さない |
| Macで実際に学習を完走したい | ✕ 現実的でない | 最良ケースで11.5日。MPS経路も未整備 |
| 日本語で動く小型モデルが欲しい | ✕ 目的が違う | コーパスの99.77%が中国語。データ差し替えが前提 |
minimind の価値は「2時間」という数字そのものではなく、学習パイプラインの全段が読める形で置かれていることにある。事前学習・SFT・LoRA・DPO・PPO・GRPO・蒸留がそれぞれ独立したスクリプトになっていて、高レベルAPIに隠れていない。この構成の教育的価値は、手元のマシンで完走できるかどうかとは独立している。
逆に、「2時間で自作できる」という一文だけを見て手元のノートPCで試すと、導入(Python 3.14 の非互換)とデータ容量(3GB)と計算時間(日単位)の3か所で止まる。止まる場所が事前に分かっていれば、GPUを借りるという正しい判断に最初から行ける——本記事の実測はそのためのものだ。
小さなモデルを実際に動かす側の話は、1ビット量子化で推論を軽くするbitnet.cpp(BitNet)とは|Microsoftの1ビットLLM推論フレームワークをCPUだけで動かすが対になる。学習を小さくする方向と、推論を小さくする方向の両方を見ておくと、64Mという規模の意味が掴みやすい。
まとめ
llm 自作 を検索して minimind に辿り着いた人が、着手前に知っておくと得をする事実を6点にまとめる。
・「2時間」の定義がREADME内で食い違う。脚注は「SFT 1エポック」と言うが、費用表のSFTは1.10h。約2時間に対応するのは事前学習+SFTの合計2.31h
・その2時間はRTX 3090の値。逆算すると1ステップ0.110秒で回している
・モデルは実測63.91Mパラメータで、READMEの「64M」と一致している
・Apple SiliconにはMPSの経路が無い。--device 既定は cuda か cpu の二択で、mps を渡しても混合精度は無効のまま
・GPU自体は健全(行列積でCPUの32倍・735 GFLOP/s)。使えるGPUがあるのにスクリプトが使わない、という状態
・16GB M2でのCPU学習は1エポック11.5〜24日の外挿。完走はしていない。データは約3.0GBで、コーパスの99.77%が中国語
「ゼロからLLMを作る」という体験自体は、3元=約60円のGPUレンタルで確かに手が届く。ただしそれは手元のMacで完結する話ではない。この記事の数字が、その線引きを事前に済ませる助けになれば十分だ。
参照ソース
・jingyaogong/minimind — 公式リポジトリ・README(2026-09-08 参照)
・minimind README_en.md — 英語版。学習コストの表と「2時間」の脚注(2026-09-08 参照)
・minimind_dataset — ModelScope・学習データ配布元(2026-09-08 参照)
・trainer/train_pretrain.py — デバイス指定と混合精度の分岐(2026-09-08 参照)