Large Language Model

[핸즈온 LLM] 트랜스포머 모델 개요 (3.1)

대치동명강사 2025. 7. 1. 22:20
728x90
반응형

🧠 트랜스포머 LLM은 어떻게 텍스트를 생성할까?

— 입력에서 출력까지, 그리고 어텐션 메커니즘의 정밀 해부


1. 트랜스포머 LLM이란?

트랜스포머 기반의 대형 언어 모델(LLM)은 우리가 흔히 사용하는 챗봇, 텍스트 요약기, 코드 생성기 등에 쓰이는 핵심 기술입니다. 이 모델은 텍스트를 입력받아 응답을 생성하는 텍스트-입력 → 텍스트-출력 시스템으로 동작합니다.

하지만 중요한 점은:

✨ 트랜스포머 LLM은 텍스트 전체를 한꺼번에 생성하지 않습니다.
🔁 한 번에 한 토큰씩, 반복적으로 예측하며 문장을 완성해 갑니다.

이렇게 이전의 예측을 사용해 다음 예측을 만드는 모델을 지칭하는 머신러닝 용어가 자기회귀(Autoregressive) 모델임. 텍스트 생성 LLM은 이 같은 특성이 있어 자기회귀 모델이라 함.

정방향 계산(Forward Pass)의 구성 요소

2. 입력 → 정방향 계산 → 출력

✅ 기본 처리 흐름

  1. 텍스트 입력 → 토크나이저가 정수 시퀀스로 변환
  2. 트랜스포머 블록 스택 → 입력 시퀀스를 처리
  3. LM 헤드 → 다음 토큰에 대한 확률 분포 출력
  4. 디코딩 → 확률 분포에서 다음 토큰을 선택
  5. 선택된 토큰을 프롬프트 끝에 추가 → 다음 루프 실행

이러한 루프는 모델이 </s> 또는 최대 길이(context length)에 도달할 때까지 반복됩니다.

모델 객체를 출력하면 모델에 포함된 층을 순서대로 표시할 수 있음. 우리가 사용하는 모델의 경우 다음과 같은 결과를 얻음. 

print(model)
# output
Phi3ForCausalLM(
  (model): Phi3Model(
    (embed_tokens): Embedding(32064, 3072, padding_idx=32000)
    (embed_dropout): Dropout(p=0.0, inplace=False)
    (layers): ModuleList(
      (0-31): 32 x Phi3DecoderLayer(
        (self_attn): Phi3Attention(
          (o_proj): Linear(in_features=3072, out_features=3072, bias=False)
          (qkv_proj): Linear(in_features=3072, out_features=9216, bias=False)
          (rotary_emb): Phi3RotaryEmbedding()
        )
        (mlp): Phi3MLP(
          (gate_up_proj): Linear(in_features=3072, out_features=16384, bias=False)
          (down_proj): Linear(in_features=8192, out_features=3072, bias=False)
          (activation_fn): SiLU()
        )
        (input_layernorm): Phi3RMSNorm()
        (resid_attn_dropout): Dropout(p=0.0, inplace=False)
        (resid_mlp_dropout): Dropout(p=0.0, inplace=False)
        (post_attention_layernorm): Phi3RMSNorm()
      )
    )
    (norm): Phi3RMSNorm()
  )
  (lm_head): Linear(in_features=3072, out_features=32064, bias=False)
)

3. 출력은 어떻게 선택될까? (Sampling)

prompt = 'The capital of France is'

# 입력 프롬프트를 토큰화 합니다.
input_ids = tokenizer(prompt, return_tensors = 'pt').input_ids

# 입력 토큰을 GPU에 배치합니다.
input_ids = input_ids.to('cuda')

# lm_head 앞에 있는 model의 출력을 얻습니다.
model_output = model.model(input_ids)

# lm_head의 출력을 얻습니다.
lm_head_output = model.lm_head(model_output[0])

