LLM 엔지니어링 · 토크나이저와 임베딩 · 이론
토크나이저 — 모델이 보는 것은 글자가 아니다
한 줄 요약
모델은 글자도 단어도 아닌 토큰 id 의 열을 본다. 그 변환 규칙을 정하는 것이 토크나이저이고, 어떤 규칙을 쓰느냐가 비용, 성능, 그리고 다국어 품질을 동시에 좌우한다.
왜 이게 필요했나
첫 번째 시도는 단어 단위였다. 문제는 어휘가 무한하다는 것이다. 사전에 없는 단어가 나오면 전부 미지 토큰이 되어 정보가 사라진다. 한국어처럼 조사와 어미가 붙는 언어에서는 같은 어간의 변형만으로도 어휘가 폭발한다.
두 번째 시도는 문자 단위였다. 어휘는 작아지지만 열이 너무 길어진다. 문맥 길이가 제한된 모델에서 길이는 곧 비용이고, 멀리 떨어진 관계를 학습하기도 어려워진다.
BPE(Byte Pair Encoding) 는 그 사이를 찾은 답이다. 문자에서 시작해 자주 붙어 나오는 쌍을 하나로 합치기를 반복한다. 자주 쓰이는 단어는 통째로 하나의 토큰이 되고, 드문 단어는 조각으로 남는다. 어휘 크기를 고정하면서 미지 토큰을 없앤다.
어떻게 동작하나
학습 절차는 놀랄 만큼 단순하다.
1. 코퍼스를 단어로 나누고 각 단어를 문자 리스트로 만든다. 단어 경계를 잃지 않도록 끝에 특별한 표시를 붙인다.
2. 전체에서 가장 자주 인접하는 문자 쌍을 찾는다.
3. 그 쌍을 하나의 토큰으로 합친다. 이 병합 규칙을 순서대로 기록한다.
4. 원하는 어휘 크기가 될 때까지 2~3을 반복한다.
인코딩은 학습된 병합 규칙을 기록된 순서 그대로 적용하는 것이다. 디코딩은 토큰을 이어 붙이고 단어 경계 표시를 공백으로 되돌리면 된다.
여기서 실무에 직접 닿는 사실이 몇 가지 나온다.
같은 문장도 언어에 따라 토큰 수가 크게 다르다. 대부분의 토크나이저가 영어 위주 코퍼스로 학습되어, 한국어는 문자당 토큰 수가 훨씬 높다. 같은 내용을 한국어로 보내면 영어보다 토큰이 몇 배 나오고 비용도 그만큼 든다. 문맥 길이 제한도 더 빨리 닿는다.
토큰 경계는 문자 경계와 다르다. 그래서 모델이 "이 단어의 세 번째 글자"를 묻는 질문에 약하고, 숫자를 자리별로 다루는 계산에서도 실수가 잦다. 모델이 멍청해서가 아니라 애초에 그 단위를 보지 못하기 때문이다.
어휘가 다르면 임베딩도 다르다. 토크나이저를 바꾸면 모델을 다시 학습해야 한다. 토크나이저는 나중에 갈아 끼울 수 있는 부품이 아니다.
현장에서 만나는 모습
프롬프트 비용을 줄이려 할 때 문자 수가 아니라 토큰 수를 세야 한다. 공백과 줄바꿈, 반복되는 마크다운 기호가 생각보다 많은 토큰을 먹는다. 반대로 JSON 처럼 구조가 반복되는 형식은 압축이 잘 되는 편이다.
로그에 프롬프트를 남길 때도 토큰 수를 함께 기록해 두면 비용 이상 징후를 조기에 잡을 수 있다. 사용자가 붙여 넣은 문서 하나 때문에 평균 토큰이 열 배가 되는 일이 흔하다.
이어서 읽을 것
BPE 를 바닥부터 구현한다. 문자 빈도를 세고, 기본 어휘를 만들고, 병합 규칙 120개를 학습하고, 그 규칙으로 문서를 인코딩한 뒤, id 열만으로 원문을 복원한다. 라이브러리 없이 파이썬만 쓴다.