한국어판 nanoGPT입니다. T4 한 장으로 110M 한국어 GPT를 밑바닥부터 학습합니다. 토크나이저(BPE)부터 모델, 학습 루프, 생성까지 전부 직접 짜여 있고, 루트의 .py 4개가 전부입니다.
python data/ko/prepare.py --debug # 내장된 김소월·윤동주 시로 debug 데이터 생성 (0.1초)
python train.py config/debug.py # CPU에서 8초
python sample.py --out_dir=out-debug --temperature=0돌리면 이런 게 나옵니다:
$ python train.py config/debug.py
============================================================
STARTING from scratch
============================================================
파라미터: 426,624 (0.43M) / non-embedding 0.39M
tokens/step: 1,024 (batch 8 × ctx 128 × accum 1)
device: cpu | dtype: float32 | 목표 iter: 300
iter 0: train loss 5.5491 | val loss 5.5490 | val ppl 256.97
iter 10: loss 4.5542 | lr 9.09e-04 | 43ms/iter
...
iter 200: train loss 0.9733 | val loss 0.9846 | val ppl 2.68
...
iter 300: train loss 0.6469 | val loss 0.6602 | val ppl 1.94 (최종)
체크포인트 저장: out-debug/ckpt.pt (iter 300)
끝. iter 300 / best val loss 0.6602
iter 0의 loss 5.549는 ln(256), 그러니까 256개 바이트를 아무렇게나 찍을 때의 값입니다. 여기서 출발해 3.4KB짜리 시를 42에폭 동안 통째로 외웁니다. seed가 고정이라 누가 돌려도 같은 숫자가 나옵니다.
$ python sample.py --out_dir=out-debug --temperature=0
--- 샘플 1/1 ---
안녕하세요
꽃이 ��네
갈 봄 여는 꽃 여는
사는 꽃 피네
꽃이 피네
김소월 「산유화」(산에는 꽃 피네 / 갈 봄 여름 없이 / 꽃이 피네)를 더듬더듬 외워서 뱉습니다. 0.43M짜리 모델이 8초 만에 여기까지 오는 게 debug 설정의 목적입니다.
중간중간 섞인 �는 byte-level 토크나이저 때문입니다. vocab이 256뿐이라 모델은 바이트
단위로 글자를 만드는데, 한글 한 글자가 UTF-8로 3바이트라 세 개를 연속으로 맞춰야 글자
하나가 나옵니다. 하나만 어긋나면 깨집니다.
기본 --temperature=0.8로 돌리면 훨씬 심하게 깨집니다. 바이트마다 샘플링 확률이 걸리니
3연속 성공이 그만큼 어려워지는 건데, 본편 110M 모델에서는 문제가 안 되는 값이 여기서는
치명적입니다. 한국어에서 이 문제가 얼마나 비싼지, BPE가 어떻게 해결하는지가
1장(tokenizer.py)의 fertility 실험입니다.
python data/ko/prepare.py # FineWeb-2 ko 2GB 다운로드 + BPE 32k 학습 (수십 분)
python tokenizer.py # vocab 크기별 fertility 비교
python train.py config/nano_110m.py --batch_size=4 # 약 8~10시간
python sample.py --out_dir=out-nano_110mbatch 4 기준 한 스텝에 65,536토큰, 7,000스텝이면 약 0.46B 토큰(1.15에폭)입니다. Kaggle
세션 12개 분량인데, 9시간 제한에 끊겨도 같은 명령을 다시 치면 체크포인트에서 이어서 돕니다.
config 기본값인 batch 8로 돌리면 토큰이 두 배(0.92B), 시간도 두 배(1620시간)가 됩니다.
모델은 113,268,480 파라미터(non-embedding 88,102,656)입니다. 파라미터당 약 4토큰인데, Chinchilla 기준(약 20토큰)에 한참 못 미치는 건 T4 예산에 맞춘 선택입니다. 그 대가가 어떻게 나타나는지도 그대로 관찰 대상입니다.
| 파일 | 내용 | 장 |
|---|---|---|
| data/ko/prepare.py | 데이터 파이프라인 | 0장 |
| tokenizer.py | BPE와 fertility | 1장 |
| model.py | 트랜스포머 본체 | 2장 |
| train.py | 학습 루프 | 3장 |
| sample.py | 생성과 KV캐시 검증 | 4장 |
설명은 전부 코드 주석에 있습니다. WHY:는 설계 이유, GOTCHA:는 T4에서 밟는 함정,
TRY:는 직접 바꿔볼 지점입니다. 용어 15개(BPE, RoPE, GQA, ...)는 처음 나오는 자리에서
WHY[용어] 3줄 주석으로 설명합니다.
110M은 작습니다. 지식을 묻거나 추론을 시키면 실망하게 되는데, 어느 크기부터 무엇이 가능해지는지 감을 잡는 것까지가 이 리포의 몫입니다. debug 모델의 그럴듯한 시 구절은 학습 성과라기보다 42에폭 암기의 결과입니다. 문서 사이에 EOS 토큰도 넣지 않아서 모델이 글의 경계를 배우지 못한다는 한계도 있습니다. vocab 32768이라는 숫자는 tokenizer.py의 fertility 실험에서 나온 결론을 미리 반영해 둔 값입니다.
torch >= 2.4(Kaggle 기본 이미지면 충분합니다), numpy, tqdm.
datasets는 prepare.py의 다운로드 단계에서만 씁니다.
MIT