Ssul's Blog
허깅페이스(huggingface) 토크나이저 사용해서 모델 추론하는 3가지 방법 본문
인터넷 검색이나, 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 세 가지 루틴이다.
오늘부터 내 스타일로 고정해서, 검색 수영은 그만하고 개발 속도 업 해 보자!
'AI & ML > 학습하기' 카테고리의 다른 글
AI서비스를 위한 GPU이해하기 (0) | 2025.06.17 |
---|---|
Gemma3 finetuning(파인튜닝)하기 (0) | 2025.03.25 |
Chat_template 구조 파인튜닝하기(feat. EXAONE-3.5-7B) (0) | 2025.03.07 |
DeepSeek-R1 정리(공부하기) + Open r1 (0) | 2025.02.04 |
패캠(패스트캠퍼스) "LLM 모델 파인튜닝을 위한 GPU 최적화" 후기 (2) | 2024.12.02 |