# lm_head_output의 shape는 [1,5,32064]임. [텍스트 수, 토큰 수, 어휘사전 크기]
# lm_head_output[0, -1]로 마지막에 생성된 토큰에 대한 확률 점수를 얻을 수 있음.
# 텍스트 샘플이 하나이므로 배치 차원의 인덱스에는 0 지정, 시퀀스에 있는 마지막 토큰을 얻기 위해 인덱스 -1을 사용.
token_id = lm_head_output[0,-1].argmax(-1)
tokenizer.decode(token_id)

트랜스포머 모델의 마지막 출력은 다음과 같은 확률 분포입니다:

logits = lm_head(hidden_state)  # shape: (1, vocab_size)

이때 사용되는 디코딩 전략은 다음과 같습니다:

 

 

Greedy 확률이 가장 높은 토큰 선택 (argmax)
Sampling 확률 기반 랜덤 샘플링
Top-k / Top-p 일정 확률 이상 토큰만 샘플링 대상
Temperature 확률 분포의 날카로움을 조절 (온도=0이면 Greedy)

 

 

4. 문맥 길이 (Context Length)

트랜스포머는 병렬 처리가 뛰어나지만 동시에 처리할 수 있는 최대 토큰 수에 제한이 있습니다. 이를 문맥 길이라고 하며, 예를 들어 GPT-3의 경우 최대 2,048 ~ 4,096개 토큰까지 입력 가능했습니다.

📌 입력 길이가 길어지면 기존 토큰의 계산 결과도 같이 전달되어야 하기 때문에, 이전 토큰을 무시할 수 없습니다.

마지막 토큰을 제외한 모든 토큰의 결과를 버리는 데 왜 모든 토큰 스트림을 계산하기 위해 애쓰는가? 이전 스트림의 계산이 최종 스트림의 계산에 필요하기 때문! 마지막을 제외한 스트림의 최종 출력 벡터를 사용하지 않지만 트랜스포머 블록의 어텐션 메커니즘에서는 이전 스트림의 출력을 사용합니다. 

5. 키-밸류 캐시로 속도 높이기 (Key-Value Cache)

모델이 토큰을 하나씩 생성할 때마다 전체 시퀀스를 다시 계산하면 매우 비효율적입니다. 그래서 많은 LLM 구현체는 이전 계산 중 Key/Value 벡터를 캐싱하여, 다음 토큰 계산 시 재활용합니다.

이 캐싱이 바로 use_cache=True로 설정되는 부분이며, 생성 속도를 크게 향상시킵니다.

허깅 페이스의 transformers는 기본적으로 캐싱을 사용함. use_cache 매개변수를 False로 지정하여 캐싱을 끌 수 있음. 코드 예제를 통해 긴 문장을 생성 요청하고 캐싱을 할 때와 안 할 때의 생성 속도 차이를 살펴보자. (100개의 토큰을 생성하는데 걸리는 시간)

%%timeit -n 1

generation_output = model.generate(
    input_ids = input_ids,
    max_new_tokens = 100,
    use_cache = False
)

# output: 9.93 s ± 605 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%%timeit -n 1

generation_output = model.generate(
    input_ids = input_ids,
    max_new_tokens = 100,
    use_cache = False
)

# output :33.5 s ± 337 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

약 3.3배 차이가 난다 ㄷㄷ

6. 트랜스포머 블록 내부 구조

트랜스포머는 수십~수백 개의 블록으로 이루어진 신경망이며, 각 블록은 다음 두 가지 구성 요소로 이루어져 있습니다:

  1. 어텐션 층 (Self-Attention Layer)
    → 문맥 정보를 통합하여 토큰 간 관계를 모델링
  2. 피드포워드 층 (MLP Layer)
    → 정보 변환 및 보간(interpolation) 기능 수행

이 블록들이 쌓이면서 모델은 복잡한 언어 패턴을 학습할 수 있게 됩니다.


7. 어텐션 메커니즘

이제 많은 분들이 헷갈리는 어텐션 메커니즘의 작동 방식을 벡터 중심으로 차근차근 설명해볼게요.

