你可能看過這句話:一張 RTX 3090、約 2 小時,就能從零做出一個 LLM。它很吸引人,也最容易被誤讀。真正值得學的,不是把「2 小時」當速度紀錄,而是親手看見隨機權重如何經過 Pretrain(預訓練)學會接續文字,再經過 SFT(監督式微調)學會用對話格式回答。
這篇 MiniMind 從零訓練 LLM 教學專為第一次碰模型訓練的讀者寫。我會帶你鎖定同一版程式與兩份 mini 資料,跑完 Pretrain → SFT,保存時間、顯存、loss 與 checkpoint,再用固定方法比較 base/SFT。本文所有時間數字都依 MiniMind 發布者公開口徑拆解;你的 3090 要留下自己的 run log,才算自己的結果。
先說結論:MiniMind 從零訓練 LLM,是教學實驗,不是商用模型捷徑
一句話定位:MiniMind 把模型結構、資料載入與訓練迴圈攤開,讓你用小尺寸模型理解完整鏈路。請先記住這個驗收式:
可信的 MiniMind 實驗=版本收據+資料收據+設定收據+執行收據+能力收據。
- 你能證明的:指定程式、指定資料與指定設定能否完成訓練;loss 如何變化;兩個 checkpoint 在固定題目上的行為差異。
- 你不能直接證明的:通用知識、長尾可靠性、安全性、可服務真實流量,或能替代 ChatGPT/Claude。
- 「2 小時」沒有可直接採信的單一口徑:專案標題把它寫成從零完整訓練,首頁註解改稱 SFT 1 epoch 實測,成本表又把同一個 SFT 1 epoch 估為 1.10 小時,另列兩階段共 2.31 小時。
MiniMind 到底讓模型學什麼?
本文鎖定的 MiniMind commit 是 7a6fddd63a30c06b2fdd5fac4089922b29bc841b。這一版主線的 Dense minimind-3 約 64M parameters,預設配置使用 8 層、hidden size 768,以及專案附帶的 6,400-entry 詞表。64M 仍是一個可以看懂流程的微型模型,不是把大型商用模型縮時複製。
Pretrain:從隨機權重開始練「下一個 token」
Pretrain 的任務可白話理解成「讀到左邊內容後,猜下一小塊文字」。Hugging Face 的 causal language modeling 文件也用相同方式定義它。從零是指模型權重沒有先載入別人的 pretrained checkpoint;經過大量預測錯誤與反向傳播,權重才逐步學到文字分布。想先補直覺,可讀本站的LLM 運作原理與預訓練、SFT、強化學習完整圖解。
SFT:沿用 base 權重,練「照示範回答」
SFT 從 Pretrain checkpoint 接著學,以整理成對話或指令的資料塑造回答方式。底層同樣可能計算 token-level cross-entropy,但起點、資料與被要求模仿的格式都不同。它會讓輸出更像助理,不等於自動增加可靠知識、事實正確性或安全對齊。這也和模型蒸餾不同:本文沒有拿大型 teacher 的完整能力,直接「搬」進小模型。
先拆穿「2 小時」:官方頁面有三組未被公開紀錄調和的說法
MiniMind 官方 README首頁註解把「2 小時」明確限縮為:單張 NVIDIA 3090 跑完 SFT 階段 1 epoch 的發布者實測時間。但同一頁的訓練成本表又把 mini 資料上的 Pretrain 1 epoch 列為約 1.21 小時、SFT 1 epoch 約 1.10 小時,合計約 2.31 小時,並註明這些是經驗估算。這不只是總範圍不同:SFT 1 epoch 本身同時出現約 2 小時與約 1.10 小時。截至 2026 年 9 月 4 日,本文查核到的官方公開材料中未找到一份共同的 dataset revision、完整 config 與 raw log,可判定哪個數字對應目前資料。表格欄位也未列 batch、序列長度、dtype 或軟體版本;本文後面固定的 hash 能證明你下載了什麼,不能倒推發布者當時用了完全相同的資料位元與設定。

