
이 글에서는 Bllossom의 LLaMA 기반 한국어 언어 모델을 한국어 금융 데이터셋으로 파인튜닝한 과정을 다룬다. 자원이 한정되어있기 때문에 최적화된 파인튜닝을 위해 LoRA와 4비트 양자화 기법을 사용하여 학습했다.
훈련에 사용한 모델은 Bllossom/llama-3.2-Korean-Bllossom-3B이고, 데이터셋은 BCCard-Finance-Kor-QnA를 사용했다.
👉https://huggingface.co/Bllossom/llama-3.2-Korean-Bllossom-3B
👉https://huggingface.co/datasets/BCCard/BCCard-Finance-Kor-QnA

import torch
from datasets import load_dataset
from transformers import (BitsAndBytesConfig,
AutoTokenizer,
AutoModelForCausalLM,
TrainingArguments)
from peft import (LoraConfig,
get_peft_model,
prepare_model_for_kbit_training)
from trl import SFTTrainer
base_model = 'Bllossom/llama-3.2-Korean-Bllossom-3B'
dataset = load_dataset("BCCard/BCCard-Finance-Kor-QnA", split='train')
tokenizer는 모델의 템플릿에 맞춰 데이터를 토크나이징한다(map 메서드를 사용)
def preprocess(data):
return data.map(lambda x: {'data': tokenizer.apply_chat_template([{"role": "system", "content": x['instruction']}, {"role": "assistant", "content": x['output']}], add_generation_prompt=False, tokenize=False, return_tensors="pt")}).map(lambda samples: tokenizer(samples["data"]), batched=True)
data = preprocess(dataset)
print(data[10])
모델과 호환되는 토크나이저를 로드하고 pad_token을 설정
tokenizer = AutoTokenizer.from_pretrained("Bllossom/llama-3.2-Korean-Bllossom-3B")
tokenizer.pad_token = tokenizer.eos_token
입력 데이터(instruction, output)를 모델 학습 템플릿에 맞춰 매핑한 후 토크나이징한다
<이 단계에서 일어나는 일>
👉apply_chat_template로 시스템 메시지와 답변을 템플릿에 맞게 정리
👉토크나이저를 통해 데이터를 정수 인덱스로 변환
def preprocess(data):
return data.map(
lambda x: {
'data': tokenizer.apply_chat_template(
[{"role": "system", "content": x['instruction']},
{"role": "assistant", "content": x['output']}],
add_generation_prompt=False, tokenize=False, return_tensors="pt"
)
}
).map(lambda samples: tokenizer(samples["data"]), batched=True)
train_data = preprocess(train)
test_data = preprocess(test)
Bits and Bytes를 사용해 모델을 4비트 양자화로 메모리 효율을 높인다
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4"
)
model = AutoModelForCausalLM.from_pretrained(
base_model,
quantization_config = bnb_config,
device_map = 'auto',
low_cpu_mem_usage=True
)
LoRA를 적용해 특정 레이어만 미세조정한다
lora_config = LoraConfig(
r=4,
lora_alpha=32,
target_modules=["q_proj", "o_proj", "k_proj", "v_proj", "gate_proj", "up_proj", "down_proj"],
lora_dropout=0.1,
bias="none"
)
model.gradient_checkpointing_enable()
model = prepare_model_for_kbit_training(model)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
training_args = TrainingArguments(
output_dir="./results",
overwrite_output_dir=True,
per_device_train_batch_size=4,
gradient_accumulation_steps=8,
num_train_epochs=3,
learning_rate=2e-5,
lr_scheduler_type="cosine",
warmup_steps=500,
weight_decay=0.01,
fp16=True,
logging_steps=50,
save_steps=500,
save_total_limit=2,
dataloader_num_workers=2,
)
SFTTrainer를 활용해 LoRA 기반 훈련을 간소화한다
trainer = SFTTrainer(
model=model,
train_dataset=data,
peft_config=lora_config,
dataset_text_field="data",
tokenizer=tokenizer,
args=training_args,
max_seq_length='NONE',
packing=False
)
model.config.use_cache = False
trainer.train()
LoRA와 4비트 양자화를 사용해 한국어 금융 데이터셋으로 한국어 모델을 효율적으로 파인튜닝 해봤다!
파인튜닝을 완료한 후 모델을 테스트해 본 결과, 답변 텍스트가 반복적으로 생성되며 최대 길이에 도달해야만 답변 생성이 멈추는 현상이 발생하였다(...) 이에 대한 원인을 찾기 위해 지속적으로 디버깅을 진행하였으며, 결국 적절한 길이에서 답변이 종료되도록 수정할 수 있었다. 디버깅 과정과 상세한 내용은 다음 글에서 다루도록 하겠다.
좋은 글 잘 봤습니다 선생님!
혹시 어떤 환경에서 구현하셨는지 여쭤도 될까요?