MiniMind — Train a Small Language Model Yourself, End to End
Separate what a small model can and cannot do with numbers
한국어 원문으로 표시합니다.
목표
기준 사전학습·SFT 모델의 퍼플렉서티와 BPB 를 재고, 문서 100개로 과적합 곡선을 만든다. SFT 에서 본 질문과 못 본 질문, 두 자리 덧셈, 학습 길이 밖의 자리, 사전학습 모델이 사실을 아는지를 재어 이 모델이 무엇을 못 하는지를 숫자로 남긴다.
왜 중요한가
손실이 낮고 본 질문에 다 맞게 답하는 모델도, 한 칸만 분포 밖으로 나가면 무너진다. 그 경계를 모르면 모델을 잘못된 곳에 쓰게 된다. 큰 모델에서는 여러 원인이 섞여 흐릿하게 보이지만, 100만 파라미터 모델은 '규칙은 일반화하고 사실은 외운다', '아는 것도 질문 모양이 다르면 꺼내지 못한다', '새 형식을 배우며 옛 능력을 잊는다' 가 각각 선명한 숫자로 드러난다. 평가는 새로 가르친 것만 재는 일이 아니다. SFT·DPO 뒤에 원래 능력을 다시 재야 잊은 것이 기록된다 — 이 실습의 첫 단계가 그것이다.
단계
- 기준 사전학습·SFT 모델의 검증 말뭉치 전체 손실·퍼플렉서티·BPB 를 /root/mm/eval/ppl.json 에 적으세요.
- 학습 말뭉치의 앞 문서 100개만으로 300걸음 학습하며 25걸음마다 학습·검증 손실을 /root/mm/eval/overfit_log.csv 에, 가중치를 /root/mm/eval/overfit.pth 에 남기세요.
- 기준 SFT 모델의 정답률(말투 무시)을 SFT 에서 본 질문과 못 본 질문으로 나눠 /root/mm/eval/heldout.json 에 적으세요.
- 두 자리 덧셈 10개의 답과 정답률을 /root/mm/eval/ood.json 에 적으세요.
- 256토큰 창에서 자리 1–127 과 128–255 의 평균 손실을 /root/mm/eval/length.json 에 적으세요.
- 사전학습 모델이 마을마다 특산물·수호 동물을 이어 쓸 수 있는지 세어 /root/mm/eval/probe.json 에 적으세요.
- /root/mm/eval/report.md 에
## 무엇을 재나## 과적합## 작은 모델이 못 하는 것세 절을 쓰고, 사전학습 모델의 BPB 와 두 자리 덧셈 정답률을 넣으세요.
참고
- BPB = 전체 손실(나트, 예측한 토큰 수만큼) ÷ ln 2 ÷ 그 토큰들이 나타내는 UTF-8 바이트 수. 창마다 첫 토큰은 예측하지 않으므로 빼고, bos·eos·pad 는 바이트가 없으니 뺍니다.
- 문서 100개 학습은 노드에서 20초 안팎입니다.
- 흔한 실수: 퍼플렉서티를 배치마다 exp 한 뒤 평균 내는 것(평균 손실을 exp 하세요), 과적합 곡선에서 학습 손실만 보는 것, 정답 비교에서 말투(입니다/이다)까지 따지는 것.
- 원문: MiniMind README — 토크나이저·BPB·zero 모델 예시 · eval_llm.py · model_minimind.py — rope_scaling(YaRN)
퍼플렉서티와 BPB
/opt/mm/ref/val.npy 를 128토큰씩 겹치지 않게 자른 창 전부로 기준 사전학습(pretrain.pth)·SFT(sft.pth) 모델의 평균 손실(창 수로 가중 평균), 퍼플렉서티(exp 평균 손실), BPB 를 /root/mm/eval/ppl.json 에 {"pretrain": {"loss", "ppl", "bpb"}, "sft": {…}} 로 적으세요.
BPB 의 분모는 창마다 첫 토큰을 뺀 나머지 토큰(3 이상, 즉 특수 토큰 제외)을 tok.decode 해 UTF-8 로 센 바이트입니다. SFT 모델의 퍼플렉서티가 왜 수백인지 떠올려 보세요 — 무엇을 1e-3 으로 배웠는지.
문서 100개로 과적합시키기
/opt/mm/ref/train.npy 에서 101번째 bos 앞까지(문서 100개)만 떼어, mmkit.seed_all(0) 으로 새 모델을 만들어 배치 8·길이 128·lr 3e-3(일정 없이)으로 300걸음 학습하고, 25걸음마다 step,train_loss,val_loss(검증은 mmkit.lm_loss(model, val, n_batches=4))를 /root/mm/eval/overfit_log.csv 에, 마지막 가중치를 /root/mm/eval/overfit.pth 에 남기세요.
검증 손실이 가장 낮았던 걸음을 찾아보세요. 그 뒤로 학습 손실은 계속 내려가는데 검증 손실은 오릅니다 — 규칙 대신 그 100개를 외우기 시작한 지점입니다. 채점기는 최저점이 끝보다 앞에 있고, 끝에서 0.2 이상 올랐고, 학습·검증 손실이 0.5 이상 벌어졌는지 봅니다.
본 질문과 못 본 질문
기준 SFT 모델로 sft.jsonl(본 질문)과 sft_val.jsonl(못 본 질문) 각각에서 한 차례짜리 대화 앞 60개에 욕심쟁이로 답하게 하고, 끝의 입니다·이다 를 뗀 정답률을 질문 종류별(fact: '마을' 이 든 질문, add: 덧셈)로 /root/mm/eval/heldout.json 에 {"seen": {…}, "held_out": {…}} 로 적으세요.
못 본 사실 질문은 사전학습 말뭉치에는 나왔지만 SFT 에서 그 질문 모양을 본 적 없는 것들입니다. 못 본 덧셈 쌍과 정답률을 견줘 보세요 — 규칙과 사실이 다르게 일반화됩니다.
두 자리 덧셈
기준 SFT 모델에 두 자리 덧셈 10개(12+15, 23+41, 30+30, 45+12, 17+21, 50+25, 11+11, 34+52, 26+13, 40+19)를 '{a} 더하기 {b}는?' 으로 묻고, 답이 정답 숫자로 시작하는 몫을 /root/mm/eval/ood.json 에 accuracy 와 rows(q: "12+15" 꼴, answer: 모델의 답)로 적으세요.
모델은 한 자리 덧셈 100개만 보았습니다. 답의 형식(숫자 + 입니다/이다)은 지키지만 숫자는 한 자리 덧셈의 답처럼 나옵니다 — 규칙의 '범위' 까지는 배우지 못했습니다.
학습 길이 밖의 자리
val.npy 를 256토큰씩 자른 창 전부를 기준 사전학습 모델에 넣고, 자리별 다음 토큰 손실을 자리 1–127(loss_pos_1_127)과 128–255(loss_pos_128_255)로 나눠 평균해 /root/mm/eval/length.json 에 windows 와 함께 적으세요.
cross_entropy(…, reduction="none") 로 자리마다의 손실을 받아 두 구간으로 나눕니다. 이 모델은 128토큰 창으로만 학습했습니다. RoPE 가 상대 위치를 쓰므로 무너지지는 않지만, 본 적 없는 먼 거리에서는 덜 정확합니다.
사전학습 모델은 사실을 아는가
/opt/mm/data/world.json 의 마을마다 기준 사전학습 모델에 [bos] + '{마을} 마을의 특산물은'·'… 수호 동물은' 을 넣고 6토큰 욕심쟁이로 이어 써, 정답으로 시작하는 수를 세어 /root/mm/eval/probe.json 에 specialty_known·animal_known·held_out_known_by_pretrain(떼어 둔 4개 사실 가운데 맞힌 수)으로 적으세요.
SFT 모델이 못 본 사실 질문을 틀린 이유가 '몰라서' 인지 '알지만 질문 형식으로 꺼내지 못해서' 인지 여기서 가릅니다. 수호 동물은 여섯 가지가 마을 순서대로 돌아가 외우기 쉽고, 특산물은 열둘이 하나씩 짝지어져 있습니다.
이 모델이 못 하는 것
/root/mm/eval/report.md 에 ## 무엇을 재나 ## 과적합 ## 작은 모델이 못 하는 것 세 절을 쓰고, 1단계의 사전학습 모델 bpb 와 4단계의 accuracy 를 숫자로 넣으세요.
마지막 절에 '못 하는 것' 을 원인별로(모르는 사실, 아는데 못 꺼내는 사실, 본 적 없는 범위, 잊은 능력) 한 줄씩 적어 보세요. 이 모델을 어디에 쓰면 안 되는지가 곧 이 목록입니다.