백엔드 개발자의 시선으로 풀어본 LLM 내부 동작 원리: 6단계로 쉽게 이해하기

백엔드 개발자의 시선으로 풀어본 LLM 내부 동작 원리: 6단계로 쉽게 이해하기

요약: LLM이 사용자의 질문을 받아 답변을 생성하기까지의 내부 동작 과정을 6단계(토큰화, 임베딩, 위치 인코딩, 트랜스포머 & 어텐션, 예측, 디코딩)로 나누어 설명합니다.

💡 리뷰어 한줄평

snow.y LLM의 내부 원리를 너무 깊지 않게, 딱 필요한 만큼만 알고 싶은 개발자를 위한 최고의 가이드입니다. 단 30분 투자로 LLM을 보는 시야를 넓혀보세요!

yun.cheese 백엔드 개발자의 시선으로 LLM의 블랙박스를 열어, ‘왜 그렇게 써야 하는지’에 대한 기술적 궁금증을 6단계로 명쾌하게 풀어낸 원리 해설서.

시작하며

안녕하세요, 카카오페이 정산플랫폼팀에서 정산 플랫폼을 개발하고 있는 와이입니다.

LLM을 현업에서 활용하거나, LLM의 내부 동작 원리를 이해해 더 효과적으로 사용하고 싶은 백엔드 개발자를 위해 이 글을 작성했습니다.

동작 원리를 파고들다 보면 끝이 보이지 않기도 하고, 관련 책을 보면 많은 내용이 부담스럽게 느껴질 수 있습니다. 하지만 LLM이 어떻게 내 말을 이해하고 대답하는지 궁금하면서도 원론적인 내용은 부담스러운 분들에게, 이 글이 큰 흐름을 가볍게 이해하는 데 도움이 되었으면 좋겠습니다.

저 역시 LLM을 유용하게 사용하면서, 그 내부가 어떤 원리로 동작하는지 궁금해졌습니다. LLM으로부터 좋은 답변을 얻기 위한 프롬프트 작성법이나 활용법을 들었을 때, 왜 그런 방식이 효과적인지 몰랐지만, 동작 원리를 알고 나니 그 이유를 이해할 수 있었습니다.

이 글에서는 LLM이 사용자의 질문을 받아 답변을 생성하기까지의 내부 동작 과정을 6단계(토큰화, 임베딩, 위치 인코딩, 트랜스포머와 어텐션, 예측, 디코딩)로 나누어 설명하려고 합니다. LLM 기반 서비스나 기능을 개발 중이거나, ChatGPT, Gemini 등 대화형 인공지능을 업무에 적극적으로 활용하고 싶은 분들에게 도움이 될 것이라고 기대합니다.

LLM을 사용하는 비전문가인 백엔드 개발자의 관점에서 작성된 것으로, 전문가의 시각과는 다를 수 있습니다. 내용 중 오류를 발견하시면 언제든지 피드백 부탁드립니다. 🙇🏻‍♂️

LLM 내부 동작 원리 6단계

단계동작설명
1단계Tokenization프롬프트를 토큰 단위로 분리하고, 각 토큰에 고유한 ID 할당하기
2단계Embedding토큰화된 ID를 의미를 담은 벡터로 변환하기
3단계Positional Encoding단어의 순서를 인식할 수 있도록 위치 정보를 벡터에 더하기
4단계Transformer & Attention문맥을 파악하고, 각 단어의 표현을 정교하게 다듬기
5단계Prediction다음에 올 토큰 예측하기
6단계Loop & Decoding예측된 토큰을 반복적으로 디코딩하여 문장 완성하기

1단계: Tokenization

사용자의 프롬프트를 LLM이 이해할 수 있는 최소 단위인 토큰(Token) 으로 분리하고, 각 토큰에 고유한 ID를 매핑하는 단계

토큰화(Tokenization)는 LLM이 문장을 이해하기 위해 의미 있는 최소 단위로 분리하는 과정입니다. 마치 우리가 문장을 읽을 때 단어 단위로 끊어 읽는 것과 비슷한데, LLM은 인식하지 못하는 새로운 단어가 등장하면 그 단어를 더 작은 단위로 쪼개어 해석합니다.

  • 예를 들어 “인공지능”이라는 단어는 학습 데이터에 따라 “인공”과 “지능”이라는 두 개의 토큰으로 나뉠 수 있습니다.

토큰화는 다음과 같은 과정으로 진행됩니다.

  • “LLM의 내부 동작 원리.”라는 문장은 ['LLM', '의', '내부', '동작', '원리', '.']처럼 의미 있는 단위로 나뉩니다.
  • 각 토큰은 고유한 정수 ID에 매핑됩니다. (예: [73, 12, 36, 63, 96, 91])

Tokenization
Tokenization

Gemini 모델은 토큰화를 위해 SentencePiece라는 토크나이저를 사용합니다. SentencePiece는 기존 토크나이저와 비교해 몇 가지 중요한 차별점이 있으며, 토큰을 고유한 정수 ID에 매핑하는 과정을 다음과 같이 구현합니다.

  • 언어 중립적: 공백을 기준으로 단어를 나누지 않고, 문장을 유니코드 문자들의 연속으로 보고, 자주 함께 등장하는 글자들을 통계적으로 묶어 하나의 토큰으로 만듭니다.
    • 한국어처럼 조사가 단어에 붙거나 공백의 의미가 크지 않은 언어를 처리하는 데 매우 효과적
  • 서브워드(Subword) 기반: 어휘 사전에 없는 새로운 단어나 긴 단어가 등장하면, 이를 더 작은 의미 단위(Subword)로 분해합니다.

