Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

GPT

2018년에 발표된 GPT (Generative Pre-trained Transformer) 는 OpenAI의 첫 번째 Transformer 기반 언어 생성 모델로, 자연어 처리(NLP)에 사전학습(Pre-training) + 미세조정(Fine-tuning) 접근을 처음 성공적으로 도입한 사례입니다. Radford et al. (2018) 이 모델은 후속 모델들(GPT-2, GPT-3 등)의 기반이 되었으며, "프롬프트 기반 생성 모델"의 시초로 평가받습니다.

import torch
import transformers
from transformers import GPT2Tokenizer, GPT2Model

tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
model = GPT2Model.from_pretrained('gpt2')
tensor([[[ 0.1629, -0.2166, -0.1410,  ..., -0.2619, -0.0819,  0.0092],
         [ 0.4628,  0.0248, -0.0785,  ..., -0.0859,  0.5122, -0.3939],
         [-0.0644,  0.1551, -0.6306,  ...,  0.2488,  0.3691,  0.0833],
         ...,
         [-0.5591, -0.4490, -1.4540,  ...,  0.1650, -0.1302, -0.3740],
         [ 0.1400, -0.3875, -0.7916,  ..., -0.1780,  0.1824,  0.2185],
         [ 0.1721, -0.2420, -0.1124,  ..., -0.1068,  0.1205, -0.3213]]],
       grad_fn=<ViewBackward0>)

언어모델 헤드(lm_head)란?

  • 정의: Transformer가 만든 히든 스테이트를 어휘 분포(로짓)로 투사해 토큰을 예측하는 최종 선형 계층입니다.

  • GPT2Model vs GPT2LMHeadModel:

    • GPT2Model은 히든 스테이트만 출력합니다(생성용 로짓 없음).

    • GPT2LMHeadModellm_head(선형층)를 포함해 어휘 크기만큼의 로짓을 출력·생성할 수 있습니다.

  • 왜 필요한가: 히든 스테이트는 연속 벡터라 바로 텍스트로 디코드할 수 없습니다. lm_headVV(어휘 크기) 차원의 로짓을 만든 뒤, 샘플링/탑-kk/탑-pp 등으로 토큰을 선택하고 디코딩합니다.

  • 차원 관계: GPT-2 base 기준 히든 크기 H=768H=768, 어휘 크기 V50257V\approx 50257. lm_head 가중치는 WRV×HW\in\mathbb{R}^{V\times H}이고, 마지막 히든 hRHh\in\mathbb{R}^{H}에 대해

    logits=hWRV.\text{logits} = h W^{\top} \in \mathbb{R}^{V}.

    보통 임베딩과 가중치 타이잉(weight tying)이 적용됩니다.

  • 사용 패턴:

    • 단일 스텝: 이전 셀처럼 last_hlm_head에 투사해 다음 토큰을 얻습니다.

    • 생성: generate()를 사용해 연속 토큰을 자동으로 예측·이어쓰기 합니다(권장).

  • 관련 클래스: 태스크별로 헤드가 다릅니다. 예: AutoModelForCausalLM(생성), BertForMaskedLM(마스크 예측), AutoModelForSequenceClassification(분류).

from transformers import GPT2LMHeadModel

# 입력 텍스트 정의
text = "Replace me by any text you'd like."
encoded_input = tokenizer(text, return_tensors='pt')

# GPT2Model로 히든 스테이트 계산
output = model(**encoded_input)
print(output.keys())
# last_hidden_state: (batch_size, sequence_length, hidden_size)
print(output.last_hidden_state.shape)

# --- 히든 스테이트를 텍스트로 '해독'하는 방법 ---
# 히든 스테이트는 연속 벡터이므로 직접 텍스트로 변환할 수 없습니다.
# GPT2LMHeadModel의 언어모델 헤드(lm_head)를 사용해 어휘 분포(logits)로 투사한 뒤 토큰으로 변환해야 합니다.

# 동일한 프리트레인 가중치를 가진 언어모델 헤드 로딩
lm_model = GPT2LMHeadModel.from_pretrained('gpt2')

# 1) 마지막 토큰 위치의 히든 스테이트로 '다음 토큰'을 예측
with torch.no_grad():
    last_h = output.last_hidden_state[:, -1, :]                # (batch, hidden_size)
    last_logits = lm_model.lm_head(last_h)                     # (batch, vocab_size)
    next_token_id = torch.argmax(last_logits, dim=-1).item()   # 가장 높은 확률의 토큰 ID

next_token = tokenizer.decode([next_token_id], skip_special_tokens=True)
print("다음 토큰 예측:", next_token)

# 2) 권장: generate()로 자연스러운 이어쓰기 생성
#    모델이 자동으로 토큰을 순차적으로 예측하고 붙여서 문장을 생성합니다.
with torch.no_grad():
    gen_ids = lm_model.generate(
        **encoded_input,
        max_length=encoded_input['input_ids'].shape[1] + 50,
        do_sample=True,
        top_p=0.9,
        temperature=0.8,
    )

generated_text = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
print("생성 텍스트:", generated_text)
odict_keys(['last_hidden_state', 'past_key_values'])
torch.Size([1, 10, 768])
Loading...
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
다음 토큰 예측: 

