LLM 추론 엔진 밑바닥부터 만들기: 토큰화 파이프라인 노트
요약
LLM 추론 엔진의 핵심 구성 요소인 토크나이저 파이프라인의 구현 과정을 다룹니다. Hugging Face 설정 로드부터 정규 표현식을 이용한 사전 토큰화, 바이트 수준 인코딩 및 BPE 알고리즘의 원리를 상세히 설명합니다.
핵심 포인트
- 토크나이저는 텍스트를 숫자 토큰 ID로 변환하는 필수 과정임
- Hugging Face의 vocab, merges, 정규식 패턴 등을 활용한 설정 로드
- 텍스트 정규화 및 사전 토큰화를 통한 세그먼트 분리
- 유니코드 대신 바이트 단위로 작동하는 현대적 LLM의 인코딩 방식
- BPE 알고리즘을 통한 효율적인 토큰 병합 원리
LLM (Large Language Model) 추론 엔진의 개발은 토크나이저 (tokenizer)를 이해하고 구현하는 것에서 시작됩니다. 모델이 어떠한 계산을 수행하기 전에, 원시 텍스트 (raw text) 입력은 모델 어휘 사전 (vocabulary)의 항목에 해당하는 숫자 토큰 ID (numerical token IDs)로 변환되어야 합니다.
토크나이저 설정 로드하기
첫 번째 단계는 Hugging Face에서 다운로드한 토크나이저 파일을 로드하는 것이었습니다. 토크나이저 설정에는 다음과 같은 몇 가지 중요한 구성 요소가 포함되어 있습니다:
- 어휘 사전 (
vocab.json): 토큰 문자열을 정수 토큰 ID (integer token IDs)로 매핑합니다. - 병합 규칙 (
merges.txt또는 그에 상응하는 JSON 구조): 바이트 쌍 인코딩 (Byte Pair Encoding, BPE) 병합 우선순위를 정의합니다. - 정규 표현식 패턴 (
tokenizer.json): 원시 텍스트가 처음에 어떻게 더 작은 구성 요소로 분할되는지를 정의합니다. - 특수 토큰 (Special tokens): 문장 시작 (beginning-of-sequence), 문장 종료 (end-of-sequence), 패딩 (padding) 등과 같은 예약된 토큰을 정의합니다.
C++에서 nlohmann::json 라이브러리를 사용하여, 효율적인 조회를 위해 어휘 사전과 병합 사전을 네이티브 C++ 데이터 구조로 파싱했습니다.
예시 구조:
std::unordered_map<std::string, int> vocabulary;
std::unordered_map<
...
토크나이저 클래스는 전체 인코딩 파이프라인을 관리하도록 설계되었습니다:
Raw Text
|
v
...
텍스트 정규화 및 사전 토큰화 (Pre-tokenization)
현대적인 LLM 토크나이저는 단어를 어휘 사전 항목에 직접 매핑하지 않습니다. 대신, 다단계 변환을 적용합니다.
tokenizer.json에 저장된 정규 표현식 패턴은 사전 토큰화 (pre-tokenization)를 수행하는 데 사용됩니다.
예를 들어, 이 패턴은 다음과 같은 요소들을 분리합니다:
- 단어 (Words)
- 숫자 (Numbers)
- 문장 부호 (Punctuation)
- 공백 (Whitespace)
- 축약형 (Contractions)
A 단순화된 예시:
입력:
Hello world!
정규 표현식 출력:
["Hello", " world", "!"]
각 세그먼트는 이후 BPE 알고리즘에 의해 독립적으로 처리됩니다.
바이트 수준 인코딩 (Byte-Level Encoding)
GPT-2, Qwen 및 많은 Hugging Face 모델과 같은 현대적인 LLM 토크나이저는 유니코드 (Unicode) 문자에 직접 작용하기보다 바이트 (bytes) 단위로 작동합니다.
원본 텍스트:
你好
은 먼저 UTF-8 바이트로 변환됩니다:
E4 BD A0
E5 A5 BD
각 바이트는 바이트-유니코드 매핑 (byte-to-unicode mapping)을 통해 특별한 유니코드 표현으로 매핑됩니다.
이 매핑의 목적은 가능한 모든 바이트 값 (0-255)이 유효한 유니코드 토큰 후보로 표현될 수 있도록 하는 것입니다.
예시:
Byte:
0xF0
...
이는 BPE에서 사용되는 중간 표현 (intermediate representation)을 생성합니다.
Byte Pair Encoding (BPE) 병합 알고리즘
구현 과정에서의 핵심적인 발견은 토큰화 (tokenization)가 단순히 어휘 사전 조회 (vocabulary lookup)가 아니라는 점이었습니다.
토크나이저는 어휘 사전 내부에서 즉시 다음을 검색하지 않습니다:
"hello"
대신, 병합 규칙 (merge rules)에 기반하여 반복적인 병합을 수행합니다.
각 사전 토큰화된 (pre-tokenized) 세그먼트는 먼저 개별 바이트/유니코드 단위로 분해됩니다:
예시:
hello
은 다음과 같이 됩니다:
h e l l o
그 후 토크나이저는 인접한 쌍 (adjacent pairs)을 확인합니다:
(h,e)
(e,l)
(l,l)
...
각 쌍은 병합 사전 (merge dictionary)에서 검색됩니다.
병합 사전에는 우선순위 순위 (priority ranking)가 포함되어 있습니다:
("h","e") -> 10
("he","l") -> 5
("hel","l") -> 3
순위가 낮을수록 병합 우선순위가 높음을 의미합니다.
알고리즘은 다음과 과정을 반복합니다:
- 가능한 모든 인접한 쌍을 찾습니다.
- 각 쌍이 병합 사전에 존재하는지 확인합니다.
- 병합 순위가 가장 낮은 쌍을 선택합니다.
- 해당 쌍을 하나의 토큰으로 결합합니다.
- 더 이상 유효한 병합이 남지 않을 때까지 반복합니다.
어휘 사전 조회 (Vocabulary Lookup)
BPE 병합이 완료되면, 결과로 나온 토큰 문자열들을 어휘 사전 (vocabulary dictionary)에서 검색합니다.
예시:
병합 후:
["hello", "Ġworld"]
어휘 사전 조회:
hello -> 15339
Ġworld -> 1917
최종적인 토크나이저 출력은 다음과 같습니다:
[
15339,
1917
...
이 정수 ID (integer IDs)들은 이후 트랜스포머 (transformer) 모델의 입력 임베딩 (input embeddings)으로 사용됩니다.
Repo: https://github.com/NgKaiWen7/InferenceEngine/tree/tokenization
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기