밑바닥부터 만드는 mini GPT 공부 시리즈 1편
GPT는 문자열을 직접 읽지 않는다. 먼저 문장을 숫자 ID의 목록으로 바꾼다. 이 변환을 담당하는 부품이 토크나이저다.
이번 과제에서 중요한 지점은 교재처럼 이미 만들어진 토크나이저를 쓰지 않는다는 점이다. 특히 한글 리뷰 데이터를 다루기 때문에, 공백 기준 단어 분리보다 더 낮은 단위에서 시작해야 한다.
이번 글에서 다루는 것
- 토큰화가 왜 GPT 구현의 첫 단계인지
- 한글에서 공백 기준 토큰화가 왜 쉽게 무너지는지
- UTF-8 byte에서 시작하는 BPE가 어떤 문제를 해결하는지
- encode와 decode에서 가장 조심해야 할 복원 원칙
1. 토큰화는 문장을 모델의 입력으로 바꾸는 번역기다
사람은 “이 영화는 정말 좋았다”를 문장으로 읽는다. 하지만 모델은 이 문장을 그대로 받지 못한다. 모델이 처리할 수 있는 것은 정수 ID이고, 그 ID는 다시 embedding layer를 통해 벡터가 된다.
이 영화는 정말 좋았다
[2, 431, 128, 87, ... , 3]
모델이 계산할 수 있는 실수 배열
그래서 토크나이저가 깨지면 그 뒤의 모델은 아무리 잘 만들어도 출발점부터 잘못된다. 특히 decode했을 때 원문이 복원되지 않는다면, 학습과 생성 결과를 해석하기 어렵다.
2. 한글은 공백 기준으로만 보기 어렵다
영어는 공백으로 단어가 비교적 잘 나뉘는 편이다. 하지만 한국어는 조사와 어미가 붙어서 같은 뿌리의 표현도 계속 다른 모양으로 나타난다.
| 표현 | 사람의 감각 | 단순 단어 토큰화의 문제 |
|---|---|---|
| 재미있다 | 기본 표현 | 별도 단어로 등록 필요 |
| 재미있었다 | 과거형 | 처음 보면 새로운 단어처럼 처리 |
| 재미있네요 | 감상 표현 | 어휘에 없으면 알 수 없는 토큰이 늘어남 |
byte-level BPE는 이 문제를 완전히 해결하는 만능 해법은 아니다. 다만 모든 문자열을 최소한 byte 단위로 표현할 수 있게 만들어, 처음 보는 표현도 처리할 수 있는 출발점을 만든다.
3. 한글 한 글자는 byte 여러 개다
사람에게 “한”은 한 글자다. 하지만 UTF-8로 저장하면 여러 byte로 표현된다. byte-level 토크나이저는 바로 이 byte 목록에서 시작한다.
이때 byte 값 0~255를 그대로 모델 ID로 쓰지 않고, 앞쪽 ID는 특수 토큰에 배정한다. 과제에서는 0~3을 특수 토큰으로 고정하고, byte 0~255는 ID 4~259에 둔다.
4. BPE는 자주 붙는 조각을 합쳐간다
byte에서 시작하면 모든 문자를 표현할 수 있지만, 너무 잘게 쪼개진다. BPE는 여기서 자주 붙어 나오는 이웃한 토큰 쌍을 하나의 새 토큰으로 합쳐간다.
여기서 중요한 것은 merge 순서다. 학습할 때 어떤 pair를 어떤 순서로 합쳤는지 저장해야 나중에 새로운 문장을 encode할 때 같은 규칙을 재현할 수 있다.
decode에서 가장 조심할 점
byte를 하나씩 문자로 바꾸면 한글이 깨질 수 있다. merge token을 원래 byte까지 모두 펼친 뒤, 마지막에 모은 byte 배열 전체를 한 번에 UTF-8로 decode해야 한다.
스스로 점검
- 한국어에서 공백 기준 토큰화가 쉽게 부족해지는 이유는 무엇인가?
- byte 0이 token ID 0이 아니라 ID 4가 되는 이유는 무엇인가?
- decode할 때 byte를 마지막에 한 번만 UTF-8로 복원해야 하는 이유는 무엇인가?
다음 글 예고
다음 책 공부 글에서는 Dataset과 Embedding을 본다. 핵심은 “현재 토큰을 보고 다음 토큰을 맞히는 샘플”을 어떻게 만드는지다.
한 줄 정리: byte-level BPE는 한글을 포함한 모든 문자열을 최소 단위에서 안전하게 표현하고, 자주 붙는 조각을 합쳐 모델이 다루기 좋은 토큰으로 만드는 방법이다.