본문 바로가기

기술

LLM 기초 해부: 토크나이저부터 단어 임베딩의 동적 분화까지

 

밑바닥부터 만들면서 배우는 LLM 책을 읽기 시작했습니다. 1, 2장을 읽으면서 떠오르는 궁금증들을 Gemini에게 물어보고 조사한 내용을 정리해보았습니다.

Build a LLM from Scratch

1. 최신 LLM은 어떻게 단어를 쪼갤까? (Tokenizer)

  • 질문
    • 단순히 정규표현식(RE) 토크나이저를 이용하면 안될까?
    • 사전에 없는 단어를 <unknown> 토큰으로 치환하는 이유는 무엇인가? (BPE를 사용하지 않는 토크나이저)
    • GPT-5나 Gemini 3 같은 최신 모델은 어떤 토크나이저를 사용할까?
  • 답변 요약
    • RE의 한계: '학습 데이터에 없는 단어(OOV)'가 나오면 처리가 불가능하고, 모든 단어를 사전에 넣으면 메모리가 폭발함.
    • 최신 모델은 서브워드(Subword) 단위로 쪼개는 BPE(Byte Pair Encoding)를 사용함.
    • BPE 원리: 자주 나오는 문자 조합을 병합하여 사전을 만듦. 이를 통해 모르는 단어도 알고 있는 조각들의 조합으로 이해할 수 있음.

GPT나 Gemini의 과금표에 나오는 토큰이 무엇인지 항상 궁금했는데 책을 통해 알게 되었습니다.  그 정체가 대단한 것은 아니고 보통 단어와 구두점이 토큰이 됩니다.

 

텍스트를 토큰화하는 토큰화 과정에서 첫 번째로 떠오른 질문은 단순한 정규표현식을 이용하지 않고 특별한 토크나이저를 사용해야 하는 이유였습니다. 전체 텍스트를 띄어쓰기로 구분되는 단어와 구두점으로 나누는 것은 정규표현식으로 쉽게 달성할 수 있음에도 굳이 직접 토크나이저를 만드는 실습이 존재하는 이유가 궁금했습니다. 또한 사전의 크기를 책 한 권에서 등장하는 단어들로 한정해두고, 새로운 텍스트에서 모르는 단어를 만났을 때 <unknown> 토큰으로 치환하는 이유가 궁금했습니다.

 

두 가지 질문은 "왜 무한한 크기의 사전을 사용할 수 없는가?"로 이어집니다. 먼저 RE를 통해 단순히 띄어쓰기 단위로 단어를 사전에 등록하면 비슷한 의미를 가진 단어의 변형을 모두 사전에 등록해야 합니다. <unknown> 토큰 역시 사용하고 싶지 않다면 존재하는 모든 단어를 사전에 등록하면 해결됩니다.

 

그럼 무한한 크기 사전은 왜 사용할 수 없을까요? 이에 대해서는 Gemini에게도 물어보고 조사해봤을 때 완벽하게 답을 이해하지 못하여 제가 납득한 내용을 정리하겠습니다. LLM은 "다층" 신경망을 사용하는 데 초점을 맞춘 딥러닝의 한 갈래입니다. LLM의 목표는 다음 단어를 예측하는 것인데 모델의 마지막 층에서 다음에 올 단어를 최종 계산할 때 전체 사전을 훑어보고 결정합니다. 따라서 사전의 크기가 커질수록 연산과 메모리 부하가 증가합니다.

 

저는 다층 신경망 모델, 딥러닝에 대해 전혀 아는 바가 없습니다. 다만 저 내용을 읽었을 때 머릿속에 그려지는 이미지는 입력으로 텍스트가 들어가고 여러 층(layer)를 통과하면서 점차 출력이 구체화되는 그림이 그려집니다. 마지막 층을 지나면서 최종 예측한 다음 단어를 결정할 때 사전의 크기가 지대한 영향을 미치고 이 때문에 무한 크기 사전이 어렵다는 것을 어렴풋이 이해할 수 있었습니다.

 

그럼 최신 LLM들은 어떤 토크나이저를 사용하는지 궁금했습니다. 책에서 실습한 토크나이저와 같이 <unknown> 토큰을 사용하는 토크나이저 대신 Byte Pair Encoding(BPE) 알고리즘을 가진 토크나이저를 사용한다고 합니다. 단어를 바이트 단위로 더 쪼개고 병합하여 사전을 구축해 처음 보는 단어도 바이트의 조합으로 이해할 수 있습니다. 

 

BPE의 장점으로 세상에 새로운 단어가 등장했을 때 대응과 중복된 의미를 하나로 묶을 수 없는 효율성에 대한 이야기가 있지만 이는 다음 글에서 새로운 질문과 함께 더 자세히 파헤쳐보겠습니다.