📝 Example Code

SentencePiece 토크나이저를 사용해 보면서 LLM에서 입력 문장이 어떻게 토큰화되고, 내부적으로 어떤 정수 ID로 처리되는지, 그리고 다시 사람이 읽을 수 있는 문장으로 복원되는지 확인해 보겠습니다.

import sentencepiece as spm

# --- 훈련 데이터 준비 ---
training_data = """제미나이는 구글이 개발한 인공지능 언어 모델입니다.
LLM 내부 동작 원리가 궁금하지 않나요?
LLM 내부 동작 원리를 함께 알아보아요.
"""

with open("sample-data.txt", "w", encoding="utf-8") as f:
    f.write(training_data)

# --- 1. 토크나이저 모델 훈련 ---
# SentencePiece 토크나이저 모델 훈련
# 'sample_tokenizer.model', 'sample_tokenizer.vocab' 파일이 생성
spm.SentencePieceTrainer.train(
    '--input=sample-data.txt ' # 훈련시킬 데이터 파일
    '--model_prefix=sample_tokenizer ' # 생성될 토크나이저 모델 파일의 이름
    '--vocab_size=133 ' # 단어 집합의 크기 (어휘의 총 개수)
    '--model_type=bpe' # 토크나이저 모델 타입 (bpe, unigram ..)
)

# --- 2. 훈련된 토크나이저 모델로 토큰화 실행 ---
sp = spm.SentencePieceProcessor()

# 훈련된 토크나이저 모델 파일 로드
sp.load('sample_tokenizer.model')

# 테스트할 문장
sentence1 = "나는 LLM 내부 동작 원리를 공부한다."

# 문장을 토큰으로 분리
# _(언더바) 기호는 띄어쓰기(공백)를 의미
tokens1 = sp.encode_as_pieces(sentence1)
print(tokens1) # ['▁', '나', '는', '▁LLM', '▁내부', '▁동작', '▁원리를', '▁', '공', '부', '한', '다', '.']

# 문장을 정수 ID로 변환
ids1 = sp.encode_as_ids(sentence1)
print(ids1) # [89, 93, 113, 11, 8, 9, 44, 89, 107, 97, 131, 116, 91]

# 정수 ID를 다시 문장으로 복원
decoded_sentence = sp.decode_ids(ids1)
print(decoded_sentence) # 나는 LLM 내부 동작 원리를 공부한다.

🏃🏼‍♂️ 1단계 예제 코드 실행해 보기

SentencePieceTrainer.train() 함수는 훈련 데이터 파일을 통계적으로 분석해, 어떤 글자들을 하나의 토큰으로 묶을지 결정합니다. 이렇게 만들어진 규칙은 토크나이저 모델(.model)에 저장되며, 어휘 사전(.vocab)에는 생성된 토큰 목록과 각 토큰의 점수가 함께 기록됩니다.

  • 어휘 사전의 토큰 점수는 토크나이저 훈련 과정에서 빈도(해당 토큰이 얼마나 자주 등장하는지) 또는 우선순위(모델링 과정에서 해당 토큰이 얼마나 중요한지)를 기준으로 매겨집니다.
  • 여기서의 점수로그 확률(어떤 토큰이 등장할 확률을 로그 함수로 변환한 값) 또는 비용(토큰을 선택하는 데 드는 비용)을 의미하고, 토크나이저가 텍스트를 분할할 때, 어떤 토큰 조합이 더 적합한지 판단하는 기준(가장 낮은 비용 (=가장 높은 확률))으로 사용합니다.
...
개발	-49
구글	-50
궁금	-51
내부	-52
동작	-53
리가	-54
리를	-55
...

🤔 그렇다면, LLM은 매 요청마다 사용자 입력을 토크나이저 훈련에 사용할까?

LLM의 토크나이저는 실시간으로 요청이 들어올 때마다 사용자 입력을 추가로 훈련시키지 않고, 훈련(Training)과 추론(Inference)이라는 두 단계로 명확하게 구분하여 동작합니다.

👉🏻 Training Phase

  • 인공지능 언어 모델 개발사(Google, OpenAI 등)가 모델 생성 초기에 단 한 번, 대규모 데이터로 토크나이저를 학습시키는 단계
  • 이 과정에서 수십 테라바이트에 달하는 방대한 텍스트 데이터를 활용해 토크나이저를 학습시키고, 결과적으로 토크나이저 모델(.model)어휘 사전(.vocab) 파일을 생성

👉🏻 Inference Phase

  • 사용자가 질문을 할 때마다 일어나는 단계
  • LLM은 이미 만들어진 토크나이저를 단순히 메모리에 로드해서 사용하며, 사용자의 문장을 이 규칙에 따라 즉시 토큰으로 변환하고 응답을 생성

토크나이저 모델의 다양한 알고리즘

Tokenizer Algorithm
Tokenizer Algorithm

👉🏻 BPE 기본 원리

  • 텍스트 내 모든 개별 문자를 초기 어휘로 설정
  • 현재 어휘로 구성된 텍스트에서 가장 자주 함께 등장하는 문자 쌍 찾기
  • 가장 빈번한 문자 쌍을 하나의 새로운 서브워드 토큰으로 병합(예: ”l o w“에서 ’o‘와 ’w‘가 자주 나타나면 ’ow‘로 병합)
  • 새로운 토큰을 어휘에 추가하고, 미리 정의한 어휘 크기에 도달하거나 더 이상 병합할 유의미한 쌍이 없을 때까지 이 과정을 반복

BPE는 이처럼 자주 쓰이는 단어는 긴 토큰으로 만들고, 희귀하거나 새로운 단어는 여러 짧은 토큰으로 분리합니다.

