Ssul's Blog

허깅페이스(huggingface) 토크나이저 사용해서 모델 추론하는 3가지 방법 본문

AI & ML/학습하기

허깅페이스(huggingface) 토크나이저 사용해서 모델 추론하는 3가지 방법

Ssul 2025. 6. 12. 23:45

인터넷 검색이나, AI검색을 통해서 AI모델 학습/추론하는 코드를 찾아보게 되면 다양한 방식이 존재한다.

어쩔땐 pipeline, 어쩔땐 model.generate()/decode, 또 어쩔때는 model(input_ids)를 넣는 방식까지...

매 검색 코드마다 쓰는 방식이 다르니, 한번 정리하고, 나만의 AI학습 코드를 고정해야 하겠다.

 

이번 글에선 “토크나이저 → 모델 → 디코딩” 흐름을 기준으로

1️⃣ pipeline (원스톱)

2️⃣ generate (표준)

3️⃣ manual forward (로짓 해킹)

세 등급으로 쪼개 봤다.

코드는 전부 복붙-실행 되도록 적었으니 — 마음에 드는 루틴 하나 골라 바로 써 보시길!

 

#0. 공통

- 설치해주시고 pip install -q transformers accelerate torch

from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
import torch, os

MODEL_NAME = "gpt2"   # 사내 모델이면 로컬 경로만 교체
DEVICE     = "cuda" if torch.cuda.is_available() else "cpu"
os.environ["TOKENIZERS_PARALLELISM"] = "false"   # 경고 끄기

 

 

#1. pipeline 사용하기

- pipeline 객체 만들고

- 텍스트 입력하면 끝

generator = pipeline(
    task="text-generation",
    model=MODEL_NAME,
    tokenizer=MODEL_NAME,
    device=0 if DEVICE == "cuda" else -1,
)

prompt = "Thoughts on large language models:"
print(generator(prompt, max_length=50, temperature=0.8)[0]["generated_text"])

- 보는것처럼 별로 할께 없다. 그냥 넣어주면 된다

- 단점은 배치, GPU메모리 최적화 등의 한계가 존재

 

 

#2. tokenizer + model.generate()

- 토크나이저와 모델을 셋팅하고

- 입력된 텍스트를 토크나이징

- 토크나이징 된 토큰을 모델에 입력

- 모델에서 출력된 토큰을 decode로 다시 자연어로 변경

tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model     = AutoModelForCausalLM.from_pretrained(MODEL_NAME).to(DEVICE)

prompt  = "Thoughts on large language models:"
inputs  = tokenizer(prompt, return_tensors="pt").to(DEVICE)

gen_ids = model.generate(
    **inputs,
    max_new_tokens=60,
    do_sample=True,
    top_p=0.9,
    temperature=0.7,
    pad_token_id=tokenizer.eos_token_id,  # GPT-2는 pad 토큰이 없음!
)

print(tokenizer.decode(gen_ids[0], skip_special_tokens=True))

- 프로덕션 배포, 배치 추론, 하이퍼파라미터 조정 등에 사용

- 가장 무난하게 사용하는 형태. 나도 보통 이걸 가장 많이 사용하는 듯

 

#3. Manual forwar-로짓 해킹 & 연구 특화

- 이 방법은 보통 모델을 처음부터 쪼개서 볼때 사용

- 모델을 공부하고 연구한다면 이 방법이 좋다

- LLM 모델이 어떤 형태로 돌아가는지 확인할 수 있다

"""
============================================================
Manual Top-p(=nucleus) Sampling + EOS 감지 예제
------------------------------------------------------------
‣ 핵심 흐름
   1) 프롬프트를 토크나이즈하여 `generated` 시퀀스 초기화
   2) for-loop를 돌며 매 스텝마다
        a. 마지막 토큰까지 모델에 통과 → 로짓(logits) 추출
        b. 온도 스케일링 → 확률(prob) 변환
        c. Top-p 기준으로 누적 확률이 `top_p` 보다 큰 토큰은 모두 컷
        d. 남은 토큰 분포에서 1개를 샘플링 → `next_id`
        e. `next_id`를 시퀀스 뒤에 붙이고 EOS면 루프 종료
   3) 최종 `generated`를 디코딩 → 사람이 읽을 텍스트 출력
------------------------------------------------------------
※ GPT-2(혹은 사내 causal 모델)에 맞춘 예시.
   ‣ Seq2Seq(BART/T5 등)이라면 AutoModelForSeq2SeqLM,
   ‣ TokenClassification 등 다른 헤드는 forward() 사이즈가 다를 수 있음.
============================================================
"""

