借来的分词器把韩文切成字节
한국어 원문으로 표시합니다.
한 줄 요약
토크나이저는 글을 모델이 먹는 번호(토큰)로 바꾸는 사전이다. MiniMind 는 어휘 6,400개의 바이트 수준 BPE 를 쓰고, 이 크기는 작은 모델에서 임베딩이 파라미터를 잡아먹지 않게 고른 값이다. 그런데 그 사전은 중국어·영어로 학습돼 한국어를 거의 바이트 단위로 자른다. 이 모듈에서는 우리 말뭉치로 사전을 새로 학습하고, 같은 글이 몇 토큰이 되는지를 숫자로 비교한다.
왜 이게 필요했나
언어 모델은 글자를 모른다. 정수 번호의 줄만 받고, 번호마다 임베딩 벡터 한 줄을 꺼내 쓴다. 그래서 두 가지가 한꺼번에 정해진다.
- 길이. 같은 문장이 20토큰이면 20번, 60토큰이면 60번 계산한다. 어텐션은 길이의 제곱으로 비싸지고, 한 번에 볼 수 있는 문맥(128토큰, 512토큰)도 토큰 수로 잰다. 잘게 자를수록 같은 문맥 창에 담기는 글이 줄어든다.
- 파라미터. 임베딩 표는
어휘 × 차원이다. MiniMind-3(768차원)에 어휘 6,400이면 약 490만 개로 전체 64M 의 8% 쯤이다. 그런데 우리가 만들 128차원 모델에 어휘 6,400을 쓰면 임베딩만 82만 개로 모델의 절반 가까이가 된다. MiniMind README 가 "작은 모델에는 어휘를 작게 두는 편이 맞다" 고 적는 이유다.
MiniMind 는 또 토크나이저를 다시 학습하지 말라고 권한다. 사전이 바뀌면 가중치·자료 형식·추론 인터페이스가 모두 따라 바뀌어 남과 모델을 나눠 쓸 수 없고, 토큰 단위로 계산하는 퍼플렉서티(PPL)도 사전이 다르면 서로 견줄 수 없기 때문이다. 이 코스는 그 권고를 알면서 일부러 새로 학습한다 — 한국어 말뭉치에서는 빌려 온 사전이 너무 비싸다는 것을 직접 재 보기 위해서다.
어떻게 동작하나
MiniMind 의 trainer/train_tokenizer.py 는 HuggingFace tokenizers 로 세 부품을 조립한다.
tok = Tokenizer(models.BPE())
tok.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
trainer = trainers.BpeTrainer(vocab_size=6400,
initial_alphabet=pre_tokenizers.ByteLevel.alphabet(), # 바이트 256개를 처음부터
special_tokens=["<|endoftext|>", "<|im_start|>", "<|im_end|>", ...])
tok.decoder = decoders.ByteLevel()
바이트 수준(ByteLevel) 은 글을 먼저 UTF-8 바이트로 바꾸고, 바이트 256개를 기본 글자로 삼는다. 어떤 글자든 바이트로는 표현되므로 '모르는 글자' 가 생기지 않는다. 한글 한 글자는 UTF-8 로 3바이트라, 사전에 그 글자를 합친 토큰이 없으면 한 글자가 최대 세 토큰이 된다.
BPE 는 가장 자주 붙어 나오는 두 조각을 하나로 합치는 일을 어휘가 찰 때까지 되풀이한다. 합칠 쌍이 먼저 떨어지면 어휘 크기를 다 채우지 못하고 멈춘다 — 이 코스의 말뭉치는 낱말 종류가 적어 1,024 를 주어도 700여 개에서 멈춘다. 실습에서 직접 보게 된다.
특수 토큰은 학습 전에 맨 앞자리에 박아 둔다. MiniMind 는 0번 <|endoftext|>(패딩), 1번 <|im_start|>(bos), 2번 <|im_end|>(eos) 를 쓰고, 도구 호출·생각 모드용 토큰까지 36자리를 예약해 둔다. 이 번호들은 채팅 형식과 손실 마스킹이 기대는 표지라, 바이트로 쪼개지면 안 된다.
현장에서 만나는 모습
사내 문서로 오픈 모델을 미세조정하려는데 문맥 창이 빨리 차서 긴 문서가 잘린다면, 먼저 그 모델의 토크나이저가 한국어를 몇 토큰으로 자르는지 재 보아야 한다. 같은 비용으로 담을 수 있는 글의 양이 토크나이저에 따라 두세 배 달라진다. 다만 이미 학습된 모델의 사전은 바꿀 수 없다 — 사전을 바꾸는 순간 임베딩 표가 쓸모없어진다. 사전을 고르는 일은 처음부터 학습할 때만 할 수 있는 결정이고, 그래서 MiniMind 는 사전을 한 번 정하면 계속 쓴다.
모델끼리 품질을 견줄 때도 사전이 다르면 토큰당 손실을 그대로 비교하면 안 된다. 토큰이 길면 토큰 하나를 맞히기가 어렵고, 짧으면 쉽다. MiniMind README 가 토크나이저가 다를 때는 바이트당 비트(BPB)를 쓰라고 권하는 이유이고, 이 코스의 마지막 모듈에서 BPB 를 직접 잰다.
MiniMind 원본과 이 코스가 다른 점
MiniMind 의 train_tokenizer.py 는 SFT 자료(sft_t2t_mini.jsonl)의 대화 내용으로 어휘 6,400 을 학습하고, 특수 토큰 자리를 36개 잡아 둔다 — 도구 호출(<tool_call>)·생각 모드(<think>)·앞으로 쓸 예비 칸(<|buffer1|> …)까지. 이 코스는 세 가지를 바꾼다. 학습 자료는 사전학습 말뭉치의 text 만 쓰고(채점기가 같은 조건으로 다시 학습해 견줄 수 있도록), 어휘는 1,024 로 줄이고, 특수 토큰은 채팅에 꼭 필요한 셋만 둔다. 도구 호출과 생각 모드를 다루지 않기 때문이다. 대신 MiniMind 와 같은 이름·같은 번호(0·1·2)를 지켜, 이후 모듈의 채팅 형식과 손실 마스킹이 MiniMind 코드와 똑같이 동작하게 한다.
다음 실습에서 할 것
MiniMind 의 사전으로 우리 한국어 말뭉치를 잘라 글자당 토큰을 재고, 같은 조립으로 어휘 1,024 짜리 사전을 학습해 비교한다. 어휘를 384·512·1,024 로 바꿔 압축률이 어떻게 변하는지, 어휘 크기가 임베딩 몫을 얼마나 바꾸는지까지 숫자로 남긴다.