การปรับแต่งละเอียด LoRA ด้วย Hugging Face PEFT
กำหนดค่าอันดับ LoRA ค่า alpha และโมดูลเป้าหมาย เรียกใช้การปรับแต่งละเอียดแบบมีผู้สอนด้วย TRL SFTTrainer ติดตามค่าความสูญเสียระหว่างฝึก และบันทึกจุดตรวจสอบทั้งแบบผสานแล้วและแบบมีเฉพาะอะแดปเตอร์
การปรับแต่งละเอียด LoRA ด้วย Hugging Face PEFT เป็นบทเรียน AI Engineering Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Engineering Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงเลือกใช้ LoRA แทนการปรับแต่งละเอียดทั้งโมเดล
การปรับแต่งละเอียดทั้งโมเดลจะอัปเดตพารามิเตอร์ทุกตัวในโมเดล สำหรับโมเดลที่มีพารามิเตอร์ 7 พันล้านตัวและใช้ความละเอียดแบบ float32 จะต้องใช้หน่วยความจำ GPU ประมาณ 28GB เฉพาะสำหรับค่าน้ำหนัก และยังต้องใช้พื้นที่สำหรับสถานะของตัวปรับค่า เกรเดียนต์ และแอ็กทิเวชัน รวมแล้วอาจสูงถึง 80-120GB ได้โดยง่าย LoRA (Low-Rank Adaptation) ใช้วิธีเพิ่มพารามิเตอร์ที่ฝึกได้จำนวนเล็กน้อยมาก (โดยทั่วไป 0.1-1% ของพารามิเตอร์ทั้งหมด) ในรูปคู่เมทริกซ์แรงก์ต่ำที่นำไปใช้กับเลเยอร์ที่เลือก จึงลดความต้องการ GPU ได้ 10-50 เท่า ขณะเดียวกันยังให้ผลลัพธ์ใกล้เคียงกัน
# LoRA math intuition:
# Full fine-tuning: update W (large matrix, e.g., 4096 x 4096 = 16.7M parameters)
# LoRA: instead train W = W_0 + A @ B where:
# A has shape (4096, r) - only r*4096 params
# B has shape (r, 4096) - only r*4096 params
# r (rank) is typically 4, 8, or 16 - much smaller than 4096
# Memory comparison for 7B model:
# Full fine-tuning: ~80GB GPU RAM
# LoRA (rank=8): ~12GB GPU RAM - fits on a single A100 or 3090
print('LoRA makes fine-tuning accessible without massive GPU clusters')การติดตั้งไลบรารีที่จำเป็น
การปรับแต่งละเอียดด้วย LoRA โดยใช้ Hugging Face ต้องใช้ไลบรารี 3 รายการ ได้แก่ transformers (สำหรับโหลดโมเดลและแบ่งโทเคน), peft (การปรับแต่งละเอียดที่ใช้พารามิเตอร์อย่างมีประสิทธิภาพ ซึ่งเป็นส่วนที่นำ LoRA ไปใช้งาน) และ trl (การเรียนรู้แบบเสริมกำลังสำหรับทรานส์ฟอร์เมอร์ ซึ่งมี SFTTrainer สำหรับการปรับแต่งละเอียดแบบมีผู้สอน) เมื่อใช้ร่วมกัน ไลบรารีเหล่านี้จะให้เวิร์กโฟลว์การปรับแต่งละเอียดระดับสูงที่พร้อมใช้งานจริง
# pip install transformers peft trl accelerate bitsandbytes datasets
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer, SFTConfig
from datasets import Dataset
import torch
print('Libraries imported successfully')
print(f'GPU available: {torch.cuda.is_available()}')
if torch.cuda.is_available():
print(f'GPU: {torch.cuda.get_device_name(0)}')
print(f'GPU memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB')การโหลดโมเดลพื้นฐานด้วยการทำควอนไทซ์
สำหรับการปรับแต่งละเอียดด้วย LoRA ส่วนใหญ่ ให้โหลดโมเดลพื้นฐานโดยใช้ ควอนไทซ์แบบ 4 บิต ผ่านบิตส์แอนด์ไบต์ส วิธีนี้ลดการใช้หน่วยความจำของโมเดลพื้นฐานลง 75% (โมเดล 7B ลดจากประมาณ 14GB เหลือประมาณ 4GB) โดยยังคงความสามารถส่วนใหญ่ของโมเดลไว้ได้ เลเยอร์ตัวปรับ LoRA เท่านั้นที่ได้รับการฝึกด้วยความละเอียดเต็ม การใช้ควอนไทซ์แบบ 4 บิตร่วมกับ LoRA เรียกว่า QLoRA และทำให้การปรับแต่งละเอียดโมเดล 7B สามารถทำได้บน GPU สำหรับผู้ใช้ทั่วไป
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch
MODEL_NAME = 'mistralai/Mistral-7B-Instruct-v0.2' # or 'meta-llama/Llama-3.2-3B-Instruct'
# 4-bit quantization config (QLoRA)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # quantize base model to 4-bit
bnb_4bit_quant_type='nf4', # NF4 quantization type
bnb_4bit_compute_dtype=torch.float16, # compute in float16
bnb_4bit_use_double_quant=True # double quantization for extra savings
)
# Load quantized model
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
quantization_config=bnb_config,
device_map='auto', # automatically distribute across GPUs
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
tokenizer.pad_token = tokenizer.eos_token # required for training batches
print(f'Model loaded. Parameters: {model.num_parameters():,}')การกำหนดไฮเปอร์พารามิเตอร์ของ LoRA
ไฮเปอร์พารามิเตอร์ของ LoRA ที่สำคัญที่สุดมี 3 รายการ ได้แก่ r (แรงก์) ซึ่งคือมิติของเมทริกซ์แรงก์ต่ำ ยิ่งแรงก์สูง ก็ยิ่งมีพารามิเตอร์ที่ฝึกได้มากและปรับเปลี่ยนได้ละเอียดขึ้น แต่จะใช้หน่วยความจำมากขึ้นและเสี่ยงต่อการเรียนรู้เกินพอดีมากขึ้น lora_alpha คือปัจจัยการปรับขนาด ซึ่งโดยทั่วไปกำหนดเป็น 2 เท่าของแรงก์ target_modules คือเมทริกซ์น้ำหนักที่จะใช้ LoRA โดยเลเยอร์ความสนใจ (q_proj, v_proj) เป็นตัวเลือกที่ใช้กันมากที่สุด เริ่มต้นด้วย r=8 แล้วปรับค่าจากจุดนี้
from peft import LoraConfig, TaskType
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM, # causal language modeling
r=8, # rank: 4, 8, 16, 32 — higher = more params
lora_alpha=16, # scaling: usually 2*r
lora_dropout=0.1, # dropout on LoRA layers for regularization
target_modules=['q_proj', 'v_proj', # which weight matrices to adapt
'k_proj', 'o_proj', # common to target all attention projections
'gate_proj', 'up_proj', 'down_proj'], # and FFN layers
bias='none', # do not adapt bias parameters
)
# Wrap the model with LoRA adapters
from peft import get_peft_model, prepare_model_for_kbit_training
model = prepare_model_for_kbit_training(model) # prepares quantized model for training
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Output: trainable params: 4,194,304 || all params: 3,752,071,168 || trainable%: 0.11%การเตรียมชุดข้อมูล
SFTTrainer ต้องการชุดข้อมูลในรูปแบบเฉพาะ รูปแบบที่ง่ายที่สุดคือชุดข้อมูลที่มีคอลัมน์ text เพียงคอลัมน์เดียว ซึ่งมีสตริงพรอมต์ + คำตอบที่จัดรูปแบบไว้อย่างสมบูรณ์ ใช้แม่แบบการสนทนาของตัวแบ่งโทเคนเพื่อจัดรูปแบบตัวอย่างการสนทนาให้สอดคล้องกัน SFTTrainer จะจัดการการแบ่งโทเคน การจัดชุดข้อมูลย่อย และการทำมาสก์ค่าความสูญเสีย (คำนวณค่าความสูญเสียเฉพาะคำตอบของผู้ช่วย ไม่รวมพรอมต์อินพุต)
from datasets import Dataset
import json
def load_and_format_dataset(jsonl_path: str, tokenizer) -> Dataset:
examples = []
with open(jsonl_path) as f:
for line in f:
ex = json.loads(line.strip())
# Apply chat template to format as expected by the model
formatted = tokenizer.apply_chat_template(
ex['messages'],
tokenize=False,
add_generation_prompt=False
)
examples.append({'text': formatted})
return Dataset.from_list(examples)
# Load training and validation datasets
train_dataset = load_and_format_dataset('train.jsonl', tokenizer)
val_dataset = load_and_format_dataset('validation.jsonl', tokenizer)
print(f'Train examples: {len(train_dataset)}')
print(f'Validation examples: {len(val_dataset)}')
print('Sample formatted text:')
print(train_dataset[0]['text'][:300])การฝึกด้วย SFTTrainer
SFTTrainer จาก TRL จะครอบ Trainer ของ Hugging Face พร้อมค่าเริ่มต้นสำหรับการปรับแต่งละเอียดแบบมีผู้สอน ให้กำหนดไฮเปอร์พารามิเตอร์การฝึก ได้แก่ จำนวนรอบการฝึก (โดยทั่วไป 1-3 รอบก็เพียงพอสำหรับการปรับแต่งละเอียดตามคำสั่ง) ขนาดชุดข้อมูลย่อย จำนวนขั้นตอนการสะสมเกรเดียนต์ (เพื่อจำลองชุดข้อมูลย่อยที่ใหญ่ขึ้นเมื่อมีหน่วยความจำจำกัด) อัตราการเรียนรู้ (2e-4 เป็นจุดเริ่มต้นที่ใช้กันทั่วไป) และไดเรกทอรีเอาต์พุตสำหรับจุดตรวจสอบ
from trl import SFTTrainer, SFTConfig
training_args = SFTConfig(
output_dir='./fine-tuned-model',
num_train_epochs=2, # 2-3 epochs for instruction fine-tuning
per_device_train_batch_size=4, # increase if GPU memory allows
gradient_accumulation_steps=4, # effective batch size = 4*4 = 16
learning_rate=2e-4, # typical LoRA learning rate
warmup_ratio=0.05, # warmup for 5% of steps
lr_scheduler_type='cosine', # cosine decay learning rate schedule
logging_steps=10,
eval_strategy='steps',
eval_steps=50, # evaluate on validation set every 50 steps
save_steps=100,
max_seq_length=2048, # max token length per example
fp16=True, # mixed precision training
report_to='none' # or 'wandb' for experiment tracking
)
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
)
print('Starting LoRA fine-tuning...')
trainer.train()การติดตามความคืบหน้าการฝึก
ระหว่างการฝึก ให้ติดตามตัวชี้วัดสำคัญ 2 รายการ ได้แก่ ค่าความสูญเสียจากการฝึก ซึ่งควรลดลงอย่างต่อเนื่อง และ ค่าความสูญเสียจากการตรวจสอบความถูกต้อง ซึ่งควรลดลงในช่วงแรก จากนั้นคงที่หรือเพิ่มขึ้นเล็กน้อย (แสดงว่ากำลังเรียนรู้เกินพอดี) หากค่าความสูญเสียจากการตรวจสอบความถูกต้องเพิ่มขึ้นอย่างมาก ขณะที่ค่าความสูญเสียจากการฝึกยังลดลง ให้หยุดการฝึกก่อนกำหนด เพราะโมเดลกำลังจดจำตัวอย่างการฝึกแทนที่จะเรียนรู้เพื่อใช้งานกับข้อมูลใหม่ จุดหยุดที่เหมาะสมคือช่วงก่อนที่ค่าความสูญเสียจากการตรวจสอบความถูกต้องจะเริ่มเพิ่มขึ้น
# Reading training logs
# Training step logs look like:
# {'loss': 1.4523, 'grad_norm': 0.85, 'learning_rate': 0.0002, 'epoch': 0.2, 'step': 20}
# {'loss': 1.2341, 'grad_norm': 0.72, 'learning_rate': 0.00018, 'epoch': 0.4, 'step': 40}
# Validation results look like:
# {'eval_loss': 1.1823, 'eval_runtime': 12.3, 'eval_samples_per_second': 8.1, 'step': 50}
# {'eval_loss': 1.0923, 'eval_runtime': 12.1, 'eval_samples_per_second': 8.3, 'step': 100}
# {'eval_loss': 1.1234, 'eval_runtime': 12.4, 'eval_samples_per_second': 8.2, 'step': 150}
# ^ validation loss went UP at step 150 - overfitting starting
# Load the best checkpoint (lowest validation loss)
from transformers import TrainerCallback
print('Best model checkpoint is saved automatically by SFTTrainer (load_best_model_at_end=True)')การบันทึกตัวปรับ LoRA และการรวมเข้าด้วยกัน
หลังการฝึก ให้บันทึกค่าน้ำหนักของตัวปรับ LoRA แยกจากโมเดลพื้นฐาน ตัวปรับมีขนาดเล็กมาก (ตั้งแต่ไม่กี่ MB ถึงไม่กี่ร้อย MB) และสามารถนำไปใช้กับโมเดลพื้นฐานขณะอนุมานได้ สำหรับการนำไปใช้งานจริง คุณยังสามารถ รวมค่าน้ำหนักของ LoRA เข้ากับโมเดลพื้นฐาน เพื่อสร้างไฟล์โมเดลไฟล์เดียวที่ไม่ต้องใช้ไลบรารี PEFT ขณะอนุมาน ซึ่งจะช่วยลดภาระในการอนุมาน
# Save only the LoRA adapter (tiny - typically 10-100MB)
model.save_pretrained('./lora-adapter-only')
tokenizer.save_pretrained('./lora-adapter-only')
# Load the adapter for inference (requires base model + peft)
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype=torch.float16, device_map='auto')
model_with_adapter = PeftModel.from_pretrained(base_model, './lora-adapter-only')
# Alternative: Merge adapter into base model (no PEFT needed at inference)
print('Merging LoRA weights into base model...')
merged_model = model_with_adapter.merge_and_unload() # creates a regular model
merged_model.save_pretrained('./merged-model')
print('Merged model saved - can be used with standard transformers, no PEFT needed')การอนุมานด้วยโมเดลที่ปรับแต่งละเอียดแล้ว
ทดสอบโมเดลที่ปรับแต่งละเอียดแล้วกับพรอมต์ชุดหนึ่งที่กันไว้ออกจากการฝึก ก่อนสรุปว่าการทำงานประสบความสำเร็จ เปรียบเทียบเอาต์พุตแบบเคียงข้างกันระหว่างโมเดลพื้นฐานกับโมเดลเดียวกันที่ปรับแต่งละเอียดแล้ว โดยใช้พรอมต์เดียวกัน เพื่อยืนยันว่าการปรับแต่งละเอียดบรรลุเป้าหมาย ตรวจสอบทั้งกรณีที่โมเดลควรจัดการได้ดีขึ้น (งานเป้าหมาย) และกรณีที่ควรยังจัดการได้ดี (งานทั่วไปที่ไม่ต้องการให้ประสิทธิภาพลดลง)
def generate(model, tokenizer, prompt: str, max_new_tokens=512) -> str:
inputs = tokenizer(prompt, return_tensors='pt').to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=0.1,
do_sample=True,
pad_token_id=tokenizer.eos_token_id
)
# Decode only the new tokens (not the input prompt)
new_tokens = outputs[0][inputs['input_ids'].shape[1]:]
return tokenizer.decode(new_tokens, skip_special_tokens=True)
# Compare base vs fine-tuned
test_prompt = 'Extract JSON from: "Alice Johnson, 28, software engineer in NYC since 2021"'
print('=== BASE MODEL ===')
print(generate(base_model, tokenizer, test_prompt))
print('\n=== FINE-TUNED MODEL ===')
print(generate(merged_model, tokenizer, test_prompt))สรุปไฮเปอร์พารามิเตอร์สำคัญของ LoRA
ในการปรับ LoRA ให้เหมาะกับกรณีใช้งานของคุณ ให้เริ่มจากค่าเริ่มต้นเหล่านี้และปรับทีละรายการ r=8 เป็นจุดเริ่มต้นที่ปลอดภัย เพิ่มเป็น 16 หรือ 32 หากโมเดลต้องการความสามารถในการปรับเปลี่ยนที่ละเอียดขึ้น lora_alpha = 2*r เป็นตัวเลือกที่มีเสถียรภาพ อัตราการเรียนรู้ 2e-4 ใช้ได้กับการปรับแต่งละเอียดตามคำสั่งส่วนใหญ่ ลดเป็น 1e-4 หากพบว่าค่าความสูญเสียจากการฝึกไม่เสถียร รอบการฝึก 1-3 รอบ: หยุดเมื่อค่าความสูญเสียจากการตรวจสอบความถูกต้องไม่ลดลงอีก
ควรเลือกใช้การปรับแต่งละเอียดของ OpenAI เมื่อใด
Hugging Face PEFT LoRA ต้องใช้ GPU หากคุณไม่มีโครงสร้างพื้นฐาน GPU API การปรับแต่งละเอียดของ OpenAI เป็นทางเลือกแบบมีผู้ดูแล ซึ่งจัดการโครงสร้างพื้นฐานการฝึกให้คุณ อัปโหลดไฟล์ JSONL เรียก API เพื่อเริ่มการฝึก และรับ ID โมเดลที่ปรับแต่งละเอียดแล้วสำหรับใช้ในการเรียก API การปรับแต่งละเอียดของ OpenAI รองรับ GPT-4o-mini และ GPT-3.5-turbo ค่าใช้จ่ายต่อโทเคนสูงกว่า แต่ไม่ต้องจัดการโครงสร้างพื้นฐานเองเลย
from openai import OpenAI
client = OpenAI()
# Upload training file
train_file = client.files.create(
file=open('train.jsonl', 'rb'),
purpose='fine-tune'
)
val_file = client.files.create(
file=open('validation.jsonl', 'rb'),
purpose='fine-tune'
)
# Create fine-tuning job
job = client.fine_tuning.jobs.create(
training_file=train_file.id,
validation_file=val_file.id,
model='gpt-4o-mini', # base model to fine-tune
hyperparameters={
'n_epochs': 3,
'batch_size': 'auto',
'learning_rate_multiplier': 'auto'
}
)
print(f'Fine-tuning job created: {job.id}')
# Monitor: client.fine_tuning.jobs.retrieve(job.id)
# Use: client.chat.completions.create(model=job.fine_tuned_model, ...)ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจเกี่ยวกับการปรับแต่งละเอียดด้วย LoRA จากบทเรียนนี้
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า LoRA ลดความต้องการ GPU สำหรับการปรับแต่งละเอียดลง 10-50 เท่า โดยฝึกเฉพาะเมทริกซ์ตัวปรับแรงก์ต่ำขนาดเล็กแทนการฝึกพารามิเตอร์ทั้งหมดของโมเดล QLoRA (ควอนไทซ์แบบ 4 บิต + LoRA) ทำให้การปรับแต่งละเอียดโมเดล 7B สามารถทำได้บน GPU สำหรับผู้ใช้ทั่วไปที่มี VRAM ประมาณ 12GB และ SFTTrainer จาก TRL มี API ระดับสูงที่จัดการการแบ่งโทเคน การจัดชุดข้อมูลย่อย การทำมาสก์ค่าความสูญเสีย และการบันทึกจุดตรวจสอบให้ ต่อไปเราจะประเมินและนำโมเดลที่ปรับแต่งละเอียดแล้วไปใช้งาน
คำถามที่พบบ่อย
บทเรียน “การปรับแต่งละเอียด LoRA ด้วย Hugging Face PEFT” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การปรับแต่งละเอียด LoRA ด้วย Hugging Face PEFT” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Engineering Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การปรับแต่งละเอียด LoRA ด้วย Hugging Face PEFT”
กำหนดค่าอันดับ LoRA ค่า alpha และโมดูลเป้าหมาย เรียกใช้การปรับแต่งละเอียดแบบมีผู้สอนด้วย TRL SFTTrainer ติดตามค่าความสูญเสียระหว่างฝึก และบันทึกจุดตรวจสอบทั้งแบบผสานแล้วและแบบมีเฉพาะอะแดปเตอร์ คุณปฏิบัติ AI Engineering Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Engineering Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Engineering Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การปรับแต่งละเอียด LoRA ด้วย Hugging Face PEFT” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน AI Engineering Academy นี้ได้ไหม
ได้ บทเรียน AI Engineering Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เมื่อการปรับแต่งละเอียดดีกว่าการเขียนพรอมต์
- การเตรียมชุดข้อมูลฝึกคุณภาพสูง
- การปรับแต่งละเอียด LoRA ด้วย Hugging Face PEFT
- การประเมินและนำโมเดลที่ปรับแต่งละเอียดไปใช้งาน