👉🏻 ULM 기본 원리

  • 매우 큰 초기 어휘 집합을 생성
  • 각 서브워드 토큰이 텍스트에 나타날 확률을 계산(해당 서브워드의 빈도수 기반)
  • 주어진 단어를 여러 서브워드로 분절할 수 있는 모든 가능한 경우의 수를 고려
    • 예를 들어, ”unigram“이라는 단어가 있을 때, un + igram, uni + gram, u + ni + gram 등 다양한 분절이 가능한데, 이 중에서 가장 높은 확률을 가지는 분절 조합을 선택
  • 어휘 집합의 크기를 줄여나가는 과정에서는 전체 확률 분포에 미치는 영향이 가장 작은 서브워드를 제거하며, 미리 정해둔 vocab_size에 도달할 때까지 반복

ULM은 이처럼 확률을 기반으로 다양한 분절 조합을 평가해 최적의 토큰 분할을 찾습니다.


1단계 Tokenization 세줄 요약

  • 입력 문장을 의미 있는 최소 단위(토큰)로 분리하는 과정
  • 각 토큰에 고유한 정수 ID를 할당해 LLM이 이해할 수 있는 형태로 변환
  • 새로운 단어나 복잡한 표현도 서브워드 단위로 쪼개어 처리

2단계: Embedding

토큰화된 정수 ID를 의미를 가진 다차원 공간의 좌표(벡터) 로 변환하는 단계

임베딩은 단순히 숫자를 다른 숫자로 바꾸는 것이 아니라, 단어의 의미와 뉘앙스를 수치적으로 표현하는 핵심 과정입니다.

임베딩 결과, 각 토큰 ID(정수 ID)와 그에 대응하는 임베딩 벡터를 저장한 Embedding Table이라는 사전이 생성됩니다.

  • 예를 들어, ’Jeans‘라는 단어는 토큰화 과정을 거쳐 1423이라는 토큰 ID를 부여받고, 이 토큰 ID는 768차원의 좌표 [-0.2416, -0.0663, 0.4457, ...]로 저장됩니다.
  • 이러한 좌표값들은 방대한 데이터를 학습하는 과정에서 모델이 얻어낸 결과물입니다.

Embedding Table
Embedding Table

벡터 간의 거리는 의미론적 근접성(Semantic Proximity) 이라는 성질을 통해 단어들 사이의 관계를 수학적으로 이해하고 추론할 수 있게 해줍니다.

  • 하지만 임베딩 결과로 얻어지는 수백 차원의 벡터 공간은 우리가 직접 눈으로 확인하거나 직관적으로 상상하기 어렵습니다.
  • 이때 PCA(Principal Component Analysis, 주성분 분석) 와 같은 차원 축소 기술을 활용하면, 데이터의 가장 중요한 특징(주성분)만 남기고 수백 개의 차원을 2개의 숫자(x, y 좌표)로 압축할 수 있습니다.

고차원의 임베딩 벡터를 PCA로 2차원으로 축소해 시각화하면, ‘자동차’와 ‘오토바이’는 ‘탈 것’이라는 공통된 의미로 인해 좌표가 서로 가깝게 나타나고, ‘자동차’와 ‘에어컨’처럼 의미적 관련성이 없는 단어들은 좌표가 멀리 떨어져 있는 것을 볼 수 있습니다.

  • 이처럼, 비슷한 의미를 가진 단어들은 서로 가까운 위치에, 관련 없는 단어들은 먼 위치에 존재함을 시각적으로 확인할 수 있습니다.

Principal Component Analysis
Principal Component Analysis

문맥적 유연성

  • 임베딩은 단어, 문장, 문단 등 다양한 길이와 형태의 텍스트를 문맥에 맞게 의미를 반영한 벡터로 표현할 수 있는 능력을 가지고 있습니다.
  • 이 덕분에 LLM은 단순히 단어 뜻만이 아니라, 문맥에 따라 달라지는 의미까지 임베딩 벡터로 효과적으로 다룰 수 있게 됩니다.

📝 Example Code

한국어 특화 모델인 ko-sentence-transformers(한국어 사전학습 모델을 활용한 문장 임베딩)을 로컬에 직접 다운로드한 뒤, 텍스트를 의미가 담긴 벡터로 변환하기 위해 sentence-transformers 라이브러리를 활용하여 임베딩 단계를 재현해 보겠습니다.

from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity

# --- 1. Hugging Face Hub에서 한국어 특화 모델을 로컬 환경에 다운로드 후 메모리에 로드 ---
try:
    model = SentenceTransformer('jhgan/ko-sroberta-multitask')
except Exception as e:
    print(f"모델 로딩 중 오류가 발생했습니다: {e}")
    exit()

# --- 2. 임베딩으로 변환할 단어들을 정의 ---
words = ["자동차", "자전거", "행복"]

# --- 3. 모델을 사용하여 각 단어를 벡터로 변환 (내부적으로 토큰화와 임베딩을 수행) ---
embeddings = model.encode(words)

# --- 4. 생성된 벡터의 형태와 일부 값 확인 ---
print(f"'{words[0]}' 단어의 임베딩 벡터 차원(모델의 임베딩 차원): {embeddings[0].shape}")
print(f"'{words[0]}' 단어의 임베딩 벡터 일부: {embeddings[0][:5]}\n")

