16장. 말을 숫자로 — 토큰과 임베딩
이 장의 질문: "사랑"이라는 단어를 어떻게 숫자로 바꾸되, 의미를 잃지 않게 하는가? 텍스트를 조각내는 토큰화와, 의미를 기하학으로 바꾸는 임베딩 — 언어 AI 전체의 주춧돌 두 개를 놓는다.
전제: 3장(벡터, 내적), 7장(자기지도의 아이디어), 9장(표현 학습).
1단계: 텍스트를 조각내기 — 토큰화
신경망에 문장을 넣으려면 먼저 문장을 이산적인 단위 — 토큰 — 의 나열로 잘라야 한다. 자르는 단위의 선택부터가 설계 문제다.
단어 단위로 자르면 자연스럽지만 치명적 구멍이 있다 — 어휘가 무한하다. 신조어, 오타, 고유명사, "먹었겠더라" 같은 활용형까지 사전에 다 담을 수 없고, 사전에 없는 단어(OOV)를 만나면 시스템이 벙어리가 된다. 글자 단위로 자르면 모르는 글자는 없지만, "사"라는 글자 하나에는 의미가 거의 없고 시퀀스가 너무 길어진다.
현대의 답은 중간 — 서브워드(subword)다. 대표 알고리즘인 BPE(Byte Pair Encoding)의 원리는 압축 알고리즘처럼 단순하다: 글자(정확히는 바이트) 단위에서 출발해, 코퍼스에서 가장 자주 붙어 나오는 인접 쌍을 하나로 합치는 규칙을 원하는 어휘 크기가 될 때까지 반복해서 배운다. 결과적으로 자주 쓰는 단어("학교")는 통째로 한 토큰이 되고, 드문 단어("탈탄소화")는 의미 조각들("탈"+"탄소"+"화")로 쪼개진다. 자주 쓰는 것은 짧게, 드문 것은 조합으로 — 모르는 단어라는 개념 자체가 사라진다(어떤 문자열도 최악의 경우 바이트로는 표현된다).
토큰화는 배관 작업 같지만, LLM 시대의 여러 현상이 여기서 설명되므로 결코 사소하지 않다. LLM의 요금과 컨텍스트 길이 제한이 "토큰 수" 단위인 것, 같은 내용이라도 학습 데이터에 적었던 언어는 토큰이 잘게 쪼개져 비용·성능에서 손해 보는 것, 모델이 글자 수 세기나 철자 뒤집기에 의외로 약한 것(모델은 글자가 아니라 토큰 덩어리를 보기 때문) — 전부 토큰화의 그림자다. 코드랩 07에서 BPE를 밑바닥부터 만들어 보면 이 감각이 손에 붙는다.
2단계: 조각에 의미를 — 임베딩
토큰에 번호를 붙였다 치자("고양이"=1523). 이 번호를 그대로 신경망에 넣으면 안 된다 — 1523이라는 크기에는 아무 의미가 없는데, 신경망은 숫자의 크기와 거리로 계산하기 때문이다("고양이"(1523)가 "국세청"(1522)과 이웃이라는 헛소리를 배우게 된다).
해법: 각 토큰마다 학습되는 벡터를 하나씩 배정한다. 어휘가 5만 개이고 벡터를 512차원으로 하면, 5만×512짜리 표 — 임베딩 행렬 — 가 생기고, 토큰 번호는 이 표에서 자기 행을 꺼내는 색인일 뿐이다. 처음에는 전부 무작위 벡터다. 의미는 어디서 오는가?
학습이 만든다. 그리고 그 학습 신호의 원천이 언어학의 오래된 통찰이다 — "단어의 의미는 그 단어가 어울리는 이웃들이 결정한다" (분포 가설). "고양이"와 "강아지"는 서로 다른 단어지만 비슷한 문맥("__를 키운다", "__가 귀엽다")에 나타난다. 그러니 문맥으로부터 단어를 맞추는 과제(7장의 자기지도!)를 풀게 하면 — 예컨대 주변 단어들로 가운데 단어를 예측하게 하면 — 비슷한 문맥에 나오는 단어들은 비슷한 예측에 쓰여야 하므로, 그 벡터들이 자연스럽게 가까워진다. 이것이 word2vec으로 대표되는 고전적 단어 임베딩 학습이고, 현대 LLM에서는 별도 절차 없이 "다음 토큰 예측" 학습(21장) 속에서 임베딩 행렬이 함께 길러진다.
의미의 기하학
학습된 임베딩 공간에서 벌어지는 일은, 처음 보면 마술 같다.
거리가 유사도가 된다. "고양이"와 "강아지"의 벡터는 가깝고(내적/코사인 유사도가 큼 — 3장의 "닮음의 측정"이 여기서 만개한다), "고양이"와 "미분방정식"은 멀다. 동의어 사전을 만들어 준 적이 없는데, 문맥 예측 과제가 그것을 부산물로 만들어 냈다.
방향이 관계가 된다. 유명한 예 —
이 기하학은 곧바로 실용이 된다. 문장·문서 전체를 벡터 하나로 요약하는 문장 임베딩 모델을 쓰면, "질문 벡터와 가장 가까운 문서 벡터 찾기" = 키워드가 안 겹쳐도 통하는 의미 검색이 된다. 뒤에서 배울 RAG(23장)의 절반이 이것이고, 추천 시스템("이 상품 벡터와 가까운 상품")도 같은 문법이다. 감을 잡는 코드는 이 정도로 짧다:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-m3") # 다국어 문장 임베딩 모델
docs = ["환불 규정은 구매 후 7일 이내입니다.",
"고양이는 하루 16시간을 잔다.",
"결제 취소는 일주일 안에 가능해요."]
q = "산 물건 돌려줄 수 있나요?"
vecs, qv = model.encode(docs), model.encode([q])
print((vecs @ qv.T).ravel()) # 유사도: 1번·3번 문서가 높다 — 단어가 하나도 안 겹치는데!남은 문제: 문맥
여기까지의 임베딩에는 근본적 한계가 하나 있다. 토큰마다 벡터가 하나라는 것. 그런데 "배"는 먹는 배, 타는 배, 신체의 배다. "은행"은 돈을 맡기는 곳이기도, 강가이기도 하다. 단어의 진짜 의미는 문맥 속에서 정해지는데, 표에서 꺼낸 고정 벡터는 문맥을 모른다.
그러니 다음 과제가 명확해진다 — 문장 속의 다른 토큰들을 보고, 각 토큰의 벡터를 그 자리의 의미로 갱신하는 장치. "나는 배를 타고"라는 문맥에서는 "배" 벡터가 선박 쪽으로, "배가 고파서"에서는 음식·신체 쪽으로 움직여야 한다. 이 장치를 만드는 것이 5부의 나머지 전부이며, 그 완성형이 어텐션과 Transformer다. 임베딩이 단어의 사전적 의미를 놓는 주춧돌이라면, 다음 두 장은 그 위에 문맥적 의미를 세우는 건축이다.
핵심 요약
- 토큰화: 텍스트를 서브워드 조각으로 자른다(BPE — 자주 붙는 쌍을 병합). OOV가 사라지는 대신, LLM의 비용·길이 제한·철자 취약성 등 여러 현상이 토큰 단위에서 비롯된다.
- 임베딩: 토큰마다 학습되는 벡터. "문맥으로 단어 맞추기"(자기지도) 과제가 분포 가설을 타고 의미를 벡터에 새긴다.
- 학습된 공간에서 거리는 유사도, 방향은 관계가 된다 — 언어가 기하학이 되고, 그 즉시 의미 검색·추천 같은 실용이 열린다.
- 한계: 토큰당 벡터 하나는 다의어의 문맥을 담지 못한다 → 문맥으로 벡터를 갱신하는 장치(어텐션)가 다음 과제.
스스로 점검
- "가장 자주 나오는 인접 쌍을 병합"을 코퍼스 "aaabdaaabac"에 두 번 적용해 보라. (첫 병합 후보는 무엇인가?)
- LLM이 "strawberry에 r이 몇 개냐"류 질문에 약한 이유를 토큰화로 설명해 보라.
은 어디쯤에 도착해야 하는가? 이런 구조가 "문맥으로 단어 맞추기"에서 어떻게 생겨날 수 있는지 한 문단으로 추론해 보라.- 위 코드의 의미 검색이 키워드 검색(단어 일치)과 결정적으로 다른 점은 무엇이며, 반대로 키워드 검색이 더 나은 경우(예: 제품 코드 검색)는 왜 생기는가?
다음 장에서
고정 임베딩 위에 문맥을 세우는 여정 — 순서대로 읽어 나가는 RNN의 시도와 그 좌절, 그리고 "필요한 곳을 직접 바라본다"는 발상의 전환, 어텐션이다. 현대 AI를 만든 단 하나의 아이디어를 꼽으라면 바로 이것이다.