생성 텍스트: Replace me by any text you'd like. I'm afraid it's not all that effective."

"I see. So you want to know where you can find the book. The only way you can find it is with my help."

"So, what is it, a
from transformers import AutoTokenizer, AutoModelForCausalLM

# 토크나이저/모델 로딩
tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")

# GPT2는 기본 pad 토큰이 없으므로 EOS를 pad로 설정
if tokenizer.pad_token_id is None:
    tokenizer.pad_token = tokenizer.eos_token

# 입력 텍스트 정의
text = "Replace me by any text you'd like."
inputs = tokenizer(text, return_tensors="pt")

# 올바른 생성 방법: forward가 아닌 generate() 사용
with torch.no_grad():
    gen_ids = model.generate(
        **inputs,
        max_new_tokens=50,
        do_sample=True,
        top_p=0.9,
        temperature=0.8,
        pad_token_id=tokenizer.pad_token_id,
        eos_token_id=tokenizer.eos_token_id,
    )

generated_text = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
print("생성 텍스트:", generated_text)

# 참고: 단일 스텝의 '다음 토큰' 그리디 예측
with torch.no_grad():
    outputs = model(**inputs)
    next_token_id = outputs.logits[0, -1].argmax().item()
next_token = tokenizer.decode([next_token_id], skip_special_tokens=True)
print("다음 토큰(그리디):", next_token)
생성 텍스트: Replace me by any text you'd like.

It's so hard to read it, but you'll never know what's going on.

I'm sorry, I'm a writer.

I'm a writer that is not trying to win with the text you write.

다음 토큰(그리디): 

AutoModelForCausalLM과 출력 차이

  • forward와 generate의 목적 차이:

    • model(**inputs)는 입력 시퀀스에 대한 로짓(각 타임스텝의 다음 토큰 분포)을 반환합니다.

    • model.generate(...)는 토큰을 순차적으로 샘플링/그리디 선택하여 실제 “이어쓰기” 시퀀스를 생성합니다.

  • 왜 비슷한 출력이 안 나왔나: max_new_tokens, do_sample, top_p, temperature 등은 forward 인자가 아니라 generate의 인자입니다. forward의 로짓을 argmax로 디코딩하면 각 위치의 "다음 토큰"을 병렬로 고르는 셈이라 자연스러운 문장 이어쓰기가 되지 않습니다.

  • 권장 패턴: 이어쓰기가 목적이라면 AutoModelForCausalLM.generate()를 사용하고, GPT-2 계열은 pad_token_ideos_token_id로 지정해 배치/패딩 이슈를 피하세요.

  • 디버그 팁: 단일 스텝 확인은 마지막 타임스텝의 로짓(outputs.logits[0, -1])만 사용해 다음 토큰을 확인하세요.

AutoModelForCausalLM과 기본 모델 관계

  • 정의: AutoModelForCausalLM은 프리트레인 체크포인트(모델 이름)에 맞춰 적절한 *ForCausalLM 클래스를 자동 선택하는 태스크 특화 래퍼입니다. 이어쓰기/생성에 필요한 lm_headgenerate() 기능을 제공합니다.

  • 기본(base) 모델: AutoModel, GPT2Model, LlamaModel 등은 히든 스테이트(last_hidden_state)만 출력하며 lm_head가 없습니다. 따라서 직접 텍스트 생성은 불가합니다.

  • 헤드 포함 모델: GPT2LMHeadModel, LlamaForCausalLM, MistralForCausalLM, QwenForCausalLM 등은 어휘 분포 로짓(logits)을 출력하고 generate()로 토큰을 순차 생성합니다.

  • 선택 기준:

    • 디코더-온리 LM(GPT-2, LLaMA, Mistral, Qwen 등) → AutoModelForCausalLM 사용.

    • 인코더-디코더(BART, T5 등) → AutoModelForSeq2SeqLM 사용. (generate()는 시퀀스-투-시퀀스로 동작)

    • BERT 계열(MLM) → AutoModelForMaskedLM 사용. (마스크 토큰 복원 태스크)

  • 출력 차이:

    • 기본 모델: last_hidden_state 중심, 생성용 로짓 없음.

    • *ForCausalLM: logits 제공, max_new_tokens/top_p/temperaturegenerate() 인자 사용 가능.

  • 토크나이저 팁: AutoTokenizer를 쓰면 특수 토큰과 토크나이저 설정이 체크포인트에 맞게 로딩됩니다. GPT-2는 기본 pad_token이 없어 pad_token_id=eos_token_id 설정을 권장합니다.

  • 빠른 점검 코드:

    from transformers import AutoModelForCausalLM, AutoTokenizer
    tok = AutoTokenizer.from_pretrained("gpt2")
    mdl = AutoModelForCausalLM.from_pretrained("gpt2")
    inputs = tok("Hello", return_tensors="pt")
    gen = mdl.generate(**inputs, max_new_tokens=20)
    print(tok.decode(gen[0], skip_special_tokens=True))
    print("is_encoder_decoder:", mdl.config.is_encoder_decoder)

    is_encoder_decoder=False이면 디코더-온리로 AutoModelForCausalLM이 적합합니다.

References
  1. Radford, A., Narasimhan, K., Salimans, T., & Sutskever, I. (2018). Improving language understanding by generative pre-training. https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf