회고기간 : 2025.04.28월~2025.05.02금
이번 주차는 fine tunning 기법에 대해 배웠다
Fine tunning
- 이미 학습된 LLM을 특정 작업이나 데이터셋에 맞춰 재학습하는 기능
- 프롬프트 엔지니어링만으로 해결하기 어려운 문제 해결
- 모델 추가학습 없이 입력을 조정하하는 프롬프트 엔지니어링과 달리 모델을 재학습하여 특정 작업에 최적화
sLLM
- LLM의 축소판으로, 학습과 배포 비용을 줄이기 위해 설계
- sLLM은 파인튜닝이 비교적 간단하고 리소스가 적게 듦 (양자화가 필요없음)
- 주로 제한된 연산 리소스를 가진 환경에서 활용
sLLM 모델 (meta-llama/Llama-2-7b-hf) 사용해보기
from transformers import AutoTokenizer, AutoModelForCausalLM # AutoModelForCausalLM : 문장 생성에 특화된 모델 로드
promt = "Explain the history of AI in simple term."
model_name = "meta-llama/Llama-2-7b-hf"
# 1. 토크나이저 로드
tokenizer = AutoTokenizer.from_pretrained(model_name)
# 2. 모델 로드
model = AutoModelForCausalLM.pre_trained(model_name, device_map="auto")
# 3. 입력값
inputs = tokenizer(prompt, return_tensors='pt').to(model.device)
# 4. 출력값(텍스트 생성)
outputs = model.generate(**inputs, max_length=500)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(response)
⚠️ 입력값에서 tokenizer의 인자 return_tensors='pt'는 텍스트를 pytorch 텐서로 변환하여 모델이 처리할 수 있는 형태로 변환
만약, return_tensors='pt' 없이
tokenizer = AutoTokenizer.from_pretrained(model_name) 만 한다면 출력형태는

딕셔너리, 리스트 기반이 되어 텐서로 변환이 필요하다.
하지만 return_tensors를 적용하면 모델 입력이 가능한 형태가 된다