# --- 5. 단어 간의 코사인 유사도를 계산하여 의미적 거리 확인 (결괏값이 1에 가까울수록 의미가 가깝다는 뜻) ---
sim_car_bicycle = cosine_similarity([embeddings[0]], [embeddings[1]])
sim_car_happiness = cosine_similarity([embeddings[0]], [embeddings[2]])
print(f"'{words[0]}'와(과) '{words[1]}'의 코사인 유사도: {sim_car_bicycle[0][0]:.4f}")
print(f"'{words[0]}'와(과) '{words[2]}'의 코사인 유사도: {sim_car_happiness[0][0]:.4f}")

🏃🏼‍♂️ 2단계 예제 코드 실행해 보기

'자동차' 단어의 임베딩 벡터 차원: (768,) # 벡터에 들어있는 숫자의 개수(예를 들어 RGB 이미지의 경우 3차원)
'자동차' 단어의 임베딩 벡터 일부: [ 0.04892848  0.2749219   0.15132347 -0.5050876  -0.23192444] # LLM이 학습한 '자동차'의 의미를 수치적으로 표현한 벡터

'자동차'와(과) '자전거'의 코사인 유사도: 0.8315 # '탈 것'이라는 공통된 의미로 높은 유사도
'자동차'와(과) '행복'의 코사인 유사도: 0.1854 # 의미적 관련성이 없어 낮은 유사도

2단계 Embedding 세줄 요약

  • 토큰화된 정수 ID를 의미를 담은 고차원 벡터(임베딩 벡터)로 변환하는 과정
  • 각 토큰은 Embedding Table에서 자신만의 좌표(벡터)를 가짐
  • 이 벡터는 단어의 의미와 문맥을 수치적으로 표현하며, 모델이 학습을 통해 얻은 결과

3단계: Positional Encoding

LLM의 핵심인 Attention 메커니즘(문장 속에서 어떤 단어가 다른 단어에 얼마나 영향을 미치는지 수치화하여 문맥을 정확하게 파악)은 문장 내 단어들의 관계를 파악하는 데 매우 뛰어납니다.

  • 예를 들어, “나는 사과(과일)를 먹었다.” 문장과 “나는 너에게 사과(용서)했다.” 문장의 문맥을 파악할 수 있습니다.

하지만 어떤 단어가 먼저 나왔는지, 즉 순서를 알지 못한다는 단점이 있습니다. 이 때문에 “고양이가 강아지를 이겼다”“강아지가 고양이를 이겼다” 를 구분하지 못하는 문제가 발생합니다.

Positional Encoding은 바로 이 문제를 해결하기 위해 각 단어의 순서 정보를 담은 고유한 벡터를 만들어 단어의 임베딩 벡터에 더해주는 단계입니다.

최종 입력 벡터 = Word Embedding Vector(의미 벡터: 2단계 Embedding 결과) + Positional Encoding Vector(순서 벡터)

🤔 잠깐! 어떻게 위치마다 고유한 벡터를 만들까?

  • 가장 고전적이고 널리 쓰이는 방법은 sine, cosine 함수를 이용하는 방식입니다.
  • 각 단어의 위치와 벡터의 차원에 따라 서로 다른 주파수를 가진 sin, cos 값을 계산해 고유한 위치 벡터를 생성합니다.
수식:
PE(pos, 2i)   = sin(pos / 10000^(2i / d_model))  # 짝수 인덱스 차원
PE(pos, 2i+1) = cos(pos / 10000^(2i / d_model))  # 홀수 인덱스 차원

- d_model: 모델의 '크기'를 나타내는 임베딩 벡터의 총 차원 수
- 10000: 다양한 주파수를 만들기 위해 스케일링을 위한 상수
- pos: 현재 단어의 시퀀스 내 위치
- i: 임베딩 벡터의 차원 인덱스 (0 ~ d_model-1)

📝 Example

Final Embedding = Word Embedding Vector + Positional Encoding Vector

고양이가 소파 위에서 잠을 잡니다.

   [0.6525 0.266 0.5645 0.7149 0.6867 ...] ('고양이' 의미 벡터)
+  [0 1 0 1 0 ...] (0번째 위치 벡터)
----------------------------------------------------------------
   [0.6525 1.266 0.5645 1.7149 0.6867 ...] ('고양이'의 최종 임베딩)

   [0.8779 0.2883 0.0404 0.0478 0.6339 ...] ('잠을' 의미 벡터)
+  [-0.7568 -0.6536 -0.6572 -0.7537 -0.5486 ...] (3번째 위치 벡터)
----------------------------------------------------------------
   [ 0.1211 -0.3653 -0.6168 -0.7059 0.0853 ...] ('잠을'의 최종 임베딩)

🏃🏼‍♂️ 3단계 예제 코드 실행해 보기


3단계 Positional Encoding 세줄 요약

  • sin, cos 함수를 활용해 위치마다 서로 다른 값을 갖는 벡터를 생성
  • 각 단어의 순서 정보를 담은 고유한 벡터를 임베딩 벡터에 더하는 과정
  • 이를 통해 LLM이 문장 내에서 단어의 위치와 순서를 인식

4단계: Transformer & Attention

복잡한 문장을 이해하고 생성하는 기능을 가능하게 한 핵심 기술은 Transformer라는 신경망 구조와 그 안에 있는 Attention이라는 메커니즘

Transformer & Attention은 훈련(Training)과 추론(Inference) 과정에서 모두 사용됩니다.

  • 훈련: 입력 데이터를 받아서 모델이 정답을 예측하고, Transformer와 Attention 구조를 통해 파라미터를 학습
  • 추론: 사용자가 입력을 주면, 같은 Transformer와 Attention 구조를 거쳐 출력을 생성

