QLoRA = Quantized Low-Rank Adaptation
쉽게 말하면: 거대한 AI 모델을 저렴하게 fine-tuning하는 기술
32비트 → 4비트로 압축
# 예시 1: 숫자 저장 용량
original_number = 3.141592653589793 # 32비트 (4바이트)
quantized_number = 3.14 # 4비트로 압축 (0.5바이트)
# 메모리 절약: 8배!
실제 효과:
# 고해상도 이미지 (32비트 컬러)
original_image = [
[255, 128, 64], # 각 픽셀이 32비트
[200, 150, 100]
]
# 양자화 (4비트 컬러)
quantized_image = [
[15, 8, 4], # 0-15 범위로 압축
[13, 9, 6]
]
# 품질은 조금 떨어지지만 용량은 8배 절약!
# 정밀 측정 (32비트)
precise_temp = 36.78934512 °C
# 양자화 (4비트)
quantized_temp = 37 °C # 실용적으로는 충분!
전체 모델을 학습하지 않고, 작은 어댑터만 추가
# 예시: 모델 파라미터
original_model = 7,000,000,000 개 파라미터 # 7B 모델
lora_adapter = 4,000,000 개 파라미터 # 0.057% 만!
# 학습해야 할 파라미터: 1,750배 감소!
# 기존 모델: 영어 → 한국어 번역기
base_model = "영어를 이해하는 거대한 뇌"
# LoRA 어댑터: 의학 용어 전문화
medical_lora = "의학 용어 변환 레이어 (작음)"
# 결과
result = base_model + medical_lora
# → 의학 논문을 잘 번역하는 번역기!
# 기본 카메라 앱
base_camera = "사진 촬영 기능"
# LoRA 필터들 (각각 작은 용량)
vintage_filter = "빈티지 효과"
cartoon_filter = "만화 효과"
noir_filter = "흑백 효과"
# 필터만 바꿔가며 사용 (전체 앱 재설치 불필요!)
# 기본 캐릭터 모델
base_character = "기본 전사 (10GB)"
# LoRA 스킨들 (각각 100MB)
fire_skin = "불꽃 전사 스킨"
ice_skin = "얼음 전사 스킨"
shadow_skin = "그림자 전사 스킨"
# 스킨만 다운로드하면 됨!
최강의 조합!
원본 모델 (32비트) → Quantization → 압축된 모델 (4비트)
↓
LoRA 어댑터 추가
↓
효율적인 학습!
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
import torch
# 1. 모델을 4비트로 압축해서 로드 (QLoRA의 Q)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
load_in_4bit=True, # 🔑 4비트 양자화!
torch_dtype=torch.float16,
device_map="auto"
)
# 메모리 사용량: 28GB → 3.5GB! (8배 절약)
# 2. LoRA 설정 (QLoRA의 LoRA)
lora_config = LoraConfig(
r=8, # LoRA 랭크 (작을수록 가벼움)
lora_alpha=32, # 학습 강도
target_modules=["q_proj", "v_proj"], # 어느 레이어에 적용할지
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
# 3. 모델에 LoRA 어댑터 추가
model = prepare_model_for_kbit_training(model)
model = get_peft_model(model, lora_config)
# 학습 가능한 파라미터 확인
model.print_trainable_parameters()
# 출력: trainable params: 4,194,304 || all params: 7,000,000,000
# 단 0.06%만 학습! (메모리와 시간 대폭 절약)
효과:
from datasets import load_dataset
from transformers import TrainingArguments
from trl import SFTTrainer
# 1. 데이터 준비
dataset = load_dataset("json", data_files="korean_chat.json")
# 데이터 형식 예시:
# {
# "instruction": "맛있는 떡볶이 레시피를 알려줘",
# "output": "떡볶이는 다음과 같이 만듭니다..."
# }
# 2. QLoRA 모델 로드 (위와 동일)
model = AutoModelForCausalLM.from_pretrained(
"beomi/llama-2-ko-7b",
load_in_4bit=True,
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained("beomi/llama-2-ko-7b")
# 3. LoRA 설정
lora_config = LoraConfig(
r=16, # 한국어는 좀 더 복잡하니 랭크를 높임
lora_alpha=64,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
# 4. 학습 설정
training_args = TrainingArguments(
output_dir="./korean-chatbot-qlora",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
num_train_epochs=3,
learning_rate=2e-4,
fp16=True, # 혼합 정밀도 학습
save_steps=100,
logging_steps=10
)
# 5. 학습 시작
trainer = SFTTrainer(
model=model,
train_dataset=dataset["train"],
peft_config=lora_config,
dataset_text_field="text",
max_seq_length=512,
tokenizer=tokenizer,
args=training_args
)
trainer.train()
# 6. 모델 저장 (LoRA 어댑터만!)
model.save_pretrained("./my-korean-chatbot")
# 저장 용량: 8MB만! (원본은 14GB)
비교:
| 항목 | Full Fine-tuning | QLoRA |
|---|---|---|
| GPU 메모리 | 80GB (A100) | 12GB (RTX 3090) |
| 학습 시간 | 24시간 | 4시간 |
| 저장 용량 | 14GB | 8MB |
| 비용 | $500 | $50 |
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model
# 1. 15B 파라미터 코드 생성 모델을 4비트로!
model = AutoModelForCausalLM.from_pretrained(
"bigcode/starcoder",
load_in_4bit=True,
torch_dtype=torch.float16,
device_map="auto",
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained("bigcode/starcoder")
# 원래는 60GB 필요 → 이제 8GB로 가능!
# 2. Python 코드 전문화 LoRA
lora_config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["c_proj", "c_attn"], # StarCoder용
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
# 3. Python 데이터셋으로 학습
# 예: GitHub Python 레포지토리, LeetCode 문제 등
# 4. 추론 예시
prompt = """
# 함수: 피보나치 수열의 n번째 값을 반환
def fibonacci(n):
"""
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_length=200)
code = tokenizer.decode(outputs[0])
print(code)
# 출력: 완성된 피보나치 함수!
# r 값에 따른 차이
# r=4 (매우 가벼움)
lora_config = LoraConfig(r=4)
# - 학습 파라미터: 2백만 개
# - 저장 용량: 4MB
# - 성능: ⭐⭐⭐ (간단한 작업)
# - 사용처: 감성 분석, 분류
# r=8 (보통)
lora_config = LoraConfig(r=8)
# - 학습 파라미터: 4백만 개
# - 저장 용량: 8MB
# - 성능: ⭐⭐⭐⭐ (일반적 작업)
# - 사용처: 챗봇, Q&A
# r=16 (무거움)
lora_config = LoraConfig(r=16)
# - 학습 파라미터: 8백만 개
# - 저장 용량: 16MB
# - 성능: ⭐⭐⭐⭐⭐ (복잡한 작업)
# - 사용처: 번역, 요약, 코드 생성
# r=32 (매우 무거움)
lora_config = LoraConfig(r=32)
# - 학습 파라미터: 16백만 개
# - 저장 용량: 32MB
# - 성능: ⭐⭐⭐⭐⭐⭐ (최고 품질)
# - 사용처: 전문 도메인, 멀티태스크
# alpha 값의 영향
# alpha=16, r=8 → 비율 2:1
lora_config = LoraConfig(r=8, lora_alpha=16)
# 기존 모델 70% + 새로운 학습 30%
# → 안정적이지만 변화 적음
# alpha=32, r=8 → 비율 4:1
lora_config = LoraConfig(r=8, lora_alpha=32)
# 기존 모델 50% + 새로운 학습 50%
# → 균형잡힌 학습 (추천!)
# alpha=64, r=8 → 비율 8:1
lora_config = LoraConfig(r=8, lora_alpha=64)
# 기존 모델 20% + 새로운 학습 80%
# → 새로운 태스크에 강하게 적응
# 예시 1: 최소 튜닝 (빠름, 가벼움)
lora_config = LoraConfig(
target_modules=["q_proj"] # Query만
)
# 학습 시간: 1시간
# 성능: 70%
# 예시 2: 기본 튜닝 (추천)
lora_config = LoraConfig(
target_modules=["q_proj", "v_proj"] # Query + Value
)
# 학습 시간: 2시간
# 성능: 85%
# 예시 3: 풀 튜닝 (느림, 무거움)
lora_config = LoraConfig(
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"]
)
# 학습 시간: 4시간
# 성능: 95%
# 예시 4: 모든 레이어
lora_config = LoraConfig(
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"]
)
# 학습 시간: 8시간
# 성능: 98%
# 상황: 회사 규정, 매뉴얼 학습시켜야 함
# GPU: RTX 3090 (24GB) 1대
# 1. 모델 선택
model_name = "meta-llama/Llama-2-7b-hf" # 7B면 충분
# 2. QLoRA 설정
qlora_config = LoraConfig(
r=8, # 문서 Q&A는 중간 복잡도
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none"
)
# 3. 데이터 준비
# company_docs.json:
# [
# {"question": "연차는 어떻게 신청하나요?",
# "answer": "인트라넷 > 휴가관리 > 연차신청..."},
# ...
# ]
# 4. 학습 (2-3시간)
trainer.train()
# 5. 결과
# - 모델 크기: 8MB (USB에 넣어 다닐 수 있음!)
# - 정확도: 95%
# - 비용: GPU 대여 $20
# 상황: 의학 논문을 쉬운 말로 요약
# GPU: Google Colab Pro (Tesla T4)
# 1. 큰 모델 필요 (복잡한 전문 용어)
model_name = "mistralai/Mistral-7B-v0.1"
# 2. QLoRA 설정 (높은 품질)
qlora_config = LoraConfig(
r=16, # 의학 용어가 복잡하니 높게
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.1
)
# 3. 데이터 형식
# {
# "paper": "...복잡한 의학 논문 원문...",
# "summary": "이 연구는 새로운 치료법을 제안합니다..."
# }
# 4. 학습 결과
# - 전문 용어를 일반인이 이해할 수 있게 변환
# - 논문 읽는 시간: 2시간 → 5분
# 상황: 한국어, 영어, 일본어 고객 지원
# GPU: AWS EC2 g5.xlarge (A10G 24GB)
# 1. 다국어 모델
model_name = "facebook/xglm-7.5B"
# 2. 각 언어별 LoRA 어댑터
korean_lora = LoraConfig(r=8, lora_alpha=32)
english_lora = LoraConfig(r=8, lora_alpha=32)
japanese_lora = LoraConfig(r=8, lora_alpha=32)
# 3. 학습 (각각 별도로)
train_korean_lora(dataset_ko) # 한국어 고객 문의 데이터
train_english_lora(dataset_en) # 영어 고객 문의 데이터
train_japanese_lora(dataset_ja) # 일본어 고객 문의 데이터
# 4. 배포 (어댑터만 교체)
base_model + korean_lora # 한국 고객
base_model + english_lora # 영어 고객
base_model + japanese_lora # 일본 고객
# 5. 장점
# - 기본 모델 1개만 메모리에!
# - 어댑터만 바꿔가며 사용 (빠름!)
# - 각 언어별 저장 용량: 8MB씩만
# 시나리오: LLaMA 2 7B 모델 학습
# 방법 1: Full Fine-tuning (전통적 방법)
"""
- GPU: A100 80GB × 4대
- 메모리: 280GB
- 학습 시간: 24시간
- 비용: $1,000
- 저장 용량: 14GB
"""
# 방법 2: LoRA (개선)
"""
- GPU: A100 40GB × 1대
- 메모리: 40GB
- 학습 시간: 8시간
- 비용: $200
- 저장 용량: 16MB
"""
# 방법 3: QLoRA (최신!)
"""
- GPU: RTX 3090 24GB × 1대
- 메모리: 12GB
- 학습 시간: 4시간
- 비용: $50
- 저장 용량: 8MB
"""
# 성능 차이: Full (100%) ≈ LoRA (98%) ≈ QLoRA (96%)
# → 4% 차이로 비용 20배 절약!
# 1. 분류 작업 (감성 분석, 스팸 탐지 등)
LoraConfig(
r=4,
lora_alpha=16,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05
)
# 메모리: 6GB | 시간: 1시간 | 성능: ⭐⭐⭐⭐
# 2. 생성 작업 (챗봇, Q&A)
LoraConfig(
r=8,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.1
)
# 메모리: 10GB | 시간: 3시간 | 성능: ⭐⭐⭐⭐⭐
# 3. 복잡한 작업 (번역, 요약, 코드)
LoraConfig(
r=16,
lora_alpha=64,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.1
)
# 메모리: 16GB | 시간: 6시간 | 성능: ⭐⭐⭐⭐⭐⭐
# 실수 1: r 값을 너무 크게
lora_config = LoraConfig(r=128) # ❌ 너무 큼!
# → 메모리 부족, 과적합 위험
# ✅ r=8~16이면 충분
# 실수 2: target_modules를 잘못 지정
lora_config = LoraConfig(
target_modules=["wrong_name"] # ❌ 존재하지 않는 레이어
)
# ✅ 모델 구조 확인 필요: print(model)
# 실수 3: 4bit 양자화 후 fp32 사용
model = AutoModelForCausalLM.from_pretrained(
"llama-7b",
load_in_4bit=True,
torch_dtype=torch.float32 # ❌ 32비트 사용
)
# ✅ torch_dtype=torch.float16 사용
# 실수 4: batch size를 너무 크게
training_args = TrainingArguments(
per_device_train_batch_size=32 # ❌ 메모리 초과
)
# ✅ batch_size=1~4, gradient_accumulation으로 보완
# 1. 필요한 라이브러리 설치
pip install -q -U transformers peft accelerate bitsandbytes
# 2. GPU 확인
python -c "import torch; print(torch.cuda.is_available())"
# True가 나와야 함!
# 3. 메모리 확인
python -c "import torch; print(torch.cuda.get_device_properties(0).total_memory / 1e9)"
# 12GB 이상이면 OK
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model
import torch
# 1. 작은 모델로 시작 (1.3B)
model = AutoModelForCausalLM.from_pretrained(
"TinyLlama/TinyLlama-1.1B-Chat-v1.0",
load_in_4bit=True,
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(
"TinyLlama/TinyLlama-1.1B-Chat-v1.0"
)
# 2. 간단한 LoRA 설정
lora_config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
# 3. 학습 가능한 파라미터 확인
model.print_trainable_parameters()
# 4. 간단한 테스트
prompt = "안녕하세요!"
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_length=50)
print(tokenizer.decode(outputs[0]))
from datasets import Dataset
from transformers import TrainingArguments, Trainer
# 1. 간단한 데이터셋 만들기
data = {
"text": [
"질문: 파이썬이 뭐예요? 답변: 파이썬은 프로그래밍 언어입니다.",
"질문: 머신러닝이 뭐예요? 답변: 머신러닝은 AI의 한 분야입니다.",
# ... 더 많은 예제
]
}
dataset = Dataset.from_dict(data)
# 2. Tokenize
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length",
truncation=True, max_length=128)
tokenized_dataset = dataset.map(tokenize_function, batched=True)
# 3. 학습 설정
training_args = TrainingArguments(
output_dir="./my-first-qlora",
num_train_epochs=3,
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
learning_rate=2e-4,
logging_steps=10,
save_steps=100,
fp16=True
)
# 4. 학습 시작!
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
tokenizer=tokenizer
)
trainer.train()
# 5. 저장
model.save_pretrained("./my-qlora-model")
"거대한 AI 모델을 노트북으로도 학습시킬 수 있게 해주는 마법 같은 기술"
✅ 사용해야 할 때:
❌ 사용하지 말아야 할 때:
# 상황: 하나의 기본 모델로 여러 전문 분야 커버
# 1. 기본 모델 로드 (1번만!)
base_model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
load_in_4bit=True,
device_map="auto"
)
# 2. 분야별 LoRA 어댑터 학습
# 의료 분야
medical_lora = LoraConfig(r=16, lora_alpha=32)
medical_model = get_peft_model(base_model, medical_lora)
train(medical_model, medical_dataset)
medical_model.save_pretrained("./lora-medical")
# 법률 분야
legal_lora = LoraConfig(r=16, lora_alpha=32)
legal_model = get_peft_model(base_model, legal_lora)
train(legal_model, legal_dataset)
legal_model.save_pretrained("./lora-legal")
# 금융 분야
finance_lora = LoraConfig(r=16, lora_alpha=32)
finance_model = get_peft_model(base_model, finance_lora)
train(finance_model, finance_dataset)
finance_model.save_pretrained("./lora-finance")
# 3. 사용 시 어댑터만 교체!
from peft import PeftModel
# 의료 질문
model = PeftModel.from_pretrained(base_model, "./lora-medical")
answer = model.generate("당뇨병 증상은?")
# 법률 질문
model = PeftModel.from_pretrained(base_model, "./lora-legal")
answer = model.generate("계약서 작성 시 주의사항은?")
# 금융 질문
model = PeftModel.from_pretrained(base_model, "./lora-finance")
answer = model.generate("주식 투자 전략은?")
# 장점:
# - 기본 모델 1개만 메모리에 (7GB)
# - 어댑터만 교체 (각 8MB, 0.1초 소요)
# - 무한대로 확장 가능!
실제 적용 사례:
# 예시: AI 어시스턴트 서비스
class MultiDomainAssistant:
def __init__(self):
# 기본 모델 로드 (1번만)
self.base_model = load_base_model_4bit()
# 어댑터 경로들
self.adapters = {
"의료": "./lora-medical",
"법률": "./lora-legal",
"금융": "./lora-finance",
"코딩": "./lora-coding",
"교육": "./lora-education"
}
def answer(self, question, domain):
# 도메인에 맞는 어댑터 로드
model = PeftModel.from_pretrained(
self.base_model,
self.adapters[domain]
)
return model.generate(question)
# 사용
assistant = MultiDomainAssistant()
assistant.answer("감기 증상은?", domain="의료")
assistant.answer("상속법이 뭐예요?", domain="법률")
assistant.answer("파이썬 함수 만들기", domain="코딩")
# 메모리 사용량:
# - 기본 모델: 7GB (고정)
# - 현재 어댑터: 8MB (동적 로드)
# 총: ~7GB만 사용! (전체를 5개 로드하면 35GB 필요)
# 상황: 모델을 단계별로 발전시키기
# Phase 1: 기본 한국어 학습
model = load_model_4bit("llama-2-7b")
lora_v1 = LoraConfig(r=8, lora_alpha=16)
model = get_peft_model(model, lora_v1)
train(model, korean_basic_dataset)
model.save_pretrained("./lora-korean-v1")
# Phase 2: 존댓말 학습 (v1 기반)
model = PeftModel.from_pretrained(base_model, "./lora-korean-v1")
lora_v2 = LoraConfig(r=8, lora_alpha=16)
model = get_peft_model(model, lora_v2)
train(model, formal_korean_dataset)
model.save_pretrained("./lora-korean-v2")
# Phase 3: 전문 용어 학습 (v2 기반)
model = PeftModel.from_pretrained(base_model, "./lora-korean-v2")
lora_v3 = LoraConfig(r=8, lora_alpha=16)
model = get_peft_model(model, lora_v3)
train(model, technical_korean_dataset)
model.save_pretrained("./lora-korean-v3")
# 결과:
# v1: 기본 한국어 ⭐⭐⭐
# v2: 기본 + 존댓말 ⭐⭐⭐⭐
# v3: 기본 + 존댓말 + 전문용어 ⭐⭐⭐⭐⭐
# 각 버전 보관 가능 (각 8MB)
# 버전 관리가 쉬움!
# 상황: 사용자별 맞춤 AI 만들기
class PersonalizedAI:
def __init__(self, base_model_path):
self.base_model = load_model_4bit(base_model_path)
def create_user_adapter(self, user_id, user_data):
"""
사용자 데이터로 개인 LoRA 생성
user_data: 사용자의 대화 히스토리, 선호도 등
"""
lora_config = LoraConfig(
r=4, # 개인화는 작게
lora_alpha=8,
target_modules=["q_proj", "v_proj"]
)
model = get_peft_model(self.base_model, lora_config)
# 사용자 데이터로 학습
train(model, user_data)
# 사용자별 어댑터 저장
model.save_pretrained(f"./user-lora/{user_id}")
return f"사용자 {user_id}의 AI 생성 완료!"
def chat(self, user_id, message):
# 사용자 어댑터 로드
model = PeftModel.from_pretrained(
self.base_model,
f"./user-lora/{user_id}"
)
return model.generate(message)
# 실제 사용
ai = PersonalizedAI("llama-2-7b")
# 사용자 A의 AI (격식체 선호)
ai.create_user_adapter(
user_id="user_A",
user_data=formal_conversation_data
)
# 사용자 B의 AI (캐주얼 선호)
ai.create_user_adapter(
user_id="user_B",
user_data=casual_conversation_data
)
# 대화
ai.chat("user_A", "오늘 날씨 어때?")
# → "오늘 날씨는 맑고 화창합니다."
ai.chat("user_B", "오늘 날씨 어때?")
# → "오늘 완전 좋아! 나가 놀기 딱이야~"
# 장점:
# - 100만 사용자 = 100만 개 어댑터 (각 2MB)
# - 총 저장 공간: 2TB (감당 가능!)
# - 기본 모델 1개로 모든 사용자 커버
# 문제: 4bit 모델 + 32bit 학습 = 메모리 낭비
# ❌ 비효율적
model = load_in_4bit() # 4bit
training_args = TrainingArguments(
fp16=False # 32bit로 학습!
)
# → 메모리: 20GB 사용
# ✅ 효율적
model = load_in_4bit() # 4bit
training_args = TrainingArguments(
fp16=True # 16bit로 학습!
)
# → 메모리: 10GB 사용 (절반!)
# ✅✅ 더 효율적 (A100, H100 전용)
training_args = TrainingArguments(
bf16=True # BFloat16 (더 안정적)
)
# 문제: 역전파 시 메모리 부족
# ❌ 메모리 부족
model = load_in_4bit()
training_args = TrainingArguments(
gradient_checkpointing=False
)
# → 메모리: 16GB (Out of Memory!)
# ✅ 해결
model = load_in_4bit()
model.gradient_checkpointing_enable() # 이거 추가!
training_args = TrainingArguments(
gradient_checkpointing=True,
gradient_checkpointing_kwargs={"use_reentrant": False}
)
# → 메모리: 8GB (절반!)
# → 학습 시간: +20% (약간 느려짐, 감수 가능)
# 문제: Batch size를 크게 하고 싶은데 메모리 부족
# ❌ 메모리 초과
training_args = TrainingArguments(
per_device_train_batch_size=16 # 메모리 폭발!
)
# ✅ 해결 (효과는 동일!)
training_args = TrainingArguments(
per_device_train_batch_size=1, # 작게
gradient_accumulation_steps=16 # 16번 누적
)
# 효과: batch_size=16과 동일
# 메모리: batch_size=1 수준
# 학습 시간: 거의 동일
# 💡 이해하기:
# 일반 방식 (batch=16):
# [데이터 16개] → 한번에 계산 → 업데이트
# 메모리: 16개분 필요
#
# Gradient Accumulation:
# [데이터 1개] → 계산 → 누적
# [데이터 1개] → 계산 → 누적
# ... (16번 반복)
# → 업데이트
# 메모리: 1개분만 필요!
# ❌ 비효율적 (전체 데이터를 메모리에)
dataset = load_dataset("json", data_files="large_data.json")
full_dataset = dataset["train"] # 100GB 메모리에 로드!
# ✅ 효율적 (스트리밍)
dataset = load_dataset(
"json",
data_files="large_data.json",
streaming=True # 스트리밍 모드!
)
# 데이터를 필요할 때만 조금씩 로드
# 메모리: 최소한만 사용
# ✅✅ 더 효율적 (사전 처리 + 캐싱)
from datasets import load_dataset
dataset = load_dataset("json", data_files="data.json")
# Tokenize하고 저장
tokenized = dataset.map(
tokenize_function,
batched=True,
remove_columns=dataset["train"].column_names,
cache_file_name="./cached_tokenized.arrow" # 캐시 저장!
)
# 다음 실행 때는 캐시에서 즉시 로드
# 시간 절약: 10분 → 10초
# 최신 기술: 메모리를 더욱 효율적으로!
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
# 기존 방식
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16
)
# ✅ 페이지드 어텐션 추가
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True, # Double Quantization
bnb_4bit_quant_type="nf4" # NormalFloat4 (더 정확)
)
model = AutoModelForCausalLM.from_pretrained(
"llama-7b",
quantization_config=bnb_config,
device_map="auto"
)
# 효과:
# - 메모리: 추가 10% 절약
# - 정확도: 유지 또는 향상
# - 속도: 거의 동일
# 증상
RuntimeError: CUDA out of memory. Tried to allocate 2.00 GiB
# 해결책 순서대로 시도:
# 1. Batch size 줄이기
training_args = TrainingArguments(
per_device_train_batch_size=1, # 4 → 1
gradient_accumulation_steps=4
)
# 2. Gradient checkpointing 활성화
model.gradient_checkpointing_enable()
# 3. Max length 줄이기
tokenizer(text, max_length=512) # 1024 → 512
# 4. LoRA rank 줄이기
lora_config = LoraConfig(r=4) # 8 → 4
# 5. Target modules 줄이기
lora_config = LoraConfig(
target_modules=["q_proj"] # v_proj 제거
)
# 6. 메모리 정리
import gc
import torch
gc.collect()
torch.cuda.empty_cache()
# 7. 더 작은 모델 사용
# 7B → 3B → 1B
# 증상: 1 epoch에 10시간...
# 해결책:
# 1. 데이터 로더 워커 증가
training_args = TrainingArguments(
dataloader_num_workers=4 # 0 → 4
)
# 2. FP16 활성화
training_args = TrainingArguments(
fp16=True
)
# 3. Optimizer 변경
from transformers import AdamW
from bitsandbytes.optim import AdamW8bit
training_args = TrainingArguments(
optim="adamw_8bit" # 8bit optimizer (빠름!)
)
# 4. 데이터 캐싱
dataset = dataset.map(
preprocess,
cache_file_name="./cache.arrow"
)
# 5. 학습률 스케줄러 조정
training_args = TrainingArguments(
lr_scheduler_type="cosine", # linear → cosine
warmup_steps=100 # warmup 줄이기
)
# 증상: Loss는 줄어드는데 결과가 이상함
# 원인 1: 과적합
# 해결:
lora_config = LoraConfig(
lora_dropout=0.1 # Dropout 추가
)
training_args = TrainingArguments(
num_train_epochs=3, # 10 → 3 (줄이기)
eval_strategy="steps",
eval_steps=100, # 평가 자주 하기
save_strategy="steps",
save_steps=100,
load_best_model_at_end=True # 최적 모델 로드
)
# 원인 2: Learning rate가 너무 높음
# 해결:
training_args = TrainingArguments(
learning_rate=1e-4 # 2e-4 → 1e-4
)
# 원인 3: 데이터 품질 문제
# 해결:
# - 데이터 검증
# - 중복 제거
# - 균형 맞추기
# 원인 4: LoRA rank가 너무 작음
# 해결:
lora_config = LoraConfig(
r=16 # 4 → 16
)
# ========================================
# 1단계: 환경 설정
# ========================================
# 설치
!pip install -q transformers peft accelerate bitsandbytes datasets
import torch
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TrainingArguments,
BitsAndBytesConfig
)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from datasets import load_dataset
from trl import SFTTrainer
# GPU 확인
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB")
# ========================================
# 2단계: 데이터 준비
# ========================================
# 한국어 뉴스 요약 데이터셋
dataset = load_dataset("json", data_files={
"train": "korean_news_summary_train.json",
"test": "korean_news_summary_test.json"
})
# 데이터 형식:
# {
# "article": "서울시는 오늘 새로운 교통 정책을 발표했다...",
# "summary": "서울시 새 교통정책 발표"
# }
# 프롬프트 템플릿
def format_prompt(example):
return f"""### 기사:
{example['article']}
### 요약:
{example['summary']}"""
# 데이터셋에 적용
train_dataset = dataset["train"].map(
lambda x: {"text": format_prompt(x)}
)
# ========================================
# 3단계: 모델 로드 (QLoRA)
# ========================================
# 4bit 양자화 설정
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
# 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"beomi/llama-2-ko-7b",
quantization_config=bnb_config,
device_map="auto",
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained("beomi/llama-2-ko-7b")
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
# ========================================
# 4단계: LoRA 설정
# ========================================
# Gradient checkpointing 활성화
model.gradient_checkpointing_enable()
model = prepare_model_for_kbit_training(model)
# LoRA 설정 (요약은 복잡하니 r=16)
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=[
"q_proj",
"k_proj",
"v_proj",
"o_proj"
],
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# ========================================
# 5단계: 학습 설정
# ========================================
training_args = TrainingArguments(
output_dir="./korean-summarizer-qlora",
num_train_epochs=3,
per_device_train_batch_size=1,
gradient_accumulation_steps=8, # 실질적 batch=8
gradient_checkpointing=True,
optim="paged_adamw_8bit",
learning_rate=2e-4,
lr_scheduler_type="cosine",
warmup_steps=100,
logging_steps=10,
save_steps=100,
eval_steps=100,
eval_strategy="steps",
fp16=True,
push_to_hub=False,
report_to="none"
)
# ========================================
# 6단계: 학습
# ========================================
trainer = SFTTrainer(
model=model,
train_dataset=train_dataset,
peft_config=lora_config,
dataset_text_field="text",
max_seq_length=1024,
tokenizer=tokenizer,
args=training_args
)
# 학습 시작!
print("🚀 학습 시작...")
trainer.train()
# 모델 저장
trainer.save_model("./korean-summarizer-final")
print("✅ 학습 완료!")
# ========================================
# 7단계: 테스트
# ========================================
def summarize(article):
prompt = f"""### 기사:
{article}
### 요약:
"""
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(
**inputs,
max_new_tokens=100,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.2,
do_sample=True
)
result = tokenizer.decode(outputs[0], skip_special_tokens=True)
# 요약 부분만 추출
summary = result.split("### 요약:")[-1].strip()
return summary
# 테스트
test_article = """
서울시는 15일 2024년 새로운 대중교통 정책을 발표했다.
이번 정책에는 심야버스 노선 확대, 지하철 막차 시간 연장,
통합 환승 할인 제도 개선 등이 포함되어 있다.
특히 강남과 강북을 연결하는 심야버스 5개 노선이 신설되며,
주말 지하철 운행 시간이 새벽 2시까지 연장된다.
"""
summary = summarize(test_article)
print("\n📰 원문:")
print(test_article)
print("\n📝 요약:")
print(summary)
# ========================================
# 8단계: 성능 평가
# ========================================
from rouge import Rouge
rouge = Rouge()
# 테스트 데이터로 평가
predictions = []
references = []
for example in dataset["test"][:100]: # 100개 샘플
pred = summarize(example["article"])
predictions.append(pred)
references.append(example["summary"])
# ROUGE 점수 계산
scores = rouge.get_scores(predictions, references, avg=True)
print("\n📊 성능 평가:")
print(f"ROUGE-1: {scores['rouge-1']['f']:.4f}")
print(f"ROUGE-2: {scores['rouge-2']['f']:.4f}")
print(f"ROUGE-L: {scores['rouge-l']['f']:.4f}")
# ========================================
# 9단계: 모델 공유 (선택사항)
# ========================================
# Hugging Face Hub에 업로드
model.push_to_hub("my-username/korean-summarizer-qlora")
tokenizer.push_to_hub("my-username/korean-summarizer-qlora")
print("\n✨ 모든 과정 완료!")
print(f"📦 모델 크기: {os.path.getsize('./korean-summarizer-final') / 1e6:.2f} MB")
✅ GPU 메모리 확인 (최소 12GB 권장)
✅ CUDA 버전 확인 (11.0 이상)
✅ 라이브러리 설치 완료
✅ 데이터 준비 완료
✅ 충분한 저장 공간 (모델 + 체크포인트)
# 필수 설정
✅ load_in_4bit=True
✅ torch_dtype=torch.float16 (또는 bfloat16)
✅ device_map="auto"
✅ gradient_checkpointing=True
✅ fp16=True (학습 시)
# LoRA 설정
✅ r: 4~16 사이 (작업에 따라)
✅ lora_alpha: r의 2배
✅ target_modules: 적절한 레이어 선택
✅ lora_dropout: 0.05~0.1
# 학습 설정
✅ batch_size: 1~4
✅ gradient_accumulation_steps: 4~16
✅ learning_rate: 1e-4 ~ 2e-4
✅ max_seq_length: 메모리에 맞게
# 공식 문서
- Hugging Face PEFT: https://huggingface.co/docs/peft
- QLoRA 논문: https://arxiv.org/abs/2305.14314
- BitsAndBytes: https://github.com/TimDettmers/bitsandbytes
# 실습 자료
- Hugging Face 예제: https://github.com/huggingface/peft/tree/main/examples
- Google Colab 튜토리얼: 검색 "QLoRA Colab tutorial"
# 커뮤니티
- Hugging Face Forums
- Reddit r/LocalLLaMA
- Discord 서버들