
지난 글에서는 LLM이 언어를 계산하기 위해 사용하는 Vector, Matrix, Tensor와 그 위에서 이루어지는 Dot Product, Cosine Similarity, Softmax를 살펴봤다.
그런데 한 가지 중요한 질문이 남아 있다.
우리가 LLM에게 입력하는 것은 Vector가 아니다.
"오늘 저녁 메뉴를 추천해줘."
우리가 입력하는 것은 Text다.
반면 Transformer가 실제로 계산하는 것은 숫자로 이루어진 Tensor다.
그렇다면 이 사이에서는 무슨 일이 일어날까?
Raw Text
↓
Tokenizer
↓
Tokens
↓
Token IDs
↓
Embedding
↓
Token Vectors
↓
Position Information
↓
Input Representations
↓
Transformer
이번 글에서는 이 변환 과정을 따라가 본다.
단순히
Tokenization = 문장을 나누는 것
Embedding = Vector로 바꾸는 것
정도로 끝내는 것이 아니라,
왜 Text를 Token으로 나눠야 하는지, Token은 어떤 기준으로 만들어지는지, Token ID는 왜 필요한지, ID가 어떻게 Vector와 연결되는지, 그 Vector는 어떻게 학습되는지, 그리고 최종적으로 어떤 형태의 Tensor가 Transformer에 입력되는지까지 살펴본다.
지난 글에서 다음과 같은 Vector를 사용했다.
[0.18, -0.42, 0.71, 0.09, ...]
Vector라면 덧셈도 할 수 있고, Dot Product도 계산할 수 있다.
하지만 다음 문장은 그렇지 않다.
"나는 오늘 학교에 갔다."
컴퓨터가 이 Text 자체에 바로 Matrix Multiplication을 수행할 수는 없다.
따라서 언어를 계산 가능한 숫자 표현으로 변환해야 한다.
가장 먼저 떠올릴 수 있는 방법은 각 표현에 번호를 붙이는 것이다.
나는 → 3201
오늘 → 745
학교 → 2987
갔다 → 5021
하지만 여기서 3201은 "나는"의 의미를 나타내는 값이 아니다.
5021 > 745
라고 해서 "갔다"가 "오늘"보다 더 크거나 중요한 의미를 가진 것도 아니다.
이 숫자는 표현을 구분하기 위한 Identifier일 뿐이다.
따라서 실제로 필요한 과정은 다음과 같다.
Text
↓
Token
↓
Token ID
↓
Embedding Vector
그리고 첫 번째 질문은 자연스럽게 이것이 된다.
Text를 어떤 단위로 나눌 것인가?
Text를 모델이 처리할 수 있는 단위로 나누고 정수 ID로 연결하는 과정의 중심에 Tokenizer가 있다.
Text를 Token Sequence로 변환하는 과정을 Tokenization이라고 한다.
Tokenizer와 Tokenization은 서로 다른 개념이니 주의하자!
Tokenizer
= Tokenization을 수행하는 구성 요소/도구
Tokenization
= Text를 Token으로 변환하는 과정
예를 들어 설명을 위해 다음 문장을 생각해보자.
나는 오늘 학교에 갔다.
Tokenizer를 거치면 개념적으로 다음처럼 나뉠 수 있다.
["나는", "오늘", "학교", "에", "갔다", "."]
각각의 조각이 Token이다.
Raw Text
↓
Tokenizer
↓
Tokenization
↓
Token Sequence
여기서 가장 먼저 구분해야 하는 것이 있다.
Token ≠ 항상 Word
Token은 단어일 수도 있지만, 단어보다 작은 조각일 수도 있고 문자나 문장부호가 별도의 Token으로 처리될 수도 있다.
실제 Tokenization 결과는 사용하는 Tokenizer에 따라 달라진다.
그렇다면 왜 그냥 한 단어 = 한 Token으로 만들지 않는 걸까?
가장 직관적인 방법은 단어 단위로 Text를 나누는 것이다.
나는 오늘 학교에 갔다.
↓
나는 / 오늘 / 학교에 / 갔다
이를 Word-level Tokenization이라고 생각할 수 있다.
문제는 실제 언어에서 등장할 수 있는 단어의 형태가 매우 많다는 것이다.
한국어만 보더라도:
먹다
먹고
먹었다
먹었어요
먹었지만
먹겠습니다
먹으려고
먹으니까
...
처럼 하나의 어휘에서 다양한 형태가 만들어진다.
신조어와 고유명사도 계속 등장한다.
ChatGPT
ChatGPT가
ChatGPT에게
ChatGPT에서는
...
모든 가능한 표현을 독립적인 Token으로 Vocabulary에 넣으려고 하면 Vocabulary가 매우 커질 수 있다.
그리고 Vocabulary에 없는 표현이 등장하면 OOV(Out-of-Vocabulary) 문제가 발생할 수 있다.
새로운 표현
↓
Vocabulary에 없음
↓
OOV
전통적인 방식에서는 이런 표현을 <UNK> 같은 Unknown Token으로 처리하기도 했다.
하지만 서로 전혀 다른 미등록 단어들이 모두 같은 <UNK>로 바뀐다면 원래 Text에 있던 정보가 손실될 수 있다.
그렇다면 반대로 아주 작은 단위로 나누면 어떨까?
예를 들어 문자 단위로 나눌 수 있다.
안녕하세요
↓
안 / 녕 / 하 / 세 / 요
이렇게 하면 필요한 기본 단위의 종류는 크게 줄어든다.
처음 보는 단어도 작은 단위들의 조합으로 표현할 가능성이 높아진다.
하지만 새로운 문제가 생긴다.
Sequence가 길어진다.
Word-level
큰 단위
↓
Token 수 ↓
Vocabulary Size ↑
Character-level
작은 단위
↓
Token 수 ↑
Vocabulary Size ↓
즉 다음과 같은 Trade-off가 존재한다.
Vocabulary Size
↕
Token Granularity
↕
Sequence Length
여기서 단어와 문자 사이의 절충안으로 등장하는 것이 Subword Tokenization이다.
핵심 아이디어는 다음과 같다.
자주 등장하는 표현은 비교적 큰 단위로 유지하고, 드물거나 새로운 표현은 더 작은 단위의 조합으로 표현한다.
예를 들어 개념적으로:
computer
→ ["computer"]
computerization
→ ["computer", "ization"]
처럼 처리할 수 있다.
실제 분리 결과는 Tokenizer와 Vocabulary에 따라 다르지만 핵심은 같다.
모든 단어를 Vocabulary에 저장하지 않고도 작은 단위들의 조합으로 다양한 Text를 표현할 수 있다.
대표적인 Subword 계열 방식에는 다음과 같은 것들이 있다.
Subword Tokenization
│
├── BPE
├── WordPiece
└── Unigram
이번 글에서는 이 중 BPE(Byte Pair Encoding)를 중심으로 원리를 이해해보자.
BPE의 핵심 아이디어는 비교적 간단하다.
자주 함께 등장하는 인접 단위를 반복적으로 병합한다.
예를 들어 학습 Corpus에 다음 표현이 있다고 가정해보자.
low
lower
lowest
처음에는 작은 단위에서 시작한다.
l o w
l o w e r
l o w e s t
그리고 Corpus에서 인접한 Pair의 등장 빈도를 확인한다.
(l, o)
(o, w)
(w, e)
(e, r)
(e, s)
...
만약 (l, o)가 자주 등장한다면 두 단위를 하나로 병합한다.
l + o
↓
lo
그러면:
lo w
lo w e r
lo w e s t
가 된다.
다시 Pair의 빈도를 계산한다.
이번에는 (lo, w)가 자주 등장한다고 해보자.
lo + w
↓
low
결과적으로:
l / o / w
↓
lo / w
↓
low
처럼 반복되는 패턴이 점점 더 큰 단위가 된다.
이런 병합을 반복하면서 Tokenizer가 사용할 Subword와 Merge Rule을 만들어갈 수 있다.
핵심은:
빈번한 패턴
↓
더 큰 Token으로 병합
드문 패턴
↓
더 작은 Token의 조합으로 표현
하는 것이다.
BPE를 이해할 때 꼭 구분해야 하는 것이 있다.
Tokenizer를 학습하는 과정과 학습된 Tokenizer를 사용하는 과정은 다르다.
Training Corpus
↓
빈도 분석
↓
반복적인 Merge
↓
Vocabulary
+
Merge Rules
이 단계에서 사용할 Token 집합과 Tokenization 규칙이 결정된다.
반면 실제 LLM을 사용할 때는:
New Text
↓
이미 준비된 Tokenizer
↓
Tokens
가 된다.
즉 사용자가 Prompt를 입력할 때마다 BPE가 새로운 Corpus 분석을 시작하고 Vocabulary를 다시 만드는 것이 아니다.
이미 학습·설정된 Tokenizer의 Vocabulary와 규칙을 이용해 새로운 Text를 Tokenize한다.
Tokenizer가 사용할 수 있는 Token의 집합을 Vocabulary(Vocab)라고 한다.
개념적으로 다음처럼 생각할 수 있다.
Vocabulary
Token ID
───────────────────
<pad> 0
<bos> 1
<eos> 2
나는 3
오늘 4
학교 5
에 6
갔다 7
. 8
...
각 Token에는 고유한 정수 번호가 대응된다.
이 번호가 Token ID다.
따라서:
"나는 오늘 학교에 갔다."
↓ Tokenization
["나는", "오늘", "학교", "에", "갔다", "."]
↓ Vocabulary Lookup
[3, 4, 5, 6, 7, 8]
가 된다.
전체 흐름을 다시 보면:
Text
↓
Tokenizer
↓
Tokens
↓
Vocabulary
↓
Token IDs
여기서 반드시 기억해야 하는 것은:
Token ID = Identifier
Token ID ≠ Meaning
라는 점이다.
Token ID 5 자체에는 "학교"의 의미가 들어 있지 않다.
ID는 다음 단계에서 어떤 Vector를 가져올 것인지 찾기 위한 Index 역할을 한다.
Vocabulary에는 일반 Text 조각 외에도 특별한 기능을 담당하는 Token이 포함될 수 있다.
이를 Special Token이라고 한다.
대표적으로 다음과 같은 역할이 있다.
| Token | 대표적인 역할 |
|---|---|
BOS | Sequence의 시작 표시 |
EOS | Sequence의 종료 표시 |
PAD | Batch 등에서 Sequence 길이를 맞출 때 사용 |
UNK | Vocabulary에서 직접 표현할 수 없는 항목을 나타낼 때 사용 |
개념적으로:
<BOS>
나는
오늘
학교에
갔다
<EOS>
처럼 Sequence의 경계를 표현할 수도 있다.
다만 모든 모델이 동일한 이름과 동일한 Special Token 구조를 사용하는 것은 아니다.
특히 현대 Subword/byte 기반 Tokenizer에서는 미등록 Text를 더 작은 단위로 표현할 수 있기 때문에 <UNK>의 실제 필요성과 사용 방식도 Tokenizer에 따라 달라진다.
따라서 이름 자체를 암기하기보다는:
Vocabulary에는 자연어 조각뿐 아니라 모델의 입력 구조나 제어에 사용되는 특별한 Token도 존재할 수 있다.
라고 이해하는 것이 좋다.
여기까지 오면 Text는 숫자로 변했다.
"나는 오늘 학교에 갔다."
↓
[3, 4, 5, 6, 7, 8]
하지만 이 숫자들을 그대로 의미 계산에 사용할 수는 없다.
예를 들어:
오늘 = 4
학교 = 5
라고 해서:
학교 - 오늘 = 1
에는 의미가 없다.
Token ID는 Categorical Identifier에 가깝다.
하지만 0.1에서 살펴본 LLM의 연산에는 이런 형태가 필요했다.
학교
↓
[0.18, -0.42, 0.71, 0.09, ...]
여기서 Embedding이 등장한다.
Embedding은 이산적인 Token을 연속적인 Vector Representation과 연결한다.
개념적으로:
"학교"
↓
Token ID = 5
↓
Embedding
↓
[0.18, -0.42, 0.71, 0.09, ...]
이렇게 얻은 Vector를 Token Embedding 또는 Embedding Vector라고 부를 수 있다.
그런데 여기서 중요한 질문이 생긴다.
ID
5를 어떤 공식으로 계산하면 저 Vector가 나오는 걸까?
단순히 5라는 숫자에 어떤 수학 공식을 적용하는 것이 아니다.
이 구조를 이해하려면 Embedding Matrix를 봐야 한다.
Vocabulary Size를 (V), Embedding Dimension을 (d)라고 하자.
Embedding Matrix (E)는 개념적으로:
[
E \in \mathbb{R}^{V \times d}
]
의 형태를 가진다.
Embedding Dimension (d)
─────────────────────────→
ID 0 [ 0.12, -0.31, 0.44, ... ]
ID 1 [-0.42, 0.11, 0.73, ... ]
ID 2 [ 0.21, 0.38, -0.17, ... ]
ID 3 [ 0.63, -0.19, 0.52, ... ]
ID 4 [-0.11, 0.27, 0.81, ... ]
ID 5 [ 0.18, -0.42, 0.71, ... ]
...
↑
Vocabulary Size (V)
Token ID가 5라면:
Token ID = 5
↓
Embedding Matrix
↓
5번 Row
↓
[0.18, -0.42, 0.71, ...]
를 가져온다.
이것이 Embedding Lookup이다.
즉:
Token ID
│
│ Index
▼
Embedding Matrix
│
│ Lookup
▼
Embedding Vector
라고 이해할 수 있다.
따라서 Token ID는 의미를 직접 담은 숫자가 아니라:
Embedding Matrix에서 어떤 Vector를 가져올지를 지정하는 Index
다.
Embedding Vector에는 하나의 값이 아니라 여러 개의 값이 들어 있다.
[0.18, -0.42, 0.71, 0.09, ..., 0.31]
이 Vector가 몇 개의 숫자로 구성되는지를 Embedding Dimension이라고 한다.
예를 들어:
[0.2, 0.5, 0.7]
dimension = 3
이다.
실제 Transformer에서는 훨씬 높은 차원의 표현을 사용한다.
개념적으로:
Token
↓
Embedding
↓
d-dimensional Vector
Transformer 아키텍처에서는 이 표현의 크기가 모델의 hidden size 또는 d_model과 밀접하게 연결된다. 구체적인 구성은 모델 아키텍처에 따라 차이가 있을 수 있다.
중요한 것은:
Token 하나가 하나의 숫자로 표현되는 것이 아니라, 여러 숫자로 이루어진 고차원 Vector로 표현된다.
는 점이다.
그렇다면:
학교
↓
[0.18, -0.42, 0.71, ...]
에서 저 숫자들은 누가 정한 것일까?
사람이 각 Token의 의미를 분석해서 직접 값을 입력하는 것은 아니다.
Embedding Matrix는 모델의 Learnable Parameter다.
모델 학습 과정에서 다른 Parameter와 함께 값이 조정된다.
개념적으로 보면:
Token IDs
↓
Embedding Matrix
↓
Token Embeddings
↓
Transformer
↓
Prediction
↓
Loss
↓
Backpropagation
↓
Parameter Update
↓
Embedding Matrix Update
처음부터 완성된 의미 Vector가 들어 있는 것이 아니라 모델의 학습 목적에 따라 Prediction Error를 줄이는 방향으로 Embedding Matrix의 값도 조정된다.
따라서:
Embedding Matrix 역시 모델이 학습하는 Parameter의 일부다.
여기서는 Loss, Gradient, Backpropagation의 구체적인 계산까지 들어가지는 않는다.
이 과정은 이후 Training & Inference 편에서 다시 다룬다.
단어를 Vector로 표현한다는 아이디어는 Transformer에서 처음 등장한 것이 아니다.
대표적인 초기 분산 표현 학습 방법 중 하나가 Word2Vec이다.
핵심 아이디어를 단순화하면:
비슷한 문맥에서 등장하는 단어들이 유사한 Vector 표현을 갖도록 학습할 수 있다.
Word2Vec의 대표적인 학습 방식에는 CBOW와 Skip-gram이 있다.
주변 단어를 이용해 중심 단어를 예측한다.
Context Words
↓
CBOW
↓
Target Word
중심 단어를 이용해 주변 단어를 예측한다.
Target Word
↓
Skip-gram
↓
Context Words
여기서 중요한 것은 Word2Vec 알고리즘 자체를 외우는 것이 아니다.
언어의 의미적·분포적 관계를 Vector 공간에 표현할 수 있다는 Embedding의 직관을 이해하는 것이다.
그리고 이 개념은 Transformer에서 더 중요한 질문으로 이어진다.
같은 단어라도 문맥에 따라 의미가 달라진다면 Vector도 달라져야 하지 않을까?
다음 두 문장을 보자.
I deposited money at the bank.
I sat on the bank of the river.
두 문장 모두 "bank"라는 표현을 포함하지만 의미는 다르다.
첫 번째는 금융기관이고 두 번째는 강둑이다.
여기서 Token Embedding과 Transformer를 거친 Contextual Representation을 구분해야 한다.
Token
↓
Token Embedding
↓
Transformer Layers
↓
Contextual Representation
Token Embedding은 Transformer가 계산을 시작하기 위한 초기 수치 표현이다.
Transformer에서는 주변 Token과의 관계가 계산된다.
첫 번째 문장에서는:
deposited
│
money ── bank
두 번째 문장에서는:
river
│
bank ── sat
처럼 주변 Context가 다르다.
Transformer Layer를 거치면서 각 위치의 내부 표현은 주변 Token의 정보를 반영해 변화한다.
전통적인 Static Embedding과 비교하면 차이가 더 명확하다.
Static Word Embedding
Word
↓
Vector
동일한 Word
→ 기본적으로 동일한 학습된 표현
Transformer-based Model
Token
↓
Initial Token Embedding
↓
Transformer Layers
↓
Contextual Representation
동일한 Token이라도
문맥에 따라 내부 표현이 달라질 수 있음
따라서 중요한 구분은:
Embedding
≠
Context Understanding
이다.
Token Embedding은 문맥 이해의 최종 결과가 아니라 Transformer가 문맥 계산을 시작하기 위한 출발점이다.
이제 Token도 Vector로 변환했다.
그런데 또 하나의 정보가 필요하다.
다음 두 문장을 비교해보자.
고양이가 개를 쫓았다.
개가 고양이를 쫓았다.
비슷한 단어들이 등장하지만 의미는 다르다.
순서가 다르기 때문이다.
자연어에서는 Token의 위치가 의미를 결정하는 중요한 정보다.
Self-Attention 연산 자체에는 순서를 별도로 알려줄 필요가 있기 때문에 Transformer에는 Position Information이 필요하다.
개념적으로:
Token Embedding
+
Position Information
↓
Transformer Input
초기 Transformer에서는 Sinusoidal Positional Encoding이 사용되었고, 모델에 따라 학습 가능한 Positional Embedding 같은 방식도 사용된다.
현대 LLM에서는 RoPE(Rotary Position Embedding)처럼 Attention 계산에 위치 정보를 반영하는 방식도 널리 사용된다.
여기서는 각각의 수학적 구현까지 깊게 들어가지는 않는다.
이번 단계에서 기억해야 할 것은:
Token이 무엇인지에 대한 정보뿐 아니라 Sequence 안에서 어디에 위치하는지에 대한 정보도 필요하다.
는 점이다.
Token은 하나씩 독립적으로 입력되는 것이 아니라 순서대로 Sequence를 구성한다.
["나는", "오늘", "학교", "에", "갔다", "."]
Token ID로 보면:
[3, 4, 5, 6, 7, 8]
Token이 6개이므로:
Sequence Length = 6
이다.
즉 Sequence Length는 입력 Sequence를 구성하는 Token의 수다.
이 개념은 LLM을 실제로 사용할 때 자주 접하게 되는 Context Window와 연결된다.
Raw Text
↓
Tokenization
↓
Token Count
↓
Sequence Length
↓
Context Window
LLM은 무한한 길이의 Text를 한 번에 처리하지 않는다.
모델은 정해진 범위의 Context를 처리하도록 설계된다.
여기서 중요한 사실은:
글자 수 ≠ Token 수
단어 수 ≠ 항상 Token 수
라는 것이다.
Tokenizer와 언어, Text의 구성에 따라 같은 길이처럼 보이는 문장도 서로 다른 수의 Token으로 나뉠 수 있다.
그래서 LLM API나 모델 문서에서 다음과 같은 표현을 자주 보게 된다.
Input Tokens
Output Tokens
Context Length
결국 이 개념들도 Tokenization에서 출발한다.
여기서 지난 글의 Vector, Matrix, Tensor를 다시 가져와보자.
Embedding Dimension이 4라고 가정한다.
각 Token이 다음 Vector로 변환되었다고 해보자.
나는
[0.2, 0.7, 0.1, 0.4]
오늘
[0.5, 0.3, 0.8, 0.2]
학교
[0.9, 0.1, 0.4, 0.6]
Token 하나는 Vector다.
Token
↓
Embedding
↓
Vector
여러 Token Vector를 Sequence 순서대로 쌓으면:
[
[0.2, 0.7, 0.1, 0.4],
[0.5, 0.3, 0.8, 0.2],
[0.9, 0.1, 0.4, 0.6]
]
Matrix가 된다.
Shape은:
[sequence_length, embedding_dimension]
↓
[3, 4]
이다.
여러 Sequence를 하나의 Batch로 묶으면 차원이 하나 더 추가된다.
[batch_size, sequence_length, embedding_dimension]
예를 들어:
[8, 128, 768]
이라면:
8 → Batch Size
128 → Sequence Length
768 → Embedding / Hidden Dimension
으로 읽을 수 있다.
즉 지난 글에서 배웠던 구조가 실제 LLM에서 이렇게 연결된다.
Vector
↓
Token 하나의 표현
Matrix
↓
Token Sequence의 표현
Tensor
↓
Batch 단위의 모델 입력
이제 Vector, Matrix, Tensor가 단순한 수학 개념이 아니라 실제 모델 안에서 어떤 데이터를 표현하는지 보이기 시작한다.
이제 실제 Tokenizer를 사용해보자.
Python의 transformers 라이브러리를 이용하면 모델과 함께 제공되는 Tokenizer를 직접 확인할 수 있다.
from transformers import AutoTokenizer
model_name = "bert-base-multilingual-cased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
text = "나는 오늘 학교에 갔다."
tokens = tokenizer.tokenize(text)
token_ids = tokenizer.encode(
text,
add_special_tokens=False
)
print("Text:")
print(text)
print("\nTokens:")
print(tokens)
print("\nToken IDs:")
print(token_ids)
실제 Token과 ID는 사용하는 Tokenizer에 따라 달라진다.
여기서 확인하려는 것은 특정 숫자가 아니다.
구조다.
Text
↓
Tokenizer
↓
Tokens
↓
Token IDs
ID를 다시 Token으로 바꿔볼 수도 있다.
converted_tokens = tokenizer.convert_ids_to_tokens(token_ids)
print(converted_tokens)
이를 통해:
Token
↕
Vocabulary
↕
Token ID
의 대응 관계를 확인할 수 있다.
이번에는 PyTorch의 nn.Embedding을 사용해 Embedding Matrix와 Lookup 구조를 직접 확인해보자.
import torch
import torch.nn as nn
vocab_size = 10
embedding_dim = 4
embedding = nn.Embedding(
num_embeddings=vocab_size,
embedding_dim=embedding_dim
)
token_ids = torch.tensor([3, 4, 5])
vectors = embedding(token_ids)
print(vectors)
print(vectors.shape)
3개의 Token ID와 4차원 Embedding을 사용했으므로 결과 Shape은:
[3, 4]
가 된다.
3 Tokens
×
4 Dimensions
↓
[3, 4]
Embedding Matrix 자체도 확인할 수 있다.
print(embedding.weight.shape)
결과는:
[10, 4]
이다.
왜냐하면:
Vocabulary Size = 10
Embedding Dimension = 4
↓
Embedding Matrix Shape
[10, 4]
이기 때문이다.
이 코드의 핵심은 nn.Embedding의 문법을 외우는 것이 아니다.
실제 연산 구조를 확인하는 것이다.
Token ID
↓
Embedding Matrix
↓
Row Lookup
↓
Embedding Vector
이제 처음 질문으로 돌아가보자.
"학교"
↓
?
↓
[0.18, -0.42, 0.71, ...]
이제 ? 안에 무엇이 있었는지 설명할 수 있다.
Raw Text
│
▼
Tokenizer
│
▼
Tokenization
│
├── Word-level
├── Character-level
└── Subword
│
└── BPE
│
▼
Tokens
│
▼
Vocabulary
│
▼
Token IDs
│
▼
Embedding Matrix
│
│ Lookup
▼
Token Embeddings
│
├───────────────┐
│ │
│ Position Information
│ │
└───────┬───────┘
▼
Input Representations
│
▼
Transformer
이번 과정을 크게 세 단계로 압축할 수도 있다.
Text
↓
Tokenizer
↓
Tokens
Tokens
↓
Vocabulary
↓
Token IDs
Token IDs
↓
Embedding Matrix
↓
Token Vectors
↓
Position Information
↓
Transformer Input
결국:
Language
↓
Discrete Representation
↓
Continuous Vector Representation
으로 변환되는 과정이다.
| 개념 | 의미 | LLM에서의 역할 |
|---|---|---|
| Tokenizer | Text를 Token 단위로 처리하는 구성 요소 | Text → Model Input 변환 |
| Tokenization | Text를 Token Sequence로 변환하는 과정 | 입력 단위 생성 |
| Token | Tokenizer가 처리하는 기본 단위 | 모델 입력의 기본 단위 |
| Word-level | 단어 중심 Tokenization | 직관적이지만 큰 Vocabulary/OOV 문제 가능 |
| Character-level | 문자 중심 Tokenization | 작은 Vocabulary, 긴 Sequence |
| Subword | 단어보다 작은 재사용 가능한 단위 | Vocabulary와 Sequence Length 사이의 균형 |
| BPE | 빈번한 인접 단위를 반복적으로 병합 | Subword Vocabulary 구성 |
| OOV | Vocabulary에 없는 표현 | Tokenization 설계가 해결해야 하는 문제 중 하나 |
| Vocabulary | Token 집합과 ID 체계 | Token ↔ ID 연결 |
| Token ID | Token의 정수 식별자 | Embedding Lookup의 Index |
| Special Token | 특별한 기능을 담당하는 Token | Sequence 구조 및 모델 제어 |
| Embedding | Token을 Vector Representation과 연결 | 모델이 계산할 수 있는 입력 생성 |
| Embedding Matrix | Token별 Vector를 저장하는 학습 가능한 Matrix | Token ID → Vector |
| Embedding Lookup | ID에 해당하는 Row를 가져오는 과정 | Token Vector 생성 |
| Embedding Dimension | Token Vector의 차원 | Token 표현 크기 |
| Learnable Parameter | 학습을 통해 값이 조정되는 Parameter | Embedding Matrix도 학습 대상 |
| Word2Vec | 분산 단어 표현을 학습하는 대표적 방법 | Embedding 개념의 배경 |
| Position Information | Sequence에서 Token의 위치 정보 | Token 순서 반영 |
| Sequence Length | Sequence를 구성하는 Token 수 | 입력 크기 |
| Context Window | 모델이 처리할 수 있는 Context 범위 | Token 입력·출력 범위와 연결 |
| Contextual Representation | 문맥이 반영된 내부 Vector 표현 | Transformer Layer를 통해 형성 |
이제 Text를 Vector로 바꾸는 과정까지 왔다.
Text
↓
Tokenization
↓
Token
↓
Token ID
↓
Embedding
↓
Vector
하지만 Vector가 만들어졌다고 해서 LLM이 문맥을 이해한 것은 아니다.
다시 두 문장을 보자.
I deposited money at the bank.
I sat on the bank of the river.
bank가 어떤 의미인지를 판단하려면 주변 Token과의 관계를 봐야 한다.
deposited
│
money ── bank
river
│
bank ── sat
그렇다면 다음 질문은:
하나의 Token Vector가 다른 Token Vector와 어떤 관계를 갖는지 어떻게 계산할까?
가 된다.
여기서 지난 글의 Dot Product가 다시 등장한다.
Vector A
│
│ Dot Product
│
Vector B
↓
Relation Score
그리고 Transformer에서는 이 아이디어가 Query, Key, Value와 만나 Attention으로 이어진다.
Token Embeddings
↓
Query · Key
↓
Attention Score
↓
Softmax
↓
Attention Weight
↓
Value
↓
Contextual Representation
지난 글에서 배운 Dot Product와 Softmax가 왜 필요했는지도 이제 드러나기 시작한다.
다음 글에서는 Transformer의 핵심으로 들어간다.
왜 RNN만으로는 부족했을까?
↓
Attention은 무엇을 해결했을까?
↓
Query · Key · Value
↓
Scaled Dot-Product Attention
↓
Self-Attention
↓
Multi-Head Attention
↓
Transformer Block
↓
Contextual Representation
그리고 지금까지의 세 글이 하나의 흐름으로 연결된다.
1. Vector & NumPy
"Vector를 어떻게 계산하는가?"
↓
2. Tokenization & Embedding
"Language를 어떻게 Vector로 만드는가?"
↓
3. Attention & Transformer
"Vector 사이의 관계로 Context를 어떻게 만드는가?"
우리는 이제 LLM의 전체 흐름 중:
Language → Number → Context → Generation
에서
Language → Number
까지 왔다.
다음은 Number → Context다.