Transformer는 LLM의 효율적이고 강력한 두뇌 역할을 합니다.

  • 어떻게 데이터를 처리할지를 정의하는 신경망 구조
  • 훈련 단계에서 이 구조를 사용해 대규모 데이터로 모델을 학습
  • 문장 전체의 단어들을 한 번에 병렬로 처리하여 빠른 학습이 가능

Attention은 LLM이 문장 속에서 어떤 단어가 다른 단어에 얼마나 영향을 미치는지를 계산해, 문맥을 더 정확하게 이해하도록 돕는 핵심 메커니즘입니다.

  • 문장 내 모든 단어들 간의 관련도에 집중함으로써, 복잡한 문맥과 다의어를 더 정확하게 이해
  • 문장이 길어져도 멀리 떨어진 단어들 사이의 관계를 효과적으로 파악

Transformer인코더디코더라는 두 가지 주요 블록으로 구성되어 있으며, 각 블록 안에는 핵심 구성 요소인 셀프 어텐션(Self-Attention) 메커니즘피드 포워드 신경망(Feed-Forward Neural Network, FFN) 이 반복적으로 나타납니다.

The Transformer
The Transformer

ref. Attention Is All You Need

인코더 레이어

입력 텍스트를 모델이 이해할 수 있는 내부 표현으로 변환하는 역할을 합니다.

👉🏻 입력 단계

  • Input Embedding 벡터와 Positional Encoding 벡터를 더해 최종 입력 벡터를 생성합니다. (3단계 결과)

👉🏻 인코더 레이어

  • Multi-Head Self-Attention: 입력 문장 내의 모든 단어(토큰)가 서로에게 얼마나 집중해야 하는지, 즉 서로 간의 관계를 계산합니다.
    • 예를 들어, “그가 물을 마셨다” 에서 ‘그’‘마셨다’‘물을’ 에 더 집중해야 함을 파악
    • Multi-Head: 여러 개의 독립적인 헤드(주의 집중 장치) 가 동시에 다양한 관점에서 관계(예: 주어-동사, 명사-형용사 등)를 파악하고, 그 결과를 결합
  • Feed-Forward Neural Network: 어텐션 레이어를 통과한 벡터(문맥 정보가 반영된 단어 표현)를 받아, 각 단어의 벡터를 독립적이고 비선형적으로 변환하여 더 풍부하고 복잡한 특징 표현을 생성합니다.

디코더 레이어

인코더에서 변환된 정보를 바탕으로 새로운 텍스트를 생성하는 역할을 합니다.

👉🏻 입력 단계

  • Outputs: 생성될 문장의 시작을 알리는 특수 토큰(BOS (beginning of sequence))과 이미 생성된 단어들(이전에 디코더가 생성한 단어)을 입력으로 삽입합니다.
    • 예를 들어, ‘나는’ 을 이미 생성했다면, 다음 단어를 예측할 때 “[BOS] 나는” 을 입력으로 사용
  • shifted right: 훈련 시 정답 시퀀스를 한 칸 뒤로 밀어 넣어, 현재 시점의 단어를 예측할 때 미래의 단어를 볼 수 없도록 하는 방식
    • 예를 들어, 정답 시퀀스가 [A, B, C]라면 디코더의 입력은 [시작토큰, A, B] 가 되고, 디코더는 각 시점에서 다음 단어[B, C, 종료토큰] 를 예측
    • 이렇게 하면 모델이 지금까지 생성된 단어만 보고 다음 단어를 예측하도록 훈련
  • 인코더 레이어의 입력과 마찬가지로 Output Embedding 벡터와 Positional Encoding 벡터를 더해 최종 출력 벡터를 생성합니다.

👉🏻 디코더 레이어

  • Masked Multi-Head Self-Attention: 디코더는 단어를 생성하는 역할을 하므로, 현재 단어를 예측할 때 아직 생성되지 않은 미래의 단어를 참조할 수 없도록 해야 합니다.
    • 여기서 Masked는 미래 단어에 대한 어텐션 점수를 0으로 만들어 참조하지 못하게 하는 것을 의미
    • 이 외의 작동 방식은 인코더의 멀티-헤드 어텐션과 유사
  • Multi-Head Self-Attention: 인코더의 출력디코더의 Masked Self-Attention의 출력을 동시에 참조합니다.
    • 이는 디코더가 입력 문맥(인코더 출력)과 자신이 지금까지 생성한 문맥을 모두 고려하여 다음 단어를 예측
  • Feed-Forward Neural Network: 앞선 두 어텐션 레이어의 출력을 받아 각 단어별 특징을 강화
    • 즉, 어텐션이 문맥을 파악한 뒤, 각 단어의 의미를 한 번 더 깊게 가공하는 단계

👉🏻 출력 단계

  • Linear: 디코더 레이어의 최종 출력(각 토큰별로 의미와 문맥이 반영된 고차원 벡터)을 모델의 전체 단어 개수(어휘 사전 크기)만큼의 차원을 가진 벡터로 변환합니다.
    • 디코더의 최종 출력이 [10, 768] 형태(예: 10개 토큰, 768차원 벡터)라면, Linear 레이어를 거쳐 [10, 50000]처럼 모델 전체 단어 개수만큼의 차원으로 변환
    • 디코더가 생성한 각 단어의 특징 벡터를 모델이 알고 있는 모든 단어 각각에 대해 다음 단어로 등장할 가능성 점수(Logits) 계산
  • Softmax: Linear 단계에서 계산된 점수를 확률(Output Probabilities) 로 변환합니다.
    • 가장 높은 확률을 가진 단어가 최종 출력으로 선택

