MiniMind — 小さな言語モデルを最初から最後まで自分で学習する
パディングが捨てる計算を測り、コーパスをパッキングする
한국어 원문으로 표시합니다.
목표
기준 토크나이저로 사전학습 말뭉치를 잘라 MiniMind 식 패딩이 계산의 몇 %를 버리는지 재고, 문서를 [bos] … [eos] 로 감싸 이어 붙인 uint16 배열(패킹)로 학습·검증 자료를 만든다. 검증 누수와 문서 경계, 학습 예산까지 숫자로 확인한다.
왜 중요한가
행렬 곱은 패딩인지 모른다. 패딩 자리는 손실에서 빠질 뿐 계산은 그대로 한다. 문서가 짧은데 최대 길이를 길게 잡으면 GPU 는 바쁜데 배우는 토큰은 적다. 패킹은 그 낭비를 없애는 대신 한 창에 여러 문서를 넣어 뒤 문서가 앞 문서를 보게 만든다 — 대부분의 사전학습이 감수하는 맞바꿈이다. 자료를 준비하는 단계에서 생기는 실수는 학습이 끝나야 드러난다. 검증 문서가 학습 쪽에 섞이면 검증 손실이 거짓말을 하고, 한 걸음의 토큰 수를 모르면 손실 곡선을 읽을 수 없다. 그래서 학습 전에 숫자로 확인해 둔다.
단계
/opt/mm/ref/tokenizer.json으로/opt/mm/data/pretrain.jsonl의 문서마다 토큰 수를 세어 /root/mm/data/lengths.json 에docs·mean·max·p95로 적으세요.- MiniMind
PretrainDataset처럼 문서 하나를[bos] + 토큰(최대 126) + [eos]로 싸서 길이 128 까지 패딩한다면 패딩이 몇 개인지 /root/mm/data/padding.json 에max_len·rows·pad_tokens·pad_fraction·truncated_docs로 적으세요. - 학습 말뭉치를 문서마다
[bos](1) 토큰들 [eos](2)로 감싸 이어 붙인uint16배열을 /root/mm/data/train.npy 로 저장하세요. - 같은 방법으로
/opt/mm/data/pretrain_val.jsonl을 /root/mm/data/val.npy 로 저장하세요. - 검증 문서 가운데 학습 말뭉치에 글자 그대로 있는 것의 수를 /root/mm/data/leak.json 에
val_docs·dup_in_train으로 적으세요. train.npy를 128 토큰씩 겹치지 않게 자른 창에서, 창 하나의 평균 문서 수와 앞 문서를 볼 수 있는 토큰의 비율을 /root/mm/data/windows.json 에seq_len·windows·docs_per_window·cross_doc_fraction으로 적으세요.- 배치 16 · 길이 128 일 때 한 걸음의 토큰 수와 한 바퀴(에폭)의 걸음 수를 /root/mm/data/budget.json 에
train_tokens·tokens_per_step·steps_per_epoch로 적으세요. - /root/mm/data/report.md 에
## 패딩## 패킹## 검증 분리세 절을 쓰고, 2단계의 pad_fraction 과 6단계의 cross_doc_fraction 을 숫자로 넣으세요.
참고
- 자르기는
tok.encode(글, add_special_tokens=False).ids, 저장은numpy.save(경로, numpy.array(ids, dtype=numpy.uint16))입니다. - 흔한 실수: 문서를 잇기 전에
[bos]·[eos]를 빼먹는 것(채점기가 다시 만든 배열과 길이가 달라집니다), int64 로 저장하는 것, 창의 첫 토큰이[bos]인 경우까지 '경계를 넘었다' 로 세는 것. - 원문: MiniMind lm_dataset.py — PretrainDataset · numpy.save
문서가 몇 토큰인가
/opt/mm/ref/tokenizer.json 으로 /opt/mm/data/pretrain.jsonl 의 text 를 문서마다 잘라, 문서 수·평균·최댓값·95 백분위(numpy.percentile(길이, 95))를 /root/mm/data/lengths.json 에 docs·mean·max·p95 로 적으세요.
평균과 최댓값만 봐도 패딩이 얼마나 생길지 짐작됩니다. 최대 길이를 최댓값에 맞추면 아무것도 안 잘리지만, 평균 길이 문서는 나머지를 전부 패딩으로 채웁니다.
MiniMind 식 패딩이 버리는 몫
문서 하나를 [bos] + 토큰(126개까지 자름) + [eos] 로 싸서 길이 128 까지 패딩한다고 할 때, 패딩 토큰 수와 전체(문서 수×128)에서의 비율, 잘린 문서 수를 /root/mm/data/padding.json 에 max_len·rows·pad_tokens·pad_fraction·truncated_docs 로 적으세요.
MiniMind 의 PretrainDataset.__getitem__ 이 그대로 이 계산을 합니다 — max_length - 2 로 자르고 bos·eos 를 붙인 뒤 나머지를 pad 로 채웁니다. 패딩은 손실에서 빠지지만(-100) 행렬 곱은 그 자리까지 합니다.
학습 말뭉치를 한 줄로 잇는다
스크립트 /root/mm/data/pack.py(인자: 입력 jsonl, 저장할 .npy)를 써서 /opt/mm/data/pretrain.jsonl 의 문서를 파일 순서대로 1(bos) 토큰들 2(eos) 로 감싸 이어 붙이고, uint16 배열로 /root/mm/data/train.npy 에 저장하세요.
파일 순서를 바꾸거나 섞으면 채점기가 다시 만든 배열과 달라집니다 — 섞는 일은 학습 루프가 배치를 뽑을 때 합니다. 어휘가 1024 라 uint16(최대 65535)이면 충분하고, int64 의 4분의 1 크기입니다.
검증 말뭉치도 같은 방법으로
/opt/mm/data/pretrain_val.jsonl 을 3단계와 같은 방법으로 패킹해 /root/mm/data/val.npy 에 저장하세요.
검증 자료는 학습과 같은 방식으로 만들어야 두 손실을 견줄 수 있습니다. 3단계 스크립트가 입력·출력 경로를 인자로 받게 해 두면 한 줄입니다.
검증 문서가 새지 않았나
검증 문서(pretrain_val.jsonl 의 text) 가운데 학습 말뭉치(pretrain.jsonl)에 글자 그대로 있는 것의 수를 /root/mm/data/leak.json 에 val_docs·dup_in_train 으로 적으세요.
학습 쪽 text 를 집합(set)으로 만들어 두면 한 번씩만 찾아보면 됩니다. 0 이 아니면 그 문서의 검증 손실은 외운 것을 잽니다.
한 창에 문서가 몇 개, 경계를 넘는 토큰은 몇 %
train.npy 를 128 토큰씩 겹치지 않게 자르고(남는 꼬리는 버림), 창 하나의 평균 [bos] 수(docs_per_window)와 앞 문서를 볼 수 있는 토큰의 비율(cross_doc_fraction)을 /root/mm/data/windows.json 에 seq_len·windows 와 함께 적으세요. 어떤 토큰이 '앞 문서를 볼 수 있다' 는 것은 창 안에서 그 토큰 자리까지(자기 포함) 문서 시작([bos])이 한 번 이상 있었다는 뜻입니다 — 단, 창의 첫 토큰이 [bos] 이면 그 문서는 창 안에서 처음 시작한 것이라 세지 않습니다.
numpy.cumsum(창 == 1, axis=1) 로 자리마다 그때까지 나온 bos 수를 구하고, 첫 자리가 bos 인 창은 1을 빼 주면 됩니다. 인과 마스크는 앞의 모든 토큰을 보게 하므로, 문서별 마스크를 따로 만들지 않는 한 이 토큰들은 앞 문서를 봅니다.
한 바퀴는 몇 걸음인가
배치 16 · 길이 128 로 학습할 때 한 걸음이 먹는 토큰 수와, train.npy 전체를 한 번 보는 데 드는 걸음 수(올림)를 /root/mm/data/budget.json 에 train_tokens·tokens_per_step·steps_per_epoch 로 적으세요.
다음 모듈에서 수백 걸음을 학습하면 말뭉치를 몇 바퀴 도는지 이 숫자로 압니다. 같은 자료를 여러 바퀴 보면 학습 손실이 검증 손실보다 빨리 내려가기 시작합니다.
자료를 준비한 근거 남기기
/root/mm/data/report.md 에 ## 패딩 ## 패킹 ## 검증 분리 세 절을 쓰고, 2단계의 pad_fraction 과 6단계의 cross_doc_fraction 을 숫자(소수 또는 백분율)로 넣으세요.
패딩과 패킹 가운데 무엇을 고르겠는지, 그 대가로 무엇을 감수하는지 한 줄씩 적으세요. 검증 분리 절에는 5단계의 결과를 넣으면 됩니다.