💡 개념 요약

셀프 어텐션은 하나의 토큰이, 다른 모든 토큰을 얼마나 참고할지를 결정하고
그 참고 정도에 따라 가중 평균을 통해 새로운 벡터를 만드는 과정입니다.

예시 조건

  • 입력 문장: "나는 사과를 먹었다" → 총 4개의 토큰
  • 시퀀스 길이: L = 4
  • 전체 임베딩 차원: d_model = 512
  • 어텐션 헤드 개수: h = 8
    → 각 헤드당 차원: d_k = d_v = 64

🧱 Step 1. 임베딩 및 Q/K/V 만들기

입력 X의 shape:

X: (L, d_model) = (4, 512)

Q, K, V를 만들기 위해 각각의 Projection Matrix를 곱합니다:

- 어텐션 메커니즘

Q = X @ W_Q   # shape: (4, 64)
K = X @ W_K   # shape: (4, 64)
V = X @ W_V   # shape: (4, 64)

🔎 Step 2. 관련성 점수 계산 (Attention Score)

지금 "먹었다"라는 토큰을 처리 중이라고 합시다.
그 벡터는 Q[3]에 해당합니다.

관련성 점수는 다음과 같이 계산됩니다:

scores = Q[3] @ K.T     # shape: (1, 4)
→ softmax(scores)       # attention_weights: (1, 4)

예를 들어 softmax 결과가 다음과 같다고 합시다:

attention_weights = [0.1, 0.6, 0.2, 0.1]

🔁 Step 3. 정보 통합 (Weighted Sum of V)

이제 V 행렬에서 해당 가중치만큼 각 벡터를 곱해 더합니다:

attention_output = attention_weights @ V
# shape: (1, 64)

즉, "먹었다"의 최종 표현은:

  • "나는", "사과를", "먹었다", "[PAD]" 각각의 의미 정보가
  • 가중치 0.1, 0.6, 0.2, 0.1만큼 반영된 합계입니다

🧠 Step 4. Multi-Head Attention → Concat → Output

8개의 헤드가 위 과정을 병렬로 수행한 결과:

  • 각 헤드 출력: (4, 64)
  • 8개 concat: (4, 512)
  • W_O로 선형 변환 후: (4, 512)

마지막 토큰(예: X[3])의 출력 벡터만 LM 헤드로 전달됩니다.


📌 전체 정리 (각 단계의 Shape)

입력 X (L, d_model) = (4, 512)
프로젝션 Q, K, V (L, d_k) = (4, 64)
점수 계산 Q @ Kᵀ (L, L) = (4, 4)
소프트맥스 결과 attention weights (L, L) = (4, 4)
값 통합 attention weights @ V (L, d_v) = (4, 64)
멀티헤드 concat 여러 head의 출력 연결 (L, 512)
최종 선형변환 W_O를 곱함 (L, 512)

8. LM 헤드: 다음 토큰 확률 계산

마지막 토큰의 최종 출력 벡터는 LM 헤드에 들어가 다음과 같이 처리됩니다:

logits = lm_head(last_hidden_state)  # shape: (1, vocab_size)

 

  • 모델이 알고 있는 단어 수가 32,064개라면
    → logits: shape (1, 32064)
  • softmax(logits)를 통해 각 단어에 대한 확률을 얻습니다.

🔚 마무리: 이 모든 것이 하나의 토큰을 생성하기 위한 과정

이러한 과정을 하나의 토큰을 예측할 때마다 반복합니다.
트랜스포머 LLM은 단순히 거대한 DB처럼 과거를 암기하는 것이 아니라,
문맥을 정교하게 통합하고 패턴을 보간하며 일반화할 수 있는 강력한 모델입니다.

이 글은 『핸즈온 LLM』 (박해선 옮김, 한빛미디어, p99-122)을 바탕으로 정리하였습니다.
또한, 생성형 AI의 도움을 받아 작성되었습니다.

728x90
반응형