📖 참고. Add & Norm

  • Add(잔차 연결): 이전 레이어의 입력 값을 현재 레이어의 출력 값에 더하는 방식
    • 신경망이 여러 층을 거치면서 정보가 소실되거나 왜곡되는 문제를 줄여줌
    • 즉, 중요한 정보가 다음 단계로 잘 전달되고, 학습이 더 안정적으로 이루어질 수 있음
    • 딥러닝 모델의 성능 향상과 학습 안정성에 크게 기여
  • Norm(레이어 정규화): 신경망의 각 레이어 출력값을 평균 0, 분산 1로 맞추어 데이터 분포를 정규화하는 기법
    • 값이 너무 크거나 작아지는 현상을 방지하고, 모든 레이어가 비슷한 분포의 데이터를 처리할 수 있어 학습이 더 안정적이고 빠르게 진행
    • 즉, 신경망이 각 레이어에서 일관된 데이터 분포를 유지하도록 도와주는 단계

요약하면, Transformer는 기본적으로 Self-Attention 메커니즘으로 문맥 관계를 파악하고,

Feed-Forward Neural Network로 각 단어의 표현을 정제하고 강화하는 과정을 반복하며 텍스트를 이해하고 생성합니다.


4단계 Transformer & Attention 세줄 요약

  • 문장 전체의 단어들 사이 관계를 계산해, 각 단어의 의미와 문맥을 정교하게 다듬는 과정
  • Self-Attention 메커니즘을 통해 복잡한 문맥과 단어의 상호작용을 효과적으로 파악
  • 인코더·디코더 구조와 반복적인 피드 포워드 신경망으로 텍스트를 이해하고 생성

5단계: Prediction

문맥을 파악한 최종 벡터를 바탕으로 다음에 올 가장 확률이 높은 단어(토큰)를 예측하는 단계

모델은 어휘 사전(.vocab)에 있는 모든 단어에 대해 다음에 올 확률을 계산하고, 이 확률 분포에서 하나의 토큰을 선택합니다.
예를 들어, “LLM의 내부 동작 원리는” 다음에 ‘매우’, ‘복잡’, ‘합니다’ 등의 토큰이 높은 확률을 가질 수 있습니다.

지금까지 모든 과정을 거쳐 문맥을 완벽히 파악한 벡터가 준비되었습니다.

  • 1단계: Tokenization (프롬프트를 토큰으로 분리하고 고유 ID 매핑)
  • 2단계: Embedding (토큰화된 정수 ID를 의미를 가진 벡터로 변환)
  • 3단계: Positional Encoding (단어의 순서를 파악하기 위해 위치 정보 더하기)
  • 4단계: Transformer and Attention (문맥 관계를 파악하고 각 단어의 표현을 정제)

이제 LLM은 이 벡터를 가지고 “그래서, 다음에 올 단어는 무엇일까?” 라는 질문에 답해야 하며, 이 단계는 크게 확률 계산, 샘플링 두 부분으로 나뉩니다.

확률 계산

LLM은 자신이 알고 있는 수만 개의 모든 단어(토큰)를 후보로 올려놓고, 다음에 등장할 확률을 각각 계산합니다.
이 과정은 Transformer에서 설명한 디코더 레이어의 출력 단계(Linear → Softmax) 를 거쳐 나온 결과입니다.

  • 등장 가능성 점수(Logits) 생성: 문맥을 파악한 최종 벡터를 이용해 어휘 사전의 모든 단어에 대해 등장 가능성 점수(Logit) 를 매깁니다.
  • 확률(Softmax) 변환: 이 점수들을 확률(%)로 변환하며, 모든 단어의 확률을 합하면 1(100%)이 됩니다.

이제 모델은 “다음에 ‘매우’가 올 확률은 15%(0.15), ‘복잡’이 올 확률은 12%(0.12)…” 와 같은 확률 분포표를 갖게 됩니다.

Logits & Softmax
Logits & Softmax

이렇게, 모델이 각 토큰의 확률을 계산한 뒤, Temperature확률 분포를 조절하고, 변화된 확률 분포를 기반으로 샘플링 전략을 사용해 최종적으로 토큰을 선택하게 됩니다.

👉🏻 Temperature

  • 샘플링을 적용하기 전에 Softmax 함수를 통해 모델의 확률 분포를 조절
  • 매번 가장 확실한 단어만 선택하는 경직된 패턴에서 벗어날 수 있음

Temperature 매개변수가 적용되지 않았을 때의 영향

  • T = 1이 적용되어 원래 점수와 동일

No temperature
No temperature

높은 Temperature 매개변수가 적용되었을 때 확률 분포에 미치는 영향

  • Temperature가 높으면(T > 1) 가장 높은 확률을 가진 단어의 확률은 낮아지고, 다른 단어들의 확률은 상대적으로 높아지면서, 모델이 다양하고 예측 불가능한 단어를 선택할 가능성이 높아져 창의적인 출력을 생성
  • 반면, Temperature가 너무 높으면 문맥과 관련 없는 단어가 선택되어 텍스트의 일관성이 떨어질 수 있음
  • 예제에서는 확률 감소폭이 점차 줄어드는 것을 확인할 수 있음

High temperature
High temperature

낮은 Temperature 매개변수가 적용되었을 때 확률 분포에 미치는 영향

  • Temperature가 낮으면(T < 1) 가장 높은 확률을 가진 단어의 확률은 더 높아지고, 다른 단어들의 확률은 더 낮아지면서, 모델이 가장 확실한 선택을 하도록 유도하여 보수적이고 반복적인 출력을 생성할 가능성이 높아짐

Low temperature
Low temperature

샘플링(Sampling)