2. 왜 학습 중에 사전에 새 단어를 추가하면 안 될까?

  • 질문: 모르는 단어가 나오면 <unknown>으로 바꾼다는데, 그냥 그때그때 사전에 추가하면 안 되나?
  • 답변 요약:
    • 구조적 제약: 모델의 임베딩 층과 마지막 소프트맥스 층은 행렬 크기가 고정되어 있음. 학습 도중에 행렬의 줄(Row)을 늘리는 것은 엔진이 돌아가는 중에 부품 크기를 바꾸는 것과 같아 불가능함.
    • 지식의 불균형: 새로 추가된 단어는 학습 데이터가 없어 '랜덤값' 상태임. 이는 이미 학습된 다른 단어들 사이에서 맥락을 파악하는 데 방해가 됨.
    • BPE의 우아함: 최신 모델은 단어를 바이트 단위까지 쪼개서 어떻게든 '아는 조각'으로 만들기 때문에 <unk> 자체가 거의 필요 없음.

첫 번째 질문인 토큰화에서 이어지는 질문입니다. 새로운 단어를 마주칠 때마다 추가한다는 말은 사전의 크기를 계속 늘리겠다는 말과 다르지 않습니다. 사전의 크기가 무한히 늘어나지 못하는 이유는 전 질문의 답변에서 설명되었으므로 넘어갈 수 있습니다. 그런데 한 두개만 늘어나는 것도 문제가 되는 걸까요?

 

일단 Gemini의 답변을 미루어 보았을 때 레이어를 거치면서 행렬 연산을 한다는 것을 짐작할 수 있습니다. LLM이 학습하는 과정을 잘 모르지만 학습의 시작부터 행렬의 크기가 사전 크기에 맞춰 정의되어야 한다면 한 두개라도 단어를 자꾸 추가하는 것이 기존 구조를 크게 바꾸기 때문에 어렵다고 이해하였습니다.

 

새로운 단어가 추가되었을 때 맥락을 파악하는 데 방해가 된다는 점은 아직 이해하지 못했습니다. 다른 단어와의 연결성을 어떻게 표현하는지 구체적으로 모르기 때문에 그 이미지를 떠올리기 어렵습니다. 이에 대해서는 책을 더 읽으면서 맥락을 수학적으로 어떻게 표현하는지 이해하면 다시 읽어보겠습니다.

 

BPE에 대해서 Gemini 는 단순히 바이트의 조합으로 설명했지만 여전히 몇 가지 질문이 떠오릅니다. 바이트로 조합한 단어는 최종적으로 어떤 벡터가 되어 어떻게 연산하는지 잘 모르기 때문에 <unk>가 필요없다는 것은 이해했지만 BPE가 왜 문제를 해결하는지 역시 다음 질문으로 남겨두겠습니다.

3. 데이터를 어떻게 수학적으로 변환할까? (Embedding)

  • 질문: LLM은 단어 단위 임베딩만 하는데 동음이의어 구분이 되나? 아니면 문장이나 단락 단위 임베딩도 수행하나?
  • 답변 요약:
    • 시작은 정적(Static): 입력 단계에서는 '사과(과일)'와 '사과(사죄)'가 똑같은 벡터값을 가짐.
    • 과정은 동적(Dynamic): 트랜스포머의 어텐션(Attention) 레이어를 통과하면서 주변 단어들과 상호작용함.
    • 결과 (Contextualization): 레이어를 거칠수록 각 토큰은 문맥 정보를 흡수해 '분화'됨. 즉, 입력은 단어 단위지만 출력은 문장/단락의 의미가 응축된 벡터가 됨.

우선 임베딩(Embedding)이란 텍스트, 비디오, 오디오 등의 데이터를 벡터(숫자의 나열) 형태로 변환하는 것을 말합니다. 통계학 입문 책을 보면 데이터를 크게 수학적 계산이 가능한 수치형 데이터와 그렇지 않은 범주형 데이터로 나눌 수 있습니다. (범주형 데이터는 또 명목형과 순서형 데이터로 나뉩니다.) 이때 범주형 데이터에 속하는 텍스트, 비디오, 오디오를 수학 연산에 적합한 벡터로 치환하는 과정이 필요합니다.

 

책에서 LLM은 텍스트를 임베딩할 때 주로 단어 임베딩을 활용하고, 문장이나 단락 임베딩은 RAG(retrieval-augmented generation)에서 활용된다고 말합니다. 여기서 궁금증이 생겼습니다. 문장이나 단락을 이용하지 않고, 단어만 벡터로 바꾸면 사과(과일)와 사과(사죄)가 동음이의어이므로 같은 벡터(숫자)로 바뀔 것입니다. 그럼 LLM은 어떻게 단어 임베딩만으로 동음이의어를 구분하는지 이해가 되지 않았습니다.

 

