LLM 25일 코스 - Day 14: 텍스트 생성 실전

Day 14: 텍스트 생성 실전

LLM의 텍스트 생성 품질은 디코딩 전략에 크게 좌우됩니다. 오늘은 model.generate()의 핵심 파라미터를 하나씩 실험하며 각 전략의 차이를 체감해봅니다.

기본 텍스트 생성

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

model_name = "gpt2"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

prompt = "The future of artificial intelligence is"
input_ids = tokenizer.encode(prompt, return_tensors="pt")

# Greedy Search (기본) - 매번 확률이 가장 높은 토큰 선택
output = model.generate(input_ids, max_new_tokens=50)
print("Greedy:", tokenizer.decode(output[0], skip_special_tokens=True))

# Beam Search - 여러 후보를 동시에 탐색
output = model.generate(input_ids, max_new_tokens=50, num_beams=5, early_stopping=True)
print("Beam:  ", tokenizer.decode(output[0], skip_special_tokens=True))

temperature, top_k, top_p 비교

temperature는 확률 분포의 날카로움을 조절합니다. 낮으면 결정적(보수적), 높으면 다양한(창의적) 출력을 만듭니다. top_k는 상위 k개의 토큰만, top_p는 누적 확률 p까지의 토큰만 후보로 남깁니다.

# 샘플링 활성화 + 파라미터 비교 실험
configs = [
    {"temperature": 0.3, "top_p": 1.0, "top_k": 0, "label": "낮은 온도 (보수적)"},
    {"temperature": 1.0, "top_p": 1.0, "top_k": 0, "label": "기본 온도"},
    {"temperature": 1.5, "top_p": 1.0, "top_k": 0, "label": "높은 온도 (창의적)"},
    {"temperature": 1.0, "top_p": 0.9, "top_k": 0, "label": "top_p=0.9 (nucleus)"},
    {"temperature": 1.0, "top_p": 1.0, "top_k": 50, "label": "top_k=50"},
]

for cfg in configs:
    output = model.generate(
        input_ids,
        max_new_tokens=40,
        do_sample=True,
        temperature=cfg["temperature"],
        top_p=cfg["top_p"],
        top_k=cfg["top_k"],
    )
    text = tokenizer.decode(output[0], skip_special_tokens=True)
    print(f"[{cfg['label']}]\n{text}\n")

반복 방지와 고급 파라미터

LLM은 같은 문구를 반복하는 경향이 있습니다. repetition_penaltyno_repeat_ngram_size로 이를 억제할 수 있습니다.

output = model.generate(
    input_ids,
    max_new_tokens=100,
    do_sample=True,
    temperature=0.8,
    top_p=0.92,
    top_k=50,
    repetition_penalty=1.2,       # 1.0이면 패널티 없음, 1.2 정도가 적당
    no_repeat_ngram_size=3,        # 3-gram 반복 금지
    num_return_sequences=2,        # 여러 결과 생성
    pad_token_id=tokenizer.eos_token_id,
)

for i, seq in enumerate(output):
    print(f"--- 생성 결과 {i+1} ---")
    print(tokenizer.decode(seq, skip_special_tokens=True))
    print()

생성 전략 요약: Greedy는 빠르지만 단조롭고, Beam Search는 품질이 높지만 느리며, Sampling+top_p 조합이 가장 많이 쓰입니다. 실무에서는 temperature=0.7, top_p=0.9, repetition_penalty=1.1 조합을 시작점으로 삼고 조정하세요.

오늘의 연습문제

  1. 동일한 프롬프트에 대해 temperature를 0.1, 0.5, 1.0, 1.5, 2.0으로 변경하며 생성 결과를 비교하고, 어떤 패턴이 보이는지 정리해보세요.
  2. repetition_penalty를 1.0, 1.2, 1.5, 2.0으로 바꿔가며 반복 억제 효과를 관찰해보세요. 너무 높으면 어떤 문제가 생기나요?
  3. num_beams=5인 Beam Search와 do_sample=True, top_p=0.9인 Nucleus Sampling을 같은 프롬프트에 적용하고, 결과의 일관성과 다양성을 비교해보세요.

이 글이 도움이 되었나요?