還有一個可能讓時間接近兩倍的細節:這個固定 commit 裡,Pretrain 腳本與 SFT 腳本的 --epochs 預設值都是 2。固定開銷使 wall-clock 未必精確加倍,但官方成本表寫的是 1 epoch;所以若你要測相同輪數,命令必須明寫 --epochs 1,不能只複製 README 的裸命令。
MiniMind 從零訓練 LLM 前,要準備多少硬體與空間?
NVIDIA 官方規格顯示 RTX 3090 有 24GB GDDR6X。這讓本文的 64M 模型單卡實驗具有合理起點,但 24GB 本身不是「一定不會 OOM」的保證;序列長度、batch、activation、optimizer state 與程式版本都會改變峰值。若你在選卡,先用本機 LLM 顯存決策樹建立正確的 VRAM 觀念。
- GPU:本文對齊單張 RTX 3090 24GB;其他 GPU 需另驗相容性與顯存,時間口徑不能直接比較。
- 系統:Linux、NVIDIA driver、相容 CUDA、Python 3.10 系列是最接近專案公開環境的起點。
- 資料:固定 dataset revision 後,Pretrain mini 檔為 1,241,043,656 bytes,SFT mini 為 1,739,201,170 bytes;另留 checkpoint、log 與環境空間。
- 時間:先預留比官方估算更寬的窗口;資料讀取、CUDA warm-up/kernel cache、CPU、儲存裝置與降頻都可能拖慢。
README 的參考機列的是 i9-10980XE、128GB RAM、8 張 RTX 3090、Ubuntu 20.04、CUDA 12.2 與 Python 3.10.16;成本表則標示單卡 3090,卻沒有附單卡原始 log。這表示 GPU 名稱相同仍不夠,CPU、RAM、儲存與資料載入環境也要一起留存。
步驟一:鎖 commit 與 PyTorch/CUDA 環境
痛點:今天能跑的預設分支,下週可能已經換預設值。解法:先把程式固定在本文查核的 commit,再保存軟硬體版本。PyTorch 的 wheel 要依你的 driver 與 CUDA 組合選;不要從別人的舊指令猜版本,先用官方安裝選擇器產生當下適用命令。
git clone https://github.com/jingyaogong/minimind.git
cd minimind
git checkout 7a6fddd63a30c06b2fdd5fac4089922b29bc841b
python3 -m venv .venv
source .venv/bin/activate
set -euo pipefail
# 先依 PyTorch 官方選擇器安裝相容的 torch,再裝其餘依賴
python -m pip install -r requirements.txt
mkdir -p logs
uname -a > logs/environment.txt
python --version >> logs/environment.txt
git rev-parse HEAD >> logs/environment.txt
python -c "import torch; assert torch.cuda.is_available(), 'CUDA unavailable to PyTorch'; print('torch', torch.__version__, 'torch build CUDA', torch.version.cuda, 'GPU', torch.cuda.get_device_name(0))" >> logs/environment.txt
nvidia-smi >> logs/environment.txt
python -m pip freeze > logs/pip-freeze.txt
這份 environment.txt 至少要含 OS、Python、Torch、PyTorch build 對應的 CUDA 版本、NVIDIA driver、GPU 完整型號與 commit。注意 nvidia-smi 頂端的「CUDA Version」代表 driver 可支援的最高 CUDA 版本,不等於目前 Torch wheel 內建版本。MiniMind 的訓練工具會設定 Python、NumPy、Torch 與 CUDA seed,並關閉 cuDNN benchmark;但PyTorch 官方可重現性文件明確提醒:固定 seed 仍不保證跨版本、平台或 CPU/GPU 得到完全相同結果。你要追求的是可說明的同環境重跑,不是跨宇宙 bitwise 承諾。
步驟二:固定 mini 資料版本,再驗 SHA-256
痛點:只記檔名,無法知道資料是否更新過或下載損壞。解法:把 Hugging Face dataset revision 固定在 312afb4f76391145c6902f765bb51691c09a12f5,下載後比對兩個 LFS SHA-256。從這裡開始,每個命令區塊都以 MiniMind 專案根目錄為起點;另開終端機時,也先進到同一個根目錄。
curl -L --fail \
-o dataset/pretrain_t2t_mini.jsonl \
"https://huggingface.co/datasets/jingyaogong/minimind_dataset/resolve/312afb4f76391145c6902f765bb51691c09a12f5/pretrain_t2t_mini.jsonl?download=true"
curl -L --fail \
-o dataset/sft_t2t_mini.jsonl \
"https://huggingface.co/datasets/jingyaogong/minimind_dataset/resolve/312afb4f76391145c6902f765bb51691c09a12f5/sft_t2t_mini.jsonl?download=true"
sha256sum dataset/pretrain_t2t_mini.jsonl dataset/sft_t2t_mini.jsonl
# 預期:
# 6dd6716c84ab36897bdbfc7f88e04f4441c48c1ab7ecee88ce0b0e7d4685560c dataset/pretrain_t2t_mini.jsonl
# abb1e76b2056e14728beb78db96b7b3c491a0bef1ed3e34a9b381b28f29fa518 dataset/sft_t2t_mini.jsonl
檔案大小與 hash 可由固定 revision 的官方資料頁交叉核對。本文只下載兩個名稱明確標示為 Pretrain/SFT 的訓練檔,沒有另外建立 test 檔;因此後面必須把「訓練 loss」與「未見資料驗收」分開寫。
授權也要和程式碼分開看:GitHub 程式碼採 Apache-2.0,但固定版 dataset card同時列出 Apache-2.0 與 CC-BY-NC-2.0,且沒有逐列授權/來源 manifest。這些欄位不足以推導所有資料與產出都有不受限的商業使用權;若目標是商用,資料治理要另做成一個有 provenance 的工作流。
Tokenizer 要不要也從零訓練?先不要混進主實驗
專案確實有 train_tokenizer.py,但檔案頂端直接提醒不建議重訓;主線 Pretrain/SFT 又固定從 ../model 載入專案 tokenizer。自己重訓會改變 token ID 與資料相容性,還會讓你偏離「2 小時」口徑。
所以第一輪把 tokenizer 視為凍結的量尺:模型權重從零,量尺沿用官方。若你之後要研究 BPE,請另開實驗,把訓練器改為載入新 tokenizer、重訓所有 model checkpoint,並重新計算 token 長度與截斷;原始 JSONL 文字不一定要重做,但兩條結果不能畫在同一條曲線上。
若你想親眼看一次 Tokenizer 階段,可在下載 SFT mini 後跑下面的隔離實驗。腳本只取前 10,000 行對話、建立 6,400-entry BPE 詞表,輸出到 model_learn_tokenizer/,並做 encode/decode 檢查;它不會覆蓋主線 model/,也不計入後面的訓練時間。
cd trainer
set -o pipefail
python train_tokenizer.py 2>&1 | tee ../logs/tokenizer-lab.log
# 確認這是隔離輸出;Pretrain/SFT 仍讀 ../model
ls -la ../model_learn_tokenizer
步驟三:跑 Pretrain,保存第一個 base checkpoint
痛點:只看到終端機動,就以為有可重現結果。解法:明寫與官方成本表相同的 1 epoch,保存完整命令、wall-clock、GPU 每秒取樣與 log。官方表格沒有附出 batch、序列長度、dtype、軟體版本等完整收據,所以下面是這個 pinned commit 的現行預設基線,不是宣稱逐位元還原發布者當時環境。先在第二個終端機啟動顯存紀錄:
source .venv/bin/activate
nvidia-smi \
--query-gpu=timestamp,name,memory.used,power.draw,temperature.gpu \
--format=csv -l 1 | tee logs/pretrain-gpu.csv
再回第一個終端機執行訓練。這裡攤開核心模型與吞吐敏感設定:Dense、hidden 768、8 層、序列 340、BF16、batch 32、梯度累積 8、workers 8、learning rate 5e-4,並關閉 torch.compile。值得注意的是,README 的資料段建議 Pretrain mini 採 max_seq_len≈768,腳本預設卻是 340;成本表沒有說使用哪一個。本文選 340 是忠於 pinned script 的可重跑基線,不是替成本表補答案。在單卡、完整累積週期中,每次 optimizer update 累積 256 筆樣本;最後一個不足 8 steps 的 partial update 可能較小。
cd trainer
set -o pipefail
if /usr/bin/time -v python train_pretrain.py \
--epochs 1 \
--from_weight none \
--save_weight pretrain_mini_e1 \
--device cuda:0 \
--hidden_size 768 \
--num_hidden_layers 8 \
--use_moe 0 \
--max_seq_len 340 \
--dtype bfloat16 \
--batch_size 32 \
--accumulation_steps 8 \
--num_workers 8 \
--grad_clip 1.0 \
--log_interval 100 \
--save_interval 1000 \
--use_compile 0 \
--learning_rate 5e-4 \
--data_path ../dataset/pretrain_t2t_mini.jsonl \
2>&1 | tee ../logs/pretrain.log; then
train_status=0
else
train_status=$?
fi
printf 'exit_code=%s\n' "$train_status" | tee -a ../logs/pretrain.log
[ "$train_status" -eq 0 ] || exit "$train_status"
test -s ../out/pretrain_mini_e1_768.pth
訓練結束後先到第二個終端機按 Ctrl+C 停止 GPU 監測。此時至少應看到 out/pretrain_mini_e1_768.pth,並保留 checkpoints/ 裡可續訓狀態。請從 /usr/bin/time 讀 elapsed wall-clock;CSV 的最大 memory.used 只是每秒取樣中觀察到的最高值,會混入 CUDA context/其他程序,也可能漏掉瞬間真峰值。不要把它標成精確 peak VRAM,也不要把 log 裡的 ETA 當總時間。訓練 loss 下降表示模型更會配適訓練 minibatches,還沒有回答「沒看過的文字是否也更好」。
這個 pinned loop 還有一個收據陷阱:它在最後一個 microbatch 先存檔,離開 loop 後才套用不足 8 steps 的 partial optimizer update,卻沒有再存一次。只要 loader steps 不能被 8 整除,磁碟上的 checkpoint 就不含那次最後更新。本文保留原始碼行為並揭露它;若你修補成「partial update 後再存」,請把 patch 另鎖一個 commit,不能仍稱為完全相同程式。
步驟四:由 Pretrain checkpoint 接著跑 SFT
痛點:若 SFT 不小心從隨機權重開始,兩階段鏈路就斷了。解法:先確認並保存 pretrain_mini_e1_768.pth 的 hash,再明寫 --from_weight pretrain_mini_e1;這版載入函式不會額外印出「載入成功」,若檔案不存在或無法讀取,訓練會以錯誤中止。SFT 的預設序列更長、batch 與 learning rate 也不同,不能拿每 step 秒數直接跟 Pretrain 比。
sha256sum out/pretrain_mini_e1_768.pth > logs/pretrain-checkpoint.sha256
cd trainer
set -o pipefail
if /usr/bin/time -v python train_full_sft.py \
--epochs 1 \
--from_weight pretrain_mini_e1 \
--save_weight sft_mini_e1 \
--device cuda:0 \
--hidden_size 768 \
--num_hidden_layers 8 \
--use_moe 0 \
--max_seq_len 768 \
--dtype bfloat16 \
--batch_size 16 \
--accumulation_steps 1 \
--num_workers 8 \
--grad_clip 1.0 \
--log_interval 100 \
--save_interval 1000 \
--use_compile 0 \
--learning_rate 1e-5 \
--data_path ../dataset/sft_t2t_mini.jsonl \
2>&1 | tee ../logs/sft.log; then
train_status=0
else
train_status=$?
fi
printf 'exit_code=%s\n' "$train_status" | tee -a ../logs/sft.log
[ "$train_status" -eq 0 ] || exit "$train_status"
test -s ../out/sft_mini_e1_768.pth
第二個終端機用相同 nvidia-smi 方式輸出到 logs/sft-gpu.csv。完成後確認 out/sft_mini_e1_768.pth 存在,分別記錄 Pretrain 與 SFT 的開始時間、結束時間、exit code、完整 logged minibatch-loss series 與最後一筆值、顯存取樣最大值和 checkpoint hash。自訂 --save_weight 是刻意保留資料/輪數 provenance;否則腳本會反覆覆寫預設檔名 full_sft_768.pth,而官方 quick Zero 發布物另名為 full_sft_zero_768.pth。若中斷,可研究 --from_resume 1,但「中斷+續跑」要另列,不能和乾淨的一次跑完混成同一筆時間。