그 이유를 Gemini에게 물어보니 3가지 단계로 나누어 임베딩 과정을 설명해주었습니다. 가장 처음 토큰화 후 토큰 ID 벡터로 바뀔 때는 사과(과일)와 사과(사죄)가 동일한 벡터가 되는 게 맞습니다. (이 때 단어의 위치도 임베딩하여 입력에 추가된다고 합니다.) 그 다음 어텐션 레이어를 통과하면서 주변 단어들과 상호작용하면서 가중치를 계산합니다. 이 과정에서 동음이의어들도 벡터값이 분화됩니다. 모든 레이어를 통과하고 나면 단순한 단어 벡터가 아닌 맥락 정보를 가진 벡터가 완성되고, 사과(과일)와 사과(사죄)를 구분할 수 있습니다.

 

저는 임베딩을 가장 처음 토큰화 단계를 의미한다고 생각했는데 좀 더 전체적인 관점에서 단어를 벡터화한다는 것을 알게 되었습니다. 사실 다층 신경망 모델에 대해 정확히 알진 못해서 아직 구체적으로 임베딩이 어느 단계를 의미하는지 이해하진 못했습니다. 일단 어느 정도 머릿속에 이미지를 구축해놓고 계속 읽어가면서 구체화해보겠습니다.

4. 패딩(Padding)과 마스크(Mask)의 비밀

  • 질문: 패딩 토큰이 무엇이든 결과에 영향을 주지 않는다는데, 마스크와 패딩의 차이는 무엇인가?
  • 답변 요약:
    • 패딩은 '자리 채우기', 마스크는 '가림막': 여러 문장의 길이를 맞추기 위해 가짜 토큰(Padding)을 넣지만, 모델이 이를 읽으면 안 됨.
    • 수학적 무력화: 어텐션 연산 시 마스크(Mask)를 씌워 패딩 위치의 값을 -로 만듦.
    • Softmax의 마법: -에 Softmax를 적용하면 확률이 0이 됨.
    • 결론: 패딩 자리에 어떤 토큰 ID(사과든, 바나나든)가 있더라도 0이 곱해지므로 모델의 최종 판단에는 아무런 영향을 주지 않음.

우선 질문이 나온 배경부터 설명하면, LLM에 따라 사용하는 특수 토큰 중에 <PAD>가 있습니다. 이는 LLM을 둘 이상의 배치(Batch) 사이즈로 훈련할 때 길이가 다른 텍스트의 길이를 맞추기 위해 <PAD> 토큰을 사용해 맞춰줄 때 사용한다고 합니다. 그런데 책에서 이에 대한 부연 설명에서 마스크를 사용하기 때문에 패딩 토큰이 중요하지 않아 무엇을 사용하든 결과에 영향을 주지 않는다는 언급이 있습니다. 쉽게 설명하여 패딩 토큰으로 🍎을 사용하든 🍌를 사용하든 상관없다고 합니다.

 

일단 배치 입력 학습이 무엇인지 전혀 모르기 때문에 Gemini의 설명을 이해하기 굉장히 어려웠습니다. 마스크 개념 역시 어텐션 알고리즘을 통해 설명하는데 수식을 이해하지 못해 아직 제대로 이해하지 못했습니다. 다만 머릿속 상상으로 답변을 해석하면, 마스킹을 씌워 패딩 토큰의 수학적 값을 음의 무한대로 만들고, Softmax 함수의 인자로 음의 무한대를 넣으면 0이 나옵니다. 그리고 나올 확률이 0이기 때문에 패딩 토큰은 다음 단어로 등장하지 않고 따라서 어떤 문자를 쓰던 상관없다고 이해했습니다. (Gemini에게 좀 더 물어보니 등장 확률이 0이 아니라 다음 단어를 예측할 때 패딩 토큰의 정보를 전혀 참고하지 않는 가중치의 의미가 더 정확하다고 합니다.)

 

뒷 부분을 읽으면서 배치 입력과 어텐션 알고리즘에 대해 좀 더 깊이 이해하면 다시 돌아와 답변을 이해해보겠습니다.

두 장만 읽어도 배울 게 많다.

지금까지 1장 ~ 2장 4절까지 읽고 떠오른 질문들을 Gemini와 대화하면 학습한 내용을 정리했습니다. 아직 책의 본격적인 내용은 시작도 안했는데 새롭게 알게된 내용이 많습니다. 제가 AI에 대한 기초 지식이 없기 때문이기도 하지만 항상 글을 읽을 때 여러 질문을 던지다 보면 생각할 게 정말 많은 것 같습니다.

 

새해가 되어서 책 읽는 방법을 조금 바꿔보려 합니다. 빠르게 진도를 빼기보다 읽으면서 생긴 질문들을 모두 해소하고 글로 정리하면서 넘어갈 예정입니다. 혼자서 여러 가지 질문을 던져보고 다양한 관점으로 생각해보려 합니다. AI가 발전한 세상을 살면서 느낀 점은 인풋을 늘리는 것보다 더 많이 사고하면서 고품질의 아웃풋을 늘리는 게 더 중요하다는 점입니다. 그래서 꾸역꾸역 책을 읽고 얕은 지식을 늘리기보다 계속 생각하고 답하는 식으로 책을 읽어보겠습니다.