Hugging Face를 실무 흐름으로 이해하기
Hugging Face Transformers, PEFT, TGI로 LLM을 로컬 실행·파인튜닝·서빙합니다. LoRA/QLoRA 파인튜닝, Inference API, Text Generation Inference 프로덕션 서버, 에이전트 통합까지 다룹니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.
Hugging Face Transformers, PEFT, TGI로 LLM을 로컬 실행·파인튜닝·서빙합니다. LoRA/QLoRA 파인튜닝, Inference API, Text Generation Inference 프로덕션 서버, 에이전트 통합까지 다룹니다.
Hugging Face Transformers, PEFT, TGI로 LLM을 로컬 실행·파인튜닝·서빙합니다. LoRA/QLoRA 파인튜닝, Inference API, Text Generation Inference 프로덕션 서버, 에이전트 통합까지 다룹니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.
모델과 프롬프트만 보지 않고, 데이터 흐름, 평가, 배포 이후의 운영 지표까지 한 번에 연결해서 봅니다.
글로 읽은 내용을 머릿속에 오래 남기려면 먼저 흐름을 그림으로 잡는 편이 좋습니다. 아래 두 그림은 Hugging Face를 학습할 때 계속 되돌아볼 수 있는 기준 지도입니다.
Hugging Face를 처음 펼칠 때는 세부 명령보다 큰 그림이 먼저입니다. 이 섹션에서는 앞으로 배울 개념들이 어떤 문제를 풀기 위해 등장했는지부터 잡아봅니다.
from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
import torch
# 텍스트 생성 파이프라인 — device_map="auto" 로 GPU/CPU 자동 할당
pipe = pipeline(
"text-generation",
model="meta-llama/Llama-3.1-8B-Instruct",
torch_dtype=torch.bfloat16,
device_map="auto",
)
messages = [{"role": "user", "content": "파이썬으로 퀵소트를 구현해줘"}]
result = pipe(messages, max_new_tokens=512, temperature=0.7)
print(result[0]["generated_text"][-1]["content"])| 라이브러리 | 역할 |
|---|---|
| transformers | 모델 로드·추론 통합 API |
| datasets | 학습 데이터셋 로드와 스트리밍 전처리 |
| peft | LoRA/QLoRA 경량 파인튜닝 어댑터 |
| trl | SFT·DPO·PPO 등 정렬 학습 트레이너 |
| accelerate | 멀티 GPU·분산 학습 런처 |
| huggingface_hub | 모델 업로드·다운로드·버전 관리 |
여기서는 Inference API을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
from huggingface_hub import InferenceClient
client = InferenceClient(api_key="hf_...")
# 스트리밍 응답 — 실시간 토큰 출력
response = client.chat_completion(
model="meta-llama/Llama-3.1-70B-Instruct",
messages=[{"role": "user", "content": "LLM 파인튜닝의 핵심 개념을 설명해줘"}],
max_tokens=1024,
stream=True,
)
for chunk in response:
print(chunk.choices[0].delta.content, end="", flush=True)여기서는 LoRA 파인튜닝을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM
import torch
# LoRA 설정 — r(rank)이 높을수록 표현력↑, 메모리↑
lora_config = LoraConfig(
r=16,
lora_alpha=32, # 스케일링 팩터 (보통 r의 2배)
target_modules=["q_proj", "v_proj"], # 어텐션 쿼리·값 레이어만 학습
lora_dropout=0.05,
bias="none",
task_type=TaskType.CAUSAL_LM,
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B",
torch_dtype=torch.bfloat16,
device_map="auto",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# trainable params: 6,815,744 || all params: 8,036,966,400 || trainable%: 0.08%여기서는 QLoRA (4-bit 양자화)을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
from transformers import BitsAndBytesConfig, AutoModelForCausalLM
from peft import LoraConfig, get_peft_model, TaskType, prepare_model_for_kbit_training
import torch
# 4-bit NF4 양자화 설정
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NF4가 fp4보다 성능 우수
bnb_4bit_compute_dtype=torch.bfloat16, # 연산은 bfloat16으로 수행
bnb_4bit_use_double_quant=True, # 이중 양자화로 추가 절약
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B",
quantization_config=bnb_config,
device_map="auto",
)
# 4-bit 학습을 위한 그라디언트 체크포인트 활성화
model = prepare_model_for_kbit_training(model)
lora_config = LoraConfig(r=64, lora_alpha=16, task_type=TaskType.CAUSAL_LM)
model = get_peft_model(model, lora_config)여기서는 데이터셋 준비을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
from datasets import load_dataset
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct")
# JSONL 파일 로드 — {"input": ..., "output": ...} 형식
dataset = load_dataset("json", data_files="train.jsonl", split="train")
def format_chat(example):
messages = [
{"role": "system", "content": "당신은 친절한 AI 어시스턴트입니다."},
{"role": "user", "content": example["input"]},
{"role": "assistant", "content": example["output"]},
]
# 모델 전용 채팅 템플릿 적용 (토큰화 없이 문자열 반환)
return {"text": tokenizer.apply_chat_template(messages, tokenize=False)}
dataset = dataset.map(format_chat, remove_columns=dataset.column_names)
print(dataset[0]["text"][:300])여기서는 TGI 프로덕션 서버을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
# TGI Docker 서버 실행 — 단일 GPU
docker run --gpus all -p 8080:80 \
-v $HOME/.cache/huggingface:/data \
ghcr.io/huggingface/text-generation-inference:3.0 \
--model-id meta-llama/Llama-3.1-8B-Instruct \
--max-total-tokens 4096 \
--max-input-tokens 3072# OpenAI 호환 클라이언트로 TGI 호출
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="none")
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[{"role": "user", "content": "서울의 날씨는?"}],
stream=True,
max_tokens=512,
)
for chunk in response:
print(chunk.choices[0].delta.content or "", end="", flush=True)여기서는 모델 평가을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
import evaluate
# 메트릭 로드 (최초 실행 시 자동 다운로드)
rouge = evaluate.load("rouge")
bertscore = evaluate.load("bertscore")
predictions = ["모델이 생성한 요약문입니다."]
references = ["참조 정답 요약문입니다."]
# ROUGE — 단어 겹침 기반 (빠른 계산)
rouge_result = rouge.compute(predictions=predictions, references=references)
print(rouge_result)
# {'rouge1': 0.82, 'rouge2': 0.71, 'rougeL': 0.79}
# BERTScore — 의미 임베딩 기반 (더 정확, GPU 권장)
bert_result = bertscore.compute(
predictions=predictions, references=references, lang="ko"
)
print(f"F1: {bert_result['f1'][0]:.4f}")여기서는 HF Agents & Tool을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel
# Inference API를 에이전트 LLM으로 사용
model = HfApiModel(model_id="meta-llama/Llama-3.1-70B-Instruct")
agent = CodeAgent(
tools=[DuckDuckGoSearchTool()], # 웹 검색 도구
model=model,
max_steps=10,
)
# 에이전트가 검색 → 분석 → 코드 생성 → 결과 반환
result = agent.run("최신 LLM 벤치마크 결과를 찾아서 표로 정리해줘")
print(result)Hugging Face 실무 설계은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
| 결정 지점 | 확인 질문 | 실무 기준 |
|---|---|---|
| 경계 | Hugging Face 코드에서 바뀌기 쉬운 부분은 어디인가? | 입출력, 설정, 외부 연동, 핵심 규칙을 분리합니다. |
| 상태 | 상태가 어디서 생성되고 어디서 사라지는가? | 상태 소유자와 수명 주기를 코드로 드러냅니다. |
| 장애 | 실패했을 때 호출자는 무엇을 받는가? | timeout, fallback, error contract를 먼저 정합니다. |
이 섹션은 Hugging Face 운영 기준을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.
Hugging Face 검증 전략은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
| 품질 축 | 검증 방법 | 완료 기준 |
|---|---|---|
| 정확성 | 정상/실패 케이스를 자동화합니다. | 핵심 시나리오가 재현 가능하게 통과합니다. |
| 회귀 방지 | 버그 수정 시 동일 케이스를 테스트로 남깁니다. | 같은 장애가 다시 배포되지 않습니다. |
| 운영성 | 로그, 메트릭, 알림을 확인합니다. | 문제가 생겼을 때 원인 추적 경로가 있습니다. |