from torch.nn.functional import softmax
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

# ------------------------------------------------------------------
# 1. 모델·토크나이저 로드 + 환경 세팅
# ------------------------------------------------------------------
MODEL_NAME = "gpt2"                       # 🛈 사내 모델 경로로 교체 가능
DEVICE     = "cuda" if torch.cuda.is_available() else "cpu"

tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model     = AutoModelForCausalLM.from_pretrained(MODEL_NAME).to(DEVICE)

model.eval()                              # 🔒 추론 모드 (Dropout, Grad 끔)

# ------------------------------------------------------------------
# 2. 하이퍼파라미터 & 입력 프롬프트
# ------------------------------------------------------------------
prompt         = "Thoughts on large language models:"  # 생성 시작 문장
eos_id         = tokenizer.eos_token_id                # GPT-2는 50256
max_new_tokens = 60                                    # 최댓값 세이프가드
top_p          = 0.9                                   # 누클리어 샘플링 문턱
temperature    = 0.7                                   # 온도(작을수록 보수적)

# 프롬프트 → 텐서 (batch_size=1 로 통일)
generated = tokenizer(prompt, return_tensors="pt").input_ids.to(DEVICE)

# ------------------------------------------------------------------
# 3. 토큰 생성 루프
# ------------------------------------------------------------------
for _ in range(max_new_tokens):
    # 3-a) 모델 forward ▶ 마지막 토큰의 로짓만 사용
    with torch.no_grad():                               # 그래디언트 비활성화
        logits = model(generated).logits                # (1, seq_len, vocab)
        logits = logits[:, -1, :] / temperature         # 온도 스케일링

        # (참고) temperature <1  → 분포 sharpening
        #        temperature >1  → 분포 flattening

    # 3-b) 로짓 → 소프트맥스 확률
    probs = softmax(logits, dim=-1)                     # (1, vocab)

    # 3-c) Top-p 필터링 (누적 확률 0.9 이하만 존치)
    sorted_probs, sorted_idx = torch.sort(probs, descending=True)
    cumulative = torch.cumsum(sorted_probs, dim=-1)

    keep_mask = cumulative < top_p                      # True/False 마스크
    keep_mask[..., 0] = True                            # 최소 1개는 살려둔다

    filtered_probs = torch.where(
        keep_mask, sorted_probs, torch.zeros_like(sorted_probs)
    )
    filtered_probs /= filtered_probs.sum()              # 원-분포로 재정규화

    # 3-d) 확률 분포에서 1개 토큰 샘플링
    next_token_relative = torch.multinomial(filtered_probs, num_samples=1)  # 인덱스(정렬기준)
    next_token_id = sorted_idx.gather(-1, next_token_relative)              # 실제 vocab id

    # 3-e) 시퀀스 뒤에 새 토큰 붙이기
    generated = torch.cat([generated, next_token_id], dim=-1)

    # 3-f) EOS 토큰이면 조기 종료
    if next_token_id.item() == eos_id:
        break

# ------------------------------------------------------------------
# 4. 결과 디코딩 & 출력
# ------------------------------------------------------------------
output_text = tokenizer.decode(generated[0], skip_special_tokens=True)
print(output_text)

- 토큰 선택 로직은 argmax ↔ 샘플링 ↔ 빔 서치로 자유 교체

- 로짓 조작(금칙어, 온도 스케일링)·어텐션 시각화·그라드 분석까지 무제한

 

#정리

 

레벨 키워드 주사용처
Lv 1: pipeline One-liner 데모 · 노트북 튜토리얼
Lv 2: generate 표준 API 서비스 · 배치 추론 · 실험
Lv 3: forward 로짓 해킹 논문 재현 · 맞춤 디코더

결국 구조는 하나. 1️⃣ 토크나이징 → 2️⃣ 모델 호출 → 3️⃣ 디코딩.

얼마나 깊게 컨트롤할지에 따라 pipeline → generate → forward로 단계만 내려가면 된다.

 

 

“토크나이저가 만든 input_ids어디까지 내가 ‘만지작’하고 싶은가?”

그 답이 바로 pipeline / generate / forward 세 가지 루틴이다.

오늘부터 내 스타일로 고정해서, 검색 수영은 그만하고 개발 속도 업 해 보자!