가장 확률이 높은 단어만 계속 선택하면 매우 예측 가능하고 지루한 문장이 생성되므로, LLM은 더 창의적인 문장을 만들기 위해 다양한 샘플링 전략을 사용합니다.

  • 무조건 확률 1등 단어만 선택

Greedy Search
Greedy Search

👉🏻 Top-K Sampling

  • 확률 순위가 높은 K개(예: 3개)의 단어들 중 확률에 따라 선택
  • 전체 어휘가 아닌, 확률이 가장 높은 K개의 단어 중에서만 샘플링하므로 다양성을 어느 정도 유지하면서도 엉뚱한 단어가 나오는 것을 방지
  • K값 설정에 따라 출력 품질이 크게 변화되고, K값이 너무 작으면 선택 가능한 단어의 폭이 좁아져 특정 구절이나 단어가 반복될 수 있음

Top-K Sampling
Top-K Sampling

👉🏻 Top-P Sampling

  • 확률의 합이 P(예: 70%)가 될 때까지 상위권 단어들을 후보로 올리고, 후보에 올라온 단어들 중 확률에 따라 선택
  • 문맥에 따라 동적으로 선택할 단어의 수를 조절하고, 누적 확률을 기준으로 단어를 선택하기 때문에, 다양한 단어가 나올 수 있는 확률의 핵심 영역에 집중하여 더 자연스러운 다양성을 보장
    • 확실한 예측이 가능한 문맥에서는 적은 수의 단어만 고려하고, 다양한 예측이 가능한 문맥에서는 더 많은 단어를 고려
    • Gemini와 같은 최신 모델들이 주로 사용하는 핵심 전략
  • 최적의 P값을 찾는 것이 어려울 수 있으며, P값이 너무 높으면 Top-k와 마찬가지로 관련성 없는 단어가 선택되어 일관성이 떨어질 가능성이 있음

Top-P Sampling
Top-P Sampling

이 과정을 통해 한 단어를 선택하고, 그 단어를 다시 입력에 추가하여 다음 단어를 예측하는 과정을 반복하면 비로소 하나의 문장이 완성


5단계 Prediction 세줄 요약

  • 문맥 정보를 바탕으로 어휘 사전의 모든 단어에 대해 다음에 올 확률을 계산하는 과정
  • Linear와 Softmax 단계를 거쳐 각 단어의 확률 분포를 생성
  • 다양한 샘플링 전략(Top-K, Top-P, Temperature 등)을 활용해 최적의 토큰을 선택

6단계: Loop & Decoding

예측된 토큰을 다시 입력값에 추가한 뒤, 4~6단계를 반복하여 다음 단어를 계속 생성합니다. 이 과정은 EOS(end of sequence) 토큰이 생성될 때까지 이어지며, 최종적으로 완성된 문장을 사용자에게 반환합니다.

  • 이 단계는 새로운 계산을 하는 것이 아니라, 5단계에서 설명한 Prediction(다음 토큰 예측) 과정을 자동으로 반복시켜 문장을 완성하는 엔진입니다. LLM이 긴 글을 쓸 수 있는 비결이 바로 이 반복 구조에 있습니다.
  • 이 과정을 자동 회귀(Autoregressive) 라고 부르며, 혼자서 릴레이 소설을 쓰는 것과 유사합니다.

Loop

1️⃣ 예측(Predict): 현재까지의 문장을 보고 다음 단어 하나를 예측 (5단계 과정)

  • LLM의 내부 동작 원리는매우

2️⃣ 추가(Append): 예측된 단어를 원래 문장 끝에 이어 붙여 새로운 입력값을 생성

  • 새로운 입력: LLM의 내부 동작 원리는 매우

3️⃣ 반복(Loop): 길어진 문장을 가지고 다시 1번으로 돌아가 다음 단어를 예측

  • LLM의 내부 동작 원리는 매우복잡
  • 새로운 입력: LLM의 내부 동작 원리는 매우 복잡
  • LLM의 내부 동작 원리는 매우 복잡하지만

‘Predict-Append-Loop’ 루프는 다음 두 가지 조건 중 하나를 만족할 때까지 반복됩니다.

  • [EOS] 토큰 등장: 모델이 문장의 끝을 의미하는 특수 토큰(End-Of-Sentence)을 생성할 때
  • 최대 길이 도달: 미리 설정한 최대 단어 개수(예: 100개)에 도달할 때

Predict-Append-Loop
Predict-Append-Loop

Decoding

루프가 모두 끝나고 최종적으로 생성된 정수 ID들의 배열 [3141, 912, 114, ...]을 사람이 읽을 수 있는 하나의 완성된 텍스트로 다시 변환하는 과정입니다.
❗️ 참고로, 지금까지의 예시는 이해를 돕기 위해 한글로 설명했지만, 실제 내부 동작에서는 2단계(Embedding)부터 6단계의 디코딩 직전까지 모든 과정이 정수 ID(토큰 ID)를 기반으로 처리됩니다.


6단계 Loop & Decoding 세줄 요약

  • 예측된 토큰을 반복적으로 입력에 추가하여 문장을 완성하는 과정
  • EOS 토큰이 나오거나 최대 길이에 도달할 때까지 Predict-Append-Loop를 반복
  • 최종적으로 생성된 토큰 시퀀스를 사람이 읽을 수 있는 텍스트로 디코딩해 사용자에게 응답

LLM 내부 동작 원리 활용하기

LLM의 동작 원리를 이해하면 LLM을 단순한 채팅봇이 아닌, 프로그래밍 가능한 언어 인터페이스로 활용할 수 있습니다.