LoRA
- 모델 전체 파라미터를 건드리지 않고 일부 레이어만 미세조정
- q_proj, v_proj, p_proj 등 일부 Linear 층에 LoRA를 인젝션
- LoraConfig + get_peft_model()
LoRA 기반 gpt2 감성 분석 모델 튜닝
순서 : 토크나이저 및 모델 pad_token -> LoRA 설정 및 적용 -> 데이터셋 로드 -> 데이터 분리(학습용 검증용) -> 데이터 변환(텐스트는 토큰 id, 라벨은 텐서로 변환) ->배치 자동화 -> 학습 설정 및 트레이너 설정 -> 훈련
!pip install peft datasets transformers
# 허깅페이스 로그인
from huggingface_hub import login
login(token="input your token")
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model
from dataset import load dataset
# gpu 사용 설정을 위한 device 변수 생성
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
# 병렬 토크나이저 경고 방지
os.environ['TOKENIZERS_PARALLELISM'] = 'false'
# 모델 로드
model_name = 'gpt2'
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token # [pad] 문자열 토큰에 [EOS] 할당
base_model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
base_model.config.pad_token_id = tokenizer.pad_token_id # 토큰의 id를 모델이 인식할 수 있도록 알려주는 설정값
💡여기서 pad_token을 맞춰주는 이유?
문장 길이가 다를 때, 모델이 어디까지 입력이고 어디부터 padding인지 인식할 수 있도록 해줘야함
sLLM처럼 텍스트 생성은 문장을 왼->오 로 생성하기 때문에 sequpadding을 비교하지 않아도 o
하지만 분류나 QA처럼 문장 구조를 비교하는 모델은 의미 있는 문장을 끊어 학습하기 위해 pad_token과 attention_mask가 필
| 구조 | 설명 | pad_token 필요성 |
| Sequential | 한 토큰씩 순서대로 처리 | X |
| Parallel | 여러 문장을 한번에 처리 -> 길이 맞춰야함 | O |
# LoRA 설정
lora_config = LoraConfig(
r=8, # LoRA 저랭크 차원
lora_alpha=32, # LoRA 가중치 스케일링 파라미터
target_modules=["c_attn", "c_fc", "c_proj"], # LoRA 적용할 계층
lora_dropout=0.1 # 드롭아웃 설정
)
# 기반 모델에 LoRA 적용 (+ GPU로 이동)
model = get_peft_model(base_model, lora_config).to(device)
## 학습용 데이터 준비
긍정 리뷰 train에서 500개 샘플링
부정 리뷰 train에서 500개 샘플링
train_texts = > 긍정+부정 리뷰에서 text인 것
train_label => 긍정+부정 리뷰에서 label인 것
## 검증용 데이터 준비
긍정 리뷰 train에서 100개 샘플링
부정 리뷰 train에서 100개 샘플링
eval_texts = > 긍정+부정 리뷰에서 text인 것
eval_label => 긍정+부정 리뷰에서 label인 것
# 데이터 토큰화
def preprocess_data(texts, labels):
encodings = tokenizer(
texts,
truncation=True,
padding="max_length",
max_length=512,
return_tensors="pt"
)
encodings["labels"] = torch.tensor(labels, dtype=torch.long)
return encodings
train_encodings = preprocess_data(train_texts, train_labels)
eval_encodings = preprocess_data(eval_texts, eval_labels)
# 데이터셋 변환을 위한 클래스
class IMDBDataset(torch.utils.data.Dataset):
def __init__(self, encodings):
self.encodings = encodings
def __len__(self):
return len(self.encodings["input_ids"])
def __getitem__(self, idx):
return {
key: val[idx]
for key, val in self.encodings.items()
}
train_dataset = IMDBDataset(train_encodings)
eval_dataset = IMDBDataset(eval_encodings)
# 배치를 GPU로 자동 이동시키는 data_collator 함수
def collate_fn(batch):
batch = {key: torch.stack([item[key] for item in batch]) for key in batch[0]}
return batch
# 학습 설정
training_args = TrainingArguments(
output_dir='./results', # 모델 저장 경로
per_device_train_batch_size=4, # 훈련 배치 크기
per_device_eval_batch_size=4, # 평가(검증) 배치 크기
num_train_epochs=5, # 학습 횟수 (에포크)
save_steps=100, # 저장 주기
save_total_limit=2, # 최대 저장 모델 개수
eval_strategy='epoch', # 에포크 단위 평가
logging_dir='./logs', # 로그 저장 경로
logging_steps=10, # 로그 출력 주기
fp16=True # FP16 연산 최적화
)
# 트레이너 설정
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
data_collator=collate_fn
)
trainer.train()
def predict_sentiment(text):
inputs = tokenizer(
text,
return_tensors="pt",
padding=True,
truncation=True,
max_length=512
).to(device)
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits
prediction = torch.argmax(logits, dim=-1).item()
return "긍정" if prediction == 1 else "부정"
text_review = "It was very boring..."
result = predict_sentiment(text_review)
result
Q-LoRA
- 4bit 양자화 모델 + LoRA 를 결합하여 GPU 효율을 극대화
- BitsAndBytesConfig로 4bit 설정 + LoRA 모델
Q-LoRA 사용
순서 : 양자화 설정 -> 토크나이저 및 모델 로드 -> 데이터셋 로드 -> 데이터셋 전처리 -> LoRA 설정 및 적용 ->Train
# 4-bit 양자화된 모델 로드를 위한 설정
bnb_config = {
"load_in_4bit": True,
"bnb_4bit_compute_dtype": torch.float16,
"bnb_4bit_quant_type": "nf4",
"device_map": "auto"
}
# 토크나이저 및 모델 로드 (모델 로드 시 4-bit 양자화 설정)
tokenizer = AutoTokenizer.from_pretrained(model_name)
base_model = AutoModelForCausalLM.from_pretrained(model_name, **bnb_config)
Soft Prompt
- 모델 파라미터는 고정하고, 임베딩만 학습해서 성능 개선
- 기존 문장 앞에 학습 가능한 가상 프롬프트 토큰 삽입
- PromptTuningConfig 사용
peft_config = PromptTuningConfig(
task_type="CAUSAL_LM",
num_virtual_tokens=10,
token_dim=model.config.hidden_size
)
print(model.config.hidden_size)
model = get_peft_model(model, peft_config)
RLHF
- 인간 피드백을 반영해 보상 모델을 만들고, 강화학습(PPO)로 보정 학습
- 지도학습 -> 보상모델 학습 -> PPO 튜닝
- ChatGPT처럼 인간 친화적 응답에 LLM을 적용함
# 1. 시 로드 및 지도학습
# LoRA 설정
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
# LoRA 적용
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
model.train() # 모델 학습 모드 설정
# 2. 리워드 모델 생성 및 학습
reward_model = AutoModelForCasulML.from_pretrained(
model_name,
quantization_config=bnb_config
)
reward_model = prepare_model_for_kbit_training(reward_model)
# 3. orpo 설정 및 적용
orpo_config = ORPOConfig(
output_dir="./orpo_output",
per_device_train_batch_size=8,
gradient_accumulation_steps=16,
learning_rate=2e-6,
num_train_epochs=5, # 3~5가 적합
logging_steps=50,
save_total_limit=2,
remove_unused_column=False,
fp16=True,
gradient_checkpointing=True,
max_grad_norm=1.0,
warmup_steps=100,
save_steps=500
)
DPO
- 선호-비선호 응답 쌍으로 선호 응답을 잘 예측하도록 학습시키는 방식
- PPO와 달리 보상모델이 필요하지 않아 간단하게 구현할 수 있다.
class DPOTrainer(Trainer):
def compute_loss(self, model, inputs, beta=0.1, *args, **kwargs):
# 1. 입력값 준비
input_ids=inputs['input_ids'].to(model.device),
attention_mask=inputs['attention_mask'].to(model.device),
preferred_ids=inputs['preferred_ids'].to(model.device),
dispreferred_ids=inputs['dispreferred_ids'].to(model.device)
# 2. 두 응답 각각의 모델에 통과시킴
preferred_output = model(input_ids=input_ids, attention_mask=attention_mask, labels=preferred_ids)
attention_mask=attention_mask, lables=preferred_ids
dispreferred_output = model(input_ids=input_ids, attention_mask=attention_mask, labels=dispreferred_ids)
attention_mask=attention_mask, lables=dispreferred_ids
# 3. 선호 비교 후 로스를 계산
preferred_loss=preferred_output.loss
dispreferred_loss=dispreferred_output.loss
loss = -F.logsigmoid(beta * (dispreferred_loss - preferred_loss)).mean()
return loss
⭐checkpoint 설정
checkpoint
: 학습 중간에 저장한 모델 상태(가중치, 옵티마이저, 스텝 정보)의 기록
: TrainingArguments에서 확인 가능 (save_steps, save_total_limit)
💡 Keep
이번주는 없습니다. (열심히 부지런히 살기)
⚠️ Problem
연휴라고 너무 펑펑펑 놀지 않기
🔥 Try
다음주에 시작될 프로젝트를 위해 제때 복습하고 방향성 잘 설계하기
파인튜닝 지도학습, 강화학습을 제대로 복습하면서 앞으로 있을 프로젝트에 파인튜닝을 어떻게 해야 적합할지에 대해 조금 더 고민해봐야겠다.

'SKN > Remind' 카테고리의 다른 글
| sk네트웍스 family AI 캠프 11기 5월 2주차 회고록 (0) | 2025.05.19 |
|---|---|
| sk네트웍스 family AI 캠프 11기 5월 1주차 회고록 (0) | 2025.05.12 |
| sk네트웍스 family AI 캠프 11기 4월 4주차 회고록 (3) | 2025.04.28 |
| sk네트웍스 family AI 캠프 11기 4월 3주차 회고록 (1) | 2025.04.20 |
| sk네트웍스 family AI 캠프 11기 4월 1주차 회고록 (0) | 2025.04.07 |