步驟五:先跑 20 題固定提示;held-out loss 要另建 split
這個固定 commit 的兩支主訓練腳本只建立 training loader、輸出 training loss,沒有內建 held-out evaluation loop。最誠實的基線報告方式,是把官方原路徑的 held-out loss 標為「未提供」,而不是把 train loss 改名。若要升級實驗,請在訓練前按文件/來源分組切出 validation,避免同一原文的相鄰片段跨到 train 與 validation,再為兩支腳本加只做 forward、不反向傳播的 eval loop。
同時建立一份 prompts.jsonl,在看答案前先凍結題目、預期行為與評分規則。這是讀者自訂的 20 題定性 smoke/regression suite,不是跨讀者可比較的 benchmark;可分成四組,每組 5 題:
- 語言連貫:續寫短句、改寫與摘要,檢查是否成句、是否偏離題意。
- 格式遵循:要求固定三點、JSON key 或指定字數;逐條判定格式。
- 記憶與忠實:把一段虛構事實放進 prompt,只按上下文回答;檢查是否漏讀或擅自改寫。
- 未知與幻覺:放入無答案或互相矛盾的問題,觀察是否承認不足;不要用模型自信語氣代替正確性。
在 repo 根目錄建立 prompts.jsonl,每行採 {"id":"format-01","group":"format","prompt":"只用三點回答…","expected":"必須剛好三點"},補滿 20 題後,把下面程式存成 compare_checkpoints.py。它會固定兩個 checkpoint、greedy decoding、最多 128 個新 tokens,並把全部輸出寫入 logs/,不挑最好看的案例。
import json
import torch
from transformers import AutoTokenizer
from model.model_minimind import MiniMindConfig, MiniMindForCausalLM
DEVICE = "cuda"
WEIGHTS = ["pretrain_mini_e1", "sft_mini_e1"]
if not torch.cuda.is_available():
raise RuntimeError("This receipt expects a CUDA GPU.")
tokenizer = AutoTokenizer.from_pretrained("model")
prompts = [json.loads(line) for line in open("prompts.jsonl", encoding="utf-8") if line.strip()]
if len(prompts) != 20:
raise ValueError(f"Expected 20 frozen prompts, got {len(prompts)}")
for weight in WEIGHTS:
model = MiniMindForCausalLM(MiniMindConfig(
hidden_size=768,
num_hidden_layers=8,
use_moe=False,
))
state = torch.load(f"out/{weight}_768.pth", map_location=DEVICE)
model.load_state_dict(state, strict=True)
model = model.half().eval().to(DEVICE)
with open(f"logs/{weight}-20.jsonl", "w", encoding="utf-8") as output:
for item in prompts:
if weight == "pretrain_mini_e1":
text = tokenizer.bos_token + item["prompt"]
input_format = "bos_plus_raw_prompt"
else:
messages = [{"role": "user", "content": item["prompt"]}]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
open_thinking=False,
)
input_format = "official_chat_template"
batch = tokenizer(text, return_tensors="pt", truncation=True).to(DEVICE)
with torch.inference_mode():
generated = model.generate(
inputs=batch["input_ids"],
attention_mask=batch["attention_mask"],
max_new_tokens=128,
do_sample=False,
pad_token_id=tokenizer.pad_token_id,
eos_token_id=tokenizer.eos_token_id,
)
new_tokens = generated[0, batch["input_ids"].shape[1]:]
row = {
**item,
"checkpoint": weight,
"input_format": input_format,
"generation": {"do_sample": False, "max_new_tokens": 128},
"response": tokenizer.decode(new_tokens, skip_special_tokens=True),
}
output.write(json.dumps(row, ensure_ascii=False) + "\n")
del model, state
torch.cuda.empty_cache()
從 repo 根目錄執行 python compare_checkpoints.py。這個比較刻意沿用官方 eval_llm.py 的階段差異:Pretrain 是 BOS+raw prompt,SFT 是官方 chat template;因此它比較的是「各階段建議輸入方式下的可用行為」,不是完全相同 token 序列的純 checkpoint A/B。原版 eval_llm.py 只有內建 8 題,且會 sampling 並為每題抽新 seed,不能直接充當這份 deterministic suite。
人工評分後,結果格式請寫為「SFT checkpoint 在這 20 題預設條件中通過 X/20 題」;X 只能填入本次實際結果。若未隱藏 checkpoint 標籤並隨機化輸出順序,不得稱為盲評;也不要把 X/20 改寫成代表整體能力的準確率。若用 sampling,就先指定多個 seed,保留每次輸出。要把測法做成產品級回歸流程,可接著讀AI Evals 七步教學與LLM 採樣參數 A/B Test。
「2 小時」包含什麼,又排除了什麼?
官方頁面無法提供一個自洽的「2 小時」定義:專案宣傳句把它連到完整從零訓練,註解把它限縮為單卡 3090 的 SFT 1 epoch 實測,成本表卻把 SFT 1 epoch 寫成約 1.10 小時、兩份 mini 資料各 1 epoch 合計約 2.31 小時估算。README 沒有定義計時器的開始與結束位置,也未附 raw log,因此資料首次解析、cache warm-up、模型初始化與 checkpoint I/O 是否計入均無法核實。本文的 /usr/bin/time 從 Python 程序啟動計到退出,會包含該次資料載入、初始化、訓練與存檔;不包含事前下載/安裝及事後 eval/部署,不能直接視為官方同口徑。
- 事前另計:下載資料、建環境、首次安裝與故障排除。
- 不同訓練另開 run:重新訓練 tokenizer、README 約標為 10GB 的完整 Pretrain 與約 14GB 的完整 SFT 資料,或 RL/DPO/GRPO 階段。
- 事後另計:validation/test、重複 seed、20 題人工評分與外部 benchmark。
- 產品工作另計:服務壓測、安全測試、內容政策、監控、回滾、推論成本與量化部署。
Epoch 只是讓模型再看一遍同一批資料,不是增加新的獨立資訊。Mini 資料與少量 epoch 很適合驗證 pipeline、checkpoint 與狹窄行為是否改變;它們不足以單獨證明廣泛語言能力。訓練後若想研究部署大小,可再看GGUF 量化選擇;那是下一個實驗,不要混進本次訓練時間。
七個最常見的失敗點
- 沒鎖 commit:程式與預設值變了,卻仍拿舊 README 時間比較。
- 忘了寫
--epochs 1:腳本預設跑 2 epochs,直接偏離成本表口徑。 - 先重訓 tokenizer:主線訓練仍讀官方 tokenizer,資料、token ID 與 checkpoint 互不相容。
- 只截最後一行 loss:看不到中途爆炸、恢復或過擬合;至少保存完整 curve。
- 把 seed 當保證:換 Torch、CUDA、driver 或 GPU 後仍可能不同;環境也必須固定。
- 把 20 題當排行榜:這只能抓明顯退步與呈現行為差異,不能代表所有使用情境。
- 把能聊天當能上線:production 還要定義正確性、安全、延遲、成本、流量分布與監控門檻。
MiniMind 從零訓練 LLM FAQ
1. 沒有 RTX 3090 可以跑嗎?
有機會,但先驗 CUDA、顯存與 dtype。較舊 NVIDIA GPU 可能不適合 BF16,需改成 --dtype float16;調低 batch、序列長度或換 dtype 後,都要建立新的實驗 ID,只報自己的時間與顯存取樣最大值,不能宣稱重現 3090 數字。
2. 一定要重訓 tokenizer 才叫「從零」嗎?
不一定。本文的「從零」精確指模型權重從隨機初始化開始;tokenizer 沿用官方。重訓 tokenizer 是另一個會改變所有資料與 checkpoint 相容性的實驗。
3. 為什麼命令一定要加 --epochs 1?
因為固定 commit 的腳本預設是 2。官方時間表採 1 epoch;不明寫就不是同一口徑。
4. Loss 下降就代表模型更聰明嗎?
只代表更會配適被量測的資料。Train loss 不能替代 held-out loss、固定 prompts、事實正確性與安全測試。
5. Pretrain 和 SFT 都是 next-token prediction,差在哪?
起始權重、資料與學習目標語境不同。Pretrain 建立基礎文字分布;SFT 從已有 base 接著模仿指令/對話示範。Loss 外形相似,不代表兩階段在做同一件事。
6. 20 題固定提示可以算準確率嗎?
不建議。題目不是代表整體使用分布的隨機樣本;報「通過幾題與失敗在哪裡」,比掛一個百分比更誠實。
7. 官方的 2 小時與 2.31 小時可以互相換算嗎?
不可以,文件內部仍有未解衝突。2.31 小時確實是成本表把 Pretrain 1.21 小時與 SFT 1.10 小時相加;但首頁註解又把 SFT 1 epoch 寫成實測約 2 小時。沒有同一份 config 與 raw log,不能判定原因。
8. 跑完就能拿來做產品嗎?
不能只靠這次實驗下結論。一個 exit code 0、收據完整的個別 run,只支持該環境與設定下完成訓練,以及這套題目上的行為觀察;產品還需要真實任務 eval、安全測試、延遲/成本、監控與回滾標準。
給新手的 7 個重點
- 先鎖 commit、dataset revision 與 hash,再談可重現。
- 把 tokenizer 與模型權重是否「從零」分開定義。
- 官方「2 小時」同時被寫成完整訓練與 SFT 1 epoch,不能只挑一句。
- 成本表的 SFT 1.10 小時與兩階段 2.31 小時是估算,自己的數字要靠 run log。
- 腳本預設 2 epochs;對齊表格輪數必須明寫 1。
- Train loss、held-out loss 與 20 題行為測試回答三種不同問題。
- 這套實驗的價值是看懂模型怎麼學,不是縮時製造商用 LLM。
如果你希望有人帶著把 AI 工具、模型與自動化串成完整技能,可以查看 AlphaLab 的線上課程;想先瀏覽更多零基礎與實作文章,則從AI 專區開始。
接著閱讀
左右滑動查看更多推薦
結語:不要只追兩小時,要追一條能被重算的證據鏈
MiniMind 最有價值的地方,不是讓每個人都宣稱做出了自己的 ChatGPT,而是讓「模型學習」從抽象名詞變成可觀察的過程。你會從隨機初始化一路得到 Pretrain 與 SFT checkpoints,再比較兩個已保存階段的行為;也會發現一個時間數字若沒有版本、資料、設定與驗收,幾乎無法解讀。
先照本文跑一個乾淨的 1-epoch baseline,留下五張收據;第二輪只改一個變因,例如 batch、序列長度或資料量。當你能說清楚「改了什麼、快了多少、能力哪裡變、哪裡沒變」,你真正學會的就不只是 MiniMind,而是所有模型實驗最重要的基本功。






