| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- JPA
- Spring
- 읽기쉬운코드
- 애자일기법
- 스프링
- cleancode
- AI
- 코딩테스트
- 그리디
- 엘라스틱서치
- kotlin
- database
- Elasticsearch
- spring boot
- 코드
- 자바
- 그리디알고리즘
- 알고리즘
- 코딩
- Baekjoon
- API
- 백준
- 클린코드
- 개발
- 프레임워크
- ES
- framework
- Java
- 데이터베이스
- 개발자
- Today
- Total
튼튼발자 개발 성장기🏋️
LLM 파인튜닝 실전: 금융 뉴스 분석기를 직접 만들어보자 본문
이론으로만 배운 파인튜닝을 실제로 적용해보자. 이 글에서는 금융 뉴스 분석기를 주제로, 합성 데이터 생성부터 멀티 GPU 학습, LoRA 병합, vLLM 서빙까지 전체 파이프라인을 단계별로 정리한다. 두 가지 학습 프레임워크(LLaMA-Factory와 Hugging Face TRL)를 모두 다루며, 각각의 특징과 사용법을 비교한다.
1. 프로젝트 개요: 금융 뉴스 분석기
이번 실습의 목표는 금융 뉴스를 입력받아 특정 종목에 영향을 주는지 자동으로 판별하는 LLM을 만드는 것이다.
모델이 해야 할 일
- 종목 연관 여부 판별: 뉴스가 특정 회사(종목)와 관련이 있는지 판단 (
is_stock_related) - 긍정 영향 종목 추출: 해당 뉴스로 긍정적 영향을 받을 종목 및 이유, 키워드
- 부정 영향 종목 추출: 부정적 영향을 받을 종목 및 이유, 키워드
- 뉴스 요약: 핵심 내용을 요약문으로 출력
모든 답변은 파이썬 Dictionary(JSON) 형식으로 구조화되어 출력된다.
출력 형식 예시
종목과 무관한 뉴스일 때:
"is_stock_related": false,
"summary": "뉴스 요약문"
}
종목 관련 뉴스일 때:
"is_stock_related": true,
"positive_impact_stocks": ["현대상선", "삼성전자"],
"reason_for_positive_impact": "정부의 수출 지원 확대가 물류 및 전자 기업에 긍정적 영향",
"positive_keywords": ["무역금융", "수출 지원", "반도체"],
"negative_impact_stocks": [],
"reason_for_negative_impact": "",
"negative_keywords": [],
"summary": "뉴스 요약문"
}
2. 학습 데이터 준비
데이터셋: dotori/finance_news_summarizer
허깅페이스 허브에 공개된 금융 뉴스 데이터셋을 사용한다. GPT-4o API로 생성된 합성 데이터가 포함되어 있으며, datasets 라이브러리로 손쉽게 불러올 수 있다.
from datasets import load_dataset dataset = load_dataset("dotori/finance_news_summarizer", split="train") print("전체 데이터 크기:", len(dataset))
데이터는 세 개의 필드로 구성된다:
| 필드 | 내용 |
|---|---|
system_prompt |
금융 뉴스 판별기 역할과 출력 형식을 정의하는 시스템 프롬프트 |
user_prompt |
분석 대상 뉴스 기사 전문 |
assistant |
Dictionary 형식의 정답 레이블 (종목 분류, 요약 등) |
데이터 분할 및 프롬프트 설계 핵심
"") 또는 빈 리스트([])로 작성하고, '없음' 같은 임의의 문자열을 넣어서는 안 된다.
LLaMA-Factory 데이터 형식
4개 필드가 필수:
instruction: 유저 프롬프트 (뉴스 본문)input: 추가 입력 (보통 빈값)output: 어시스턴트 답변 (반드시 문자열)system: 시스템 프롬프트
JSON 파일로 저장 후 data/dataset_info.json에 등록 필수
TRL / Hugging Face 데이터 형식
OpenAI messages 형식 사용:
[
{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}
]
Dataset.from_list()로 객체 변환 후 사용
3. 방법 1: LLaMA-Factory로 멀티 GPU 파인튜닝
LLaMA-Factory는 LLM 파인튜닝을 위한 오픈소스 프레임워크다. 설정 파일(JSON) 하나로 LoRA, DeepSpeed, 멀티 GPU 학습을 쉽게 구성할 수 있다는 것이 최대 장점이다.
설치
git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e ".[torch,metrics]" pip install deepspeed datasets vllm scikit-learn
학습 파라미터 설정
학습 구성을 Python dict로 작성하고 JSON 파일로 저장한다. 그 다음 llamafactory-cli train 명령으로 실행한다.
args = {
"stage": "sft", # 지도 학습 미세 조정
"do_train": True,
"model_name_or_path": "NCSOFT/Llama-VARCO-8B-Instruct",
"dataset": "finance_news_summarizer",
"template": "llama3",
"finetuning_type": "lora", # LoRA 어댑터 사용
"lora_target": "all", # 모든 선형 레이어에 LoRA 적용
"output_dir": "llama3_finance_lora",
"per_device_train_batch_size": 2,
"gradient_accumulation_steps": 4,
"lr_scheduler_type": "cosine",
"learning_rate": 1e-5,
"num_train_epochs": 3.0,
"bf16": True,
"deepspeed": "examples/deepspeed/ds_z3_config.json", # ZeRO-3
"ddp_find_unused_parameters": False,
}
DeepSpeed ZeRO-3란?
- 모델 파라미터, 그래디언트, 옵티마이저 상태를 여러 GPU에 분산 저장하는 기법
- 단일 GPU로는 올릴 수 없는 대형 모델도 멀티 GPU 환경에서 학습 가능하게 한다
- LLaMA-Factory에서는 설정 파일 경로만 지정하면 자동으로 적용된다
멀티 GPU 학습 실행
주피터 노트북에서 로그가 너무 길게 출력되는 문제를 막기 위해, subprocess로 실행하고 로그는 파일로 관리한다.
import subprocess, os
with open("train.log", "w") as log_file:
process = subprocess.Popen(
["llamafactory-cli", "train", "train_finance.json"],
env=dict(os.environ, CUDA_VISIBLE_DEVICES="0,1"), # GPU 2개 사용
stdout=log_file,
stderr=subprocess.STDOUT
)
print(f"학습 시작. PID: {process.pid}")
# 로그 실시간 확인
# !tail -n 50 train.log
실제 학습 결과 (A100 GPU 2개, 약 14분 28초)
- train_loss: 0.7034
- 학습 속도: 2.734 samples/sec
- 에포크: 2.95 (≈ 3 epoch)
4. 방법 2: Hugging Face TRL로 파인튜닝
TRL(Transformer Reinforcement Learning) 라이브러리는 SFTTrainer를 통해 허깅페이스 생태계와 완벽히 통합된 파인튜닝 환경을 제공한다.
LoRA 설정 (LoraConfig)
from peft import LoraConfig
peft_config = LoraConfig(
lora_alpha=32, # LoRA 스케일링 계수 (업데이트 크기 조절)
lora_dropout=0.1, # 과적합 방지 드롭아웃 (10%)
r=8, # 랭크: 저차원 공간 크기
bias="none", # 편향은 LoRA로 조정하지 않음
target_modules=["q_proj", "v_proj"], # Self-Attention Q, V에만 적용
task_type="CAUSAL_LM", # 시퀀스 생성 태스크
)
| 파라미터 | 값 | 의미 |
|---|---|---|
r (rank) |
8 | 작을수록 효율↑, 학습 능력↓. 일반적으로 4~64 사용 |
lora_alpha |
32 | α/r 비율로 업데이트 크기 결정. 보통 r의 2배 사용 |
lora_dropout |
0.1 | 학습 중 10% 뉴런 비활성화 → 과적합 방지 |
target_modules |
q_proj, v_proj | LoRA를 적용할 레이어. 더 많이 지정할수록 학습 파라미터 증가 |
SFTConfig 설정
from trl import SFTConfig
args = SFTConfig(
output_dir="llama3-8b-summarizer-ko",
num_train_epochs=3,
per_device_train_batch_size=2,
gradient_accumulation_steps=2, # 실질 배치 크기 = 2 × 2 = 4
gradient_checkpointing=True, # 메모리 절약 (속도는 소폭 감소)
optim="adamw_torch_fused",
bf16=True,
learning_rate=1e-4,
max_grad_norm=0.3, # 그래디언트 클리핑
warmup_ratio=0.03,
lr_scheduler_type="constant",
)
LLaMA 3 채팅 템플릿
학습 시 입력 데이터는 LLaMA 3의 채팅 템플릿 형식으로 변환된다. 모델에 역할(role)과 내용(content)을 구분하는 특수 토큰이 포함된다.
# LLaMA 3 채팅 템플릿 구조
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
시스템 프롬프트<|eot_id|><|start_header_id|>user<|end_header_id|>
유저 프롬프트<|eot_id|><|start_header_id|>assistant<|end_header_id|>
LLM의 답변<|eot_id|>
collate_fn: 배치 전처리의 핵심
자연어 처리에서 각 샘플의 길이가 다르기 때문에, 배치로 묶을 때 패딩(padding)이 필요하다. collate_fn은 이 전처리를 담당한다.
input_ids와 labels의 차이
- input_ids: 전체 대화 내용이 토큰화된 정수 시퀀스 (시스템 + 유저 + 어시스턴트 모두 포함)
- labels: 어시스턴트 응답 부분만 실제 정수값으로 유지하고, 나머지(시스템, 유저 부분)는 모두
-100으로 마스킹 -100은 손실 계산에서 자동으로 제외되어, 모델이 어시스턴트 답변 부분만 학습한다
attention_mask)로 구별하며, 모델이 패딩 부분을 무시하도록 한다.
5. 두 방법 비교: LLaMA-Factory vs TRL
| 항목 | LLaMA-Factory | Hugging Face TRL |
|---|---|---|
| 난이도 | 낮음 (설정 파일만 작성) | 중간 (파이썬 코드 직접 작성) |
| 멀티 GPU | DeepSpeed 내장 지원 | accelerate 별도 설정 필요 |
| 데이터 형식 | LlamaFactory 전용 JSON | OpenAI messages 형식 |
| 커스터마이징 | 제한적 (설정 파라미터 범위) | 자유로움 (collate_fn, 커스텀 루프) |
| 적합한 상황 | 빠른 실험, 멀티 GPU 환경 | 세밀한 제어, 연구 목적 |
| 사용 모델 | NCSOFT/Llama-VARCO-8B-Instruct | NCSOFT/Llama-VARCO-8B-Instruct |
6. LoRA 어댑터 병합 및 허깅페이스 업로드
학습이 완료된 LoRA 어댑터는 기본 모델과 병합(Merge)해야 독립적으로 사용할 수 있다. 병합 후에는 허깅페이스 허브에 업로드하여 어디서든 불러올 수 있게 한다.
1단계: 기본 모델 로드
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
base_model = AutoModelForCausalLM.from_pretrained(
"NCSOFT/Llama-VARCO-8B-Instruct",
torch_dtype=torch.float16,
device_map="auto"
)
2단계: LoRA 어댑터 로드 및 병합
from peft import PeftModel model = PeftModel.from_pretrained(base_model, "llama3_finance_lora") model = model.merge_and_unload() # 어댑터를 기본 모델에 통합
3단계: 병합 모델 저장
model.save_pretrained("llama3_finance_merged") tokenizer.save_pretrained("llama3_finance_merged")
4단계: 허깅페이스 허브 업로드
from huggingface_hub import HfApi api = HfApi() api.create_repo(token="hf_...", repo_id="username/model-name") api.upload_folder( token="hf_...", repo_id="username/model-name", folder_path="llama3_finance_merged" )
7. vLLM으로 파인튜닝 모델 서빙
병합된 모델을 허깅페이스에 업로드하면 어디서든 vLLM으로 불러와 빠르게 추론할 수 있다.
from vllm import LLM, SamplingParams llm = LLM(model="dotori/llama3-8b-finance-analyzer") sampling_params = SamplingParams( temperature=0, # 결정론적 출력 (동일 입력 → 동일 출력) max_tokens=1024, stop=["<|eot_id|>"] # LLaMA 3 종료 토큰 ) outputs = llm.generate(prompt, sampling_params) generated_text = outputs[0].outputs[0].text.strip()
추론 시 채팅 템플릿 적용
vLLM으로 추론할 때도 학습 때와 동일한 채팅 템플릿을 적용해야 한다. 어시스턴트 메시지 없이 프롬프트만 구성하고, add_generation_prompt=True로 생성 시작 토큰을 추가한다.
prompt = tokenizer.apply_chat_template(
messages[:-1], # assistant 제외
tokenize=False,
add_generation_prompt=True # <|start_header_id|>assistant<|end_header_id|> 자동 추가
)
8. 전체 파이프라인
합성 데이터 생성
GPT-4o API로 금융 뉴스 분석 레이블 생성. 시스템 프롬프트에 명확한 기준 명시.
데이터 전처리
학습/테스트 분할 (8:2). 프레임워크별 형식(LlamaFactory JSON / OpenAI messages)으로 변환.
파인튜닝
LLaMA-Factory + DeepSpeed ZeRO-3 (멀티 GPU) 또는 TRL SFTTrainer (단일 GPU). LoRA로 효율적 학습.
병합 및 업로드
LoRA 어댑터를 기본 모델에 merge_and_unload(). 허깅페이스 허브에 업로드.
vLLM 서빙
Paged Attention 기반 고성능 추론. temperature=0으로 일관된 JSON 출력 확보.
평가
테스트셋 20~30개 샘플로 생성 결과와 정답 레이블 비교. JSON 파싱 성공 여부도 함께 검증.
'AI > LLM 서비스 개발' 카테고리의 다른 글
| LoRA, QLoRA, SFT, DPO, vLLM, 멀티 LoRA 서빙 (0) | 2026.03.31 |
|---|---|
| AI 엔지니어의 문제 정의 능력과 데이터 전략 (0) | 2026.03.30 |
| 파인 튜닝 (1) | 2026.03.23 |
| 프롬프트 엔지니어링 (0) | 2026.03.23 |
| 언어 모델 (Language Model) (0) | 2026.03.17 |