
Qwen2.5-3B-Instruct LoRA 파인튜닝을 위한 Google Colab 가이드
요약
Google Colab 환경에서 Qwen2.5-3B-Instruct 모델을 LoRA 방식으로 파인튜닝하는 상세 가이드를 제공합니다. 하이퍼파라미터 설정부터 Google Drive를 활용한 체크포인트 저장까지 실무적인 워크플로우를 다룹니다.
핵심 포인트
- Google Colab의 T4 GPU를 활용한 비용 효율적인 학습 방법
- LoRA 어댑터 생성 및 독립형 모델 병합 과정 안내
- JSONL 형식을 사용한 데이터셋 준비 및 구성 방법
- 세션 종료에 대비한 Google Drive 연동 체크포인트 저장 전략
이 가이드는 무료 Google Colab 노트북만을 사용하여 30억 개의 파라미터를 가진 지시어 튜닝 언어 모델(instruction-tuned language model)인 Qwen2.5-3B-Instruct를 커스텀 데이터셋으로 파인튜닝(fine-tuning)하는 전체 과정을 안내합니다.
단순히 어떤 함수를 호출해야 하는지를 보여주는 것이 목표가 아닙니다. 모든 하이퍼파라미터(hyperparameter) 결정 사항을 설명하고, 모든 설계상의 트레이드오프(trade-off)를 정당화하며, 하드웨어 특화 구성 뒤에 숨겨진 논리를 명확히 밝힙니다. 이 과정을 마치면 여러분은 다음을 얻게 됩니다:
- 여러분의 데이터로 학습된 파인튜닝된 모델
- Google Drive에 영구적으로 저장된 경량 LoRA 어댑터(adapter) 가중치
- 어떤 추론 런타임(inference runtime)에도 배포할 준비가 된 완전히 병합된 독립형 모델
**
왜 Google Colab이 이 작업에 적합한 환경인가?
**
"왜 Colab인가"에 대한 솔직한 답변은 컴퓨팅 비용입니다. 로컬 CPU에서 언어 모델을 학습시키는 것은 현실적인 옵션이 아닙니다. GPU에서 1시간 미만이면 끝날 작업이 빠른 프로세서에서도 며칠 또는 몇 주가 걸릴 수 있습니다. AWS나 Azure에서 동일한 GPU 용량을 대여하는 비용은 시간당 $0.50에서 $2.00 사이입니다. Colab의 무료 티어는 16GB의 VRAM을 가진 서버급 GPU인 NVIDIA Tesla T4에 비용 부담 없이 접근할 수 있게 해줍니다.
하드웨어를 넘어, Colab은 모든 환경 설정 문제를 제거합니다. CUDA 설치도, 드라이버 관리도 필요 없으며, 로컬 머신과의 의존성 충돌(dependency conflicts) 위험도 없습니다. 런타임(runtime)은 미리 구성되어 있고 격리되어 있으며, 전적으로 브라우저 탭을 통해 접근할 수 있습니다.
미리 알아두어야 할 유일한 실제 제한 사항은 다음과 같습니다. Colab의 무료 티어는 세션 시간 제한을 적용하며, 장시간 유휴 상태(idle periods) 동안 연결이 끊어질 수 있습니다. 로컬 가상 머신에만 기록된 모든 데이터는 세션이 종료되면 영구적으로 손실됩니다. 이것이 바로 모델 체크포인트(checkpoints)를 Google Drive에 저장하는 것이 단순한 사후 고려 사항이 아니라, 단 두 줄의 Python 코드로 Colab과 네이티브하게 통합되는 워크플로우의 필수적인 부분인 정확한 이유입니다.
사전 요구 사항 {#prerequisites}
요구 사항 메모
학습 데이터셋: Colab 환경의 /content/에 업로드된 .jsonl 파일
Colab 런타임은 반드시 GPU를 사용하도록 설정되어야 합니다: 런타임(Runtime) → 런타임 유형 변경(Change runtime type) → T4 GPU
데이터셋 형식: 학습 데이터는 반드시 JSONL 파일이어야 합니다. 즉, 한 줄에 하나의 JSON 객체가 포함되어야 하며, 각 레코드는 role/content 쌍의 리스트로 구조화된 messages 키를 포함해야 합니다.
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What should I do?"}, {"role": "assistant", "content": "Here is what I'd suggest..."}]}
각 줄은 하나의 완전하고 독립적인 대화입니다. 2단계를 시작하기 전에 이 파일을 Colab 환경에 업로드하고 이름을 train.jsonl로 지정하세요.
Step 0 — 종속성 설치 (Installing Dependencies)
새 코드 셀을 생성하고 다음을 실행하세요. 이는 새로운 세션에서 항상 가장 먼저 실행되어야 하는 셀입니다.
!pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
!pip install --no-deps xformers trl peft accelerate transformers
만약 위의 명령이 어떤 이유로든 실패한다면, 표준 릴리스(standard release)를 대체제로 사용할 수 있습니다:
!pip install unsloth
각 패키지의 역할:
패키지 용도 (Package Purpose)
unsloth: 표준 Hugging Face 학습 방식에 비해 파인튜닝 (fine-tuning) 속도를 가속화하고 VRAM 사용량을 크게 줄여줍니다.
xformers: Meta Research에서 개발한 메모리 효율적인 어텐션 커널 (attention kernels)입니다.
trl: 지도 학습 파인튜닝 (supervised fine-tuning) 루프를 관리하는 SFTTrainer 클래스를 제공합니다.
peft: LoRA 어댑터 (adapter) 설정 및 파라미터 주입 (parameter injection)을 처리합니다.
accelerate: Hugging Face의 분산 학습 (distributed training) 백엔드입니다.
transformers: 모델, 토크나이저 (tokenizer), 생성 유틸리티를 로드하기 위한 핵심 라이브러리입니다.
다음 단계로 넘어가기 전, 설치가 완료될 때까지 2~4분 정도 기다려 주세요.
Step 1 — 베이스 모델 로드 및 LoRA 설정 (Loading the Base Model and Configuring LoRA)
새 코드 셀을 생성하세요. 이 단계에서는 메모리 최적화가 적용된 베이스 모델을 로드하고, 학습 가능한 LoRA 어댑터 레이어 (adapter layers)로 모델을 감쌉니다.
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
...
모델 로딩 (Loading the Model)
load_in_4bit = True — 이 파이프라인에서 메모리와 관련된 가장 중요한 결정입니다. 3B 파라미터 모델을 전체 16비트 정밀도 (16-bit precision)로 로드하면 약 67GB의 VRAM을 소비하며, 이는 단 한 번의 학습 단계가 실행되기도 전에 T4 GPU의 메모리를 고갈시킵니다. 4비트 양자화 (4-bit quantization)는 해당 가중치들을 약 1.52GB로 압축하여, 남은 메모리를 학습 활성화 값 (activations) 및 그래디언트 (gradients)를 위해 사용할 수 있게 합니다.
max_seq_length = 1024 — 학습 중 VRAM 소비량은 시퀀스 길이 (sequence length)에 따라 이차 함수적으로 증가합니다. 길이를 1024 토큰으로 제한하면 대화형 데이터를 크게 제한하지 않으면서도, 긴 학습 예시로 인한 메모리 급증 (memory spikes)을 방지할 수 있습니다.
dtype = None — 정밀도 감지 (precision detection)를 Unsloth에 위임하며, Unsloth는 하드웨어가 기본적으로 지원하는 방식에 따라 Float16 또는 Bfloat16을 선택합니다.
LoRA 설정 (Configuring LoRA)
3B 파라미터 모델을 파인튜닝 (fine-tuning)할 때의 핵심 과제는 30억 개의 가중치를 모두 업데이트하는 것이 계산적으로 매우 부담스러우며, 베이스 모델 (base model)의 일반적인 능력을 덮어쓸 위험이 있다는 점입니다. LoRA는 기존 가중치를 완전히 동결 (freezing)하고 특정 레이어 옆에 작은 학습 가능한 어댑터 행렬 (adapter matrices)을 주입함으로써 이 문제를 해결합니다. 학습 중에는 오직 어댑터만 업데이트됩니다.
r = 32 — 어댑터 행렬의 랭크 (rank)입니다. 랭크가 높을수록 어댑터의 학습 용량은 증가하지만 메모리 점유율 (memory footprint)도 함께 높아집니다. 32는 중간 규모의 데이터셋을 사용하여 단일 GPU로 파인튜닝할 때 잘 검증된 선택지입니다.
lora_alpha = 64 — 동결된 베이스 가중치와 결합되기 전, 어댑터의 업데이트에 적용되는 스케일링 인자 (scaling factor)입니다. 이를 2 × r로 설정하면 어댑터가 모델의 최종 동작에 측정 가능한 영향을 미칠 수 있도록 보장합니다.
use_rslora = True — Rank-Stabilized LoRA (RSLoRA)를 활성화합니다. 이는 스케일링 인자를 α / √r로 정규화합니다. 표준 LoRA는 랭크가 조정될 때 수치적으로 불안정해질 수 있지만, RSLoRA는 이러한 민감도를 제거하여 일관되게 더 안정적인 수렴 (convergence)을 생성합니다.
target_modules — 어댑터 주입(adapter injections)을 받을 가중치 행렬(weight matrices)을 지정합니다. 모든 주요 어텐션 투영(attention projections: q_proj, k_proj, v_proj, o_proj)과 피드포워드 레이어(feed-forward layers: gate_proj, up_proj, down_proj)를 대상으로 지정하면, 어텐션 레이어만을 대상으로 할 때보다 실질적으로 더 나은 적응(adaptation) 품질을 얻을 수 있습니다.
use_gradient_checkpointing = "unsloth" — 표준 순전파(forward pass) 과정에서는 역전파(backward pass) 시 사용하기 위해 모든 중간 활성화 텐서(intermediate activation tensor)를 VRAM에 유지합니다. 그래디언트 체크포인팅(Gradient checkpointing)은 이러한 텐서들을 버린 후, 역전파(backpropagation) 중에 필요할 때마다 이를 다시 계산합니다. 이는 약간의 연산 시간(compute time)을 대가로 상당한 VRAM 절약 효과를 얻는 방식입니다.
개념 증명 (Proof of Concept)
아래 스크린샷은 이 셀이 성공적으로 실행된 모습을 보여줍니다. Unsloth는 베이스 모델 파일을 다운로드하고, 하드웨어 환경(Tesla T4, 14.5GB VRAM)을 확인하며, 어떤 레이어들이 패치(patched)되었는지 보고합니다. poc_step1_model_load1

설정된 비율로 드롭아웃(dropout)이 적용되었고, Unsloth가 36개의 레이어를 패치했음을 확인하는 마지막 로그 라인에 주목하십시오. 이는 깨끗한 초기화(clean initialization) 시 기대되는 출력 결과입니다.
2단계 — 데이터셋 준비 및 채팅 템플릿 포맷팅 (Dataset Preparation and Chat Template Formatting)
새로운 코드 셀을 생성합니다. 이 단계에서는 원시 학습 파일(raw training file)을 읽어 Qwen 2.5가 학습 중에 요구하는 토큰 구조(token structure)로 변환합니다.
import json
from datasets import Dataset
...
데이터 읽기 (Reading the Data)
파일은 단일 JSON 배열로 로드되는 대신 한 줄씩 읽힙니다. 이는 의도적인 확장성(scalability) 선택입니다. 이 방식은 데이터셋의 크기가 얼마나 커지더라도 메모리 효율적(memory-efficient)인 상태를 유지합니다. 결과물인 Python 리스트는 이후 단계에서 사용되는 최적화된 .map() 및 .train_test_split() 연산을 제공하는 Hugging Face Dataset 객체로 변환됩니다.
채팅 템플릿 적용 (Applying the Chat Template)
지시어 튜닝 모델 (Instruction-tuned models)은 일반 텍스트로 학습하지 않습니다. 이 모델들은 사용자의 메시지가 어디서 끝나고 어시스턴트의 응답이 어디서 시작되는지를 구분하기 위해 입력값에 내장된 특수한 제어 토큰 (control tokens)에 의존합니다. Qwen 2.5는 이 목적으로 <|im_start|> 및 <|im_end|>와 같은 토큰을 사용합니다.
tokenizer.apply_chat_template()은 이러한 포맷팅을 자동으로, 그리고 정확하게 처리합니다. 이러한 토큰들을 수동으로 하드코딩하는 것은 오류가 발생하기 쉬우며, 학습 포맷과 모델의 사전 학습된 기대치 사이의 불일치 위험을 초래합니다.
tokenize=False — 토큰 ID로 변환하기 전에 대화 내용을 읽을 수 있는 문자열로 포맷팅합니다. 이는 토큰화 (tokenization) 과정을 동적 패딩 (dynamic padding)을 더 효율적으로 처리하는 학습 데이터 콜레이터 (training data collator)로 미루며, 디버깅 중에 데이터셋을 검사할 수 있는 상태로 유지해 줍니다.
add_generation_prompt=False — 학습 중에는 데이터셋에 프롬프트 (prompt)와 예상 응답이 모두 포함되어야 합니다. 이를 False로 설정하면 끝에 <|im_start|>assistant 토큰이 추가되는 것을 방지할 수 있습니다. 만약 이 토큰이 추가되면 모델은 전체 대화를 학습하는 대신 생성을 시작하도록 신호를 받게 됩니다.
remove_columns — 매핑 (mapping) 단계에서 원래의 원시 컬럼 (raw columns)을 제거하고 포맷팅된 텍스트 컬럼만 유지합니다. 이는 이후의 모든 셔플링 (shuffling) 및 캐싱 (caching) 작업에 대해 데이터셋의 메모리 점유율 (memory footprint)을 줄여줍니다.
학습/평가 분할 (The Train/Evaluation Split)
데이터의 일부를 평가용으로 남겨두는 것은 선택 사항이 아닙니다. 별도의 평가 세트 (eval set)가 없다면, 일반화 가능한 패턴을 학습하는 모델과 단순히 학습 데이터를 암기하는 모델을 구분할 방법이 없습니다. 과적합 (overfit)된 모델은 학습 손실 (training loss)은 감소하지만 검증 손실 (validation loss)은 증가하거나 정체되는 모습을 보이는데, 분할 없이는 이러한 차이를 관찰할 수 없습니다.
test_size=0.1 — 포맷팅된 데이터의 10%를 평가용으로 예약합니다. 모델의 가중치 (weights)는 이 슬라이스 (slice)를 사용하여 업데이트되지 않습니다.
seed=42 — 재현성 (reproducibility)을 위해 무작위 분할 (random split)을 고정합니다. 만약 학습이 중단된 후 조정된 하이퍼파라미터 (hyperparameters)로 재시작되더라도 분할 결과는 동일하게 유지되어, 실행 간에 평가 데이터가 학습 풀 (training pool)로 유출되는 것을 방지합니다.
Step 3 — 학습 루프 (Training Loop) 구성 및 실행
새로운 코드 셀을 생성합니다. 이곳은 모델이 학습하는 단계입니다. 아래의 모든 하이퍼파라미터는 Tesla T4 GPU의 특정 제약 사항을 고려하여 선택되었습니다.
from trl import SFTTrainer, SFTConfig
trainer = SFTTrainer(
...
혼합 정밀도 (Mixed-Precision) 구성
fp16 = True 및 bf16 = False 설정은 하드웨어 특화적인 선택입니다. Tesla T4는 Float16에 대한 네이티브 실리콘 지원을 갖추고 있지만, Bfloat16은 효율적으로 지원하지 않습니다. fp16을 사용하면 이 아키텍처에서 수치적 안정성 (numeric stability)을 유지하면서도 전체 float32 학습과 비교했을 때 메모리 사용량을 대략 절반으로 줄일 수 있습니다. T4에서 bf16 = True로 설정하면 학습 속도가 느려지고 그래디언트 불안정성 (gradient instability)이 발생할 수 있습니다.
그래디언트 누적 (Gradient Accumulation)을 통한 VRAM 관리
per_device_train_batch_size = 1은 매 순간 단일 학습 예시를 GPU에 로드합니다. 이는 실행 가능한 최소 배치 크기 (minimum viable batch size)이며, 런타임의 메모리 부족 (out of memory) 현상을 방지합니다.
배치 크기 1로 학습하는 것은 본질적으로 노이즈가 많습니다. 단일 예시에 기반한 가중치 업데이트 (weight updates)는 변동성이 매우 크고 불안정한 학습을 유발합니다. 그래디언트 누적 (gradient accumulation)이 이 문제를 해결합니다. gradient_accumulation_steps = 4를 설정하면, 모델은 4번의 연속적인 순전파 (forward passes)를 수행하고, 결과로 나온 그래디언트 (gradients)를 수학적으로 누적한 뒤, 4번 모두 완료된 후에 단 한 번의 가중치 업데이트를 적용합니다. 이를 통해 4개의 시퀀스를 동시에 VRAM에 유지할 필요 없이, 유효 배치 크기 (effective batch size)를 4 (1 × 4)로 만들 수 있습니다.
학습률 스케줄 (Learning Rate Schedule)
learning_rate = 2e-4 — LoRA 기반 파인튜닝 (fine-tuning)을 위한 표준적인 시작점입니다. 의미 있는 적응 (adaptation)을 이끌어낼 만큼 충분히 높으면서도, 베이스 모델 (base model)의 일반적인 지식을 덮어쓰지 않을 만큼 보수적입니다.
lr_scheduler_type = "cosine" — 코사인 스케줄러 (cosine scheduler)는 학습 과정 동안 코사인 곡선을 따라 2e-4에서 0을 향해 학습률 (learning rate)을 부드럽게 감소시킵니다. 이는 초기 에포크 (epochs)에서는 빠른 학습을 유도하고, 모델이 수렴 (convergence)에 가까워질수록 정밀하고 안정적인 미세 조정 (refinement)을 가능하게 합니다.
warmup_steps = 20 — LoRA 어댑터 행렬 (adapter matrices)은 무작위로 초기화되며, 학습 초기 단계의 큰 그래디언트 (gradient) 업데이트에 민감합니다. 처음 20단계 동안 학습률을 0에서 2e-4까지 점진적으로 높여줌으로써, 어댑터가 안정화되기 전의 초기 그래디언트 폭주 (gradient explosion)를 방지합니다.
weight_decay = 0.01 — L2 규제 (L2 regularization)는 과도하게 큰 어댑터 가중치 (weights)에 페널티를 부여하여, 모델이 학습 데이터에 존재하는 특정 문구나 패턴에 과도하게 인덱싱 (over-indexing)되는 것을 방지합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기