1️⃣ 풍부하고 구체적인 콘텍스트 제공
“코드에 에러가 나” 보다는 “Spring Boot 3.1.5 버전에서 JPA를 사용 중인데, NullPointerException이 발생했습니다. 아래는 관련 코드와 에러 로그입니다.” 처럼 구체적인 정보를 제공해 보세요. Attention(4단계)에서 모델이 문제의 핵심에 더 잘 집중할 수 있게 합니다.

2️⃣ 예시를 통한 출력 유도
원하는 결과물의 예시를 한두 개 보여주면, LLM이 출력 형식을 더 정확하게 학습합니다. 이는 모델 전체를 재학습시키는 것이 아니라, 특정 요청에 대한 Attention(4단계) 가중치를 효과적으로 유도하는 방법입니다.

질문: Python 리스트를 정렬하는 방법은?
답변: (예시): list.sort() 또는 sorted(list)를 사용합니다.

질문: Java 리스트를 정렬하는 방법은?
답변: (LLM이 형식에 맞춰 답변할 확률이 높아짐)

3️⃣ 명확한 역할과 형식 지정
프롬프트 시작에 “당신은 10년 차 백엔드 개발자입니다. Kotlin Spring 코드로 답변해 주세요.” 와 같이 역할을 부여해 보세요. 이는 Prediction(5단계)에서 모델이 선택할 단어의 범위를 효과적으로 좁혀줍니다.

4️⃣ 복잡한 작업은 단계별로 분해
하나의 거대한 프롬프트로 모든 것을 해결하려 하지 마세요. “A 데이터를 분석해서, 그래프를 그리고, 보고서를 써줘” 보다는 아래와 같이 작업을 나누어 요청하는 것이 훨씬 효과적입니다.

1. A 데이터의 주요 특징을 분석해줘.
2. 분석 결과를 바탕으로 막대그래프를 그리는 Python 코드를 작성해줘.
3. 위 내용을 종합해서 보고서 초안을 작성해줘.

5️⃣ 부정문 대신 긍정문으로 지시
“JSON 형식 외에는 출력하지 마” 보다는 “반드시 RFC 8259 표준을 준수하는 JSON 형식으로만 출력해줘” 처럼 긍정적이고 명확한 지시가 더 좋은 결과를 낳습니다.

6️⃣ 결과를 비판적으로 검토하고 피드백
첫 답변이 만족스럽지 않다면, “그 답변은 ~한 점에서 틀렸어. 그 점을 수정해서 다시 설명해줘.” 처럼 구체적인 피드백을 주며 대화를 이어가 보세요. 이는 Loop(6단계) 과정을 사용자가 직접 제어하며 원하는 결과로 유도하는 효과를 발휘할 수 있습니다.

📝 LLM 내부 동작 6단계 요약

LLM이 사용자의 입력을 받아 답변을 생성하는 전체 과정을 6단계로 나누어 정리해 보았습니다. 각 단계별로 핵심 동작을 함께 살펴보고 마무리하려고 합니다.

단계핵심 동작
1단계: Tokenization입력 문장을 의미 있는 최소 단위로 분리하고, 각 토큰에 고유한 정수 ID를 할당
2단계: Embedding토큰 ID를 의미와 문맥을 담은 고차원 벡터로 변환
3단계: Positional Encoding각 단어의 순서 정보를 벡터에 더해 위치를 인식
4단계: Transformer & Attention문맥을 파악하고, 각 단어의 표현을 정교하게 다듬기
5단계: Prediction다음에 올 토큰의 확률을 계산하고, 최적의 토큰을 선택
6단계: Loop & Decoding예측된 토큰을 반복적으로 입력에 추가해 문장을 완성

이 6단계를 거쳐 LLM은 입력 문장을 이해하고, 자연스러운 답변을 생성합니다.

마무리

LLM 내부 동작 원리를 파헤치며, 우리가 일상적으로 사용하는 언어가 수많은 수치적 변환과 확률적 선택, 그리고 복잡한 신경망 구조를 통해 생성된다는 사실이 흥미로웠습니다. 특히 토큰화, 임베딩, 어텐션 등 각 단계가 단순한 기술적 처리를 넘어 문맥과 의미를 수치적으로 다루는 방식이 인간의 사고와도 닮아 있다고 느끼게 되었습니다.

LLM을 잘 활용하는 것도 중요하지만, 내부 구조를 이해하면 단순히 결과를 받아들이는 것을 넘어 원하는 방향으로 모델을 더 효과적으로 활용할 수 있다는 점을 깨닫게 되는 시간이었습니다.

이처럼 기술의 발전으로 인공지능이 인간과 얼마나 더 닮아갈지에 대한 걱정과 함께, 인공지능의 구조를 이해함으로써 오히려 인간의 역량을 극대화할 수 있다는 가능성도 느끼게 되었답니다.

이번 글에서는 LLM의 내부 원리를 중심으로 설명했는데요. 카카오페이 기술 블로그에서는 이와 함께 AI를 실제로 활용한 다양한 사례도 함께 공유하고 있습니다. 예를 들어, 사내 해커톤에서는 AWS GenAI를 활용해 여러 아이디어를 구현했고, 최근에는 MCP Agent Toolkit 개발기와 같은 주제도 다루었습니다.

LLM의 원리를 설명한 이번 글이 실제 AI 적용 사례를 더 깊이 이해하는 데 도움이 되었으면 좋겠습니다.

긴 글을 읽어주신 모든 분께 감사드립니다. 🙇🏻‍

Reference

wi.fi
wi.fi

카카오페이 정산플랫폼팀에서 백엔드 개발을 하고 있는 와이입니다. 더 나은 세상을 만드는 일에 관심이 많습니다.