NeMo Microservices를 활용한 LLM 파인튜닝과 평가 실습
이번 가이드에서는 PAASUP DIP 환경에서 NVIDIA NeMo Microservices를 활용해 llama-3.2-3b-instruct 모델을 KoAlpaca 데이터로 LoRA 파인튜닝하고, 튜닝 전후 성능을 직접 비교해보는 실전 워크플로우를 다룹니다.

목차
- 들어가며
- DIP 환경 구축 가이드
- 데이터 준비
- NeMo 환경 설정과 네임스페이스 준비
- 데이터 업로드 및 Entity Store 등록
- LoRA 기반 파인튜닝 실행
- Custom 모델의 inference 모델 등록
- Inference 테스트 (Base vs Custom)
- 성능 평가 및 분석
- 결론
1. 들어가며
지난 1부에서는 NVIDIA NeMo 플랫폼의 핵심 구성 요소인 NeMo Framework와 NeMo Microservices의 역할과 차이를 소개했고, 2부에서는 NeMo Framework를 활용해 대규모 언어 모델(LLM)을 파인튜닝하고 그 성능을 평가하는 과정을 다뤘습니다.
이번 3부에서는 PAASUP DIP(Data Intelligence Platform) 환경에서 NVIDIA NeMo Microservices를 활용해 llama-3.2-3b-instruct
모델을 KoAlpaca 데이터로 LoRA 기반 파인튜닝하고, 튜닝 전후(Base vs Custom) 응답 품질 차이를 분석합니다.
- Base 모델: meta/llama-3.2-3b-instruct
- 학습 데이터셋: beomi/KoAlpaca-v1.1a
- 파인튜닝 기법: LoRA (Low-Rank Adaptation)
- 목적: 한국어 instruction tuning을 통한 성능 향상
본 프로젝트는 PAASUP DIP(Data Intelligence Platform) 환경에서 진행되며, 관리자가 사전에 구축한 NeMo Microservices와 Kubeflow 환경을 사용합니다.

2. DIP 환경 구축 가이드
PAASUP DIP에서는 카탈로그 생성만으로 NeMo Microservices와 Kubeflow 기반의 개발 환경을 쉽게 구성할 수 있습니다.
[1단계] 로그인 및 프로젝트 선택
- 목표: 사전 생성된 프로젝트와 매니저 계정으로 환경 접속
- 방법: 매니저 계정 로그인 → 프로젝트 리스트 확인 → 카탈로그 생성 메뉴 선택
[2단계] NeMo 카탈로그 생성
- 목표: 프로젝트 전용 NeMo microservices 네임스페이스(NS) 생성 및 NeMo 연동 준비
- 방법: 카탈로그 생성 메뉴 → nemo 선택 → 버전·이름 입력 → 생성 버튼 클릭
프로젝트명-카탈로그이름
형태의 프로젝트 전용 NS 생성- 예) 프로젝트명:
KoAlpaca
, 카탈로그이름:ft-ns
→ 네임스페이스:KoAlpaca-ft-ns
- 예) 프로젝트명:

[3단계] Kubeflow 카탈로그 생성
- 목표: Jupyter Notebook 실행을 위한 Kubeflow 환경 생성
- 방법: 카탈로그 생성 메뉴 → Kubeflow 선택 → 버전·이름 입력 → 생성 버튼 클릭

[4단계] 카탈로그 상태 확인
- 목표: 생성된 카탈로그의 배포 및 서비스 상태 확인
- 방법: 카탈로그 조회 화면에서 상태 모니터링

[5단계] Jupyter Notebook 환경 생성
- 목표: Python 코드 실행 환경 준비
- 방법: Kubeflow 서비스 링크 클릭 → 노트북 인스턴스 생성


[6단계] 개발 환경 접속
- 목표: 생성된 Notebook에 접속하여 코드 실행
- 방법: Connect 버튼 클릭 → JupyterLab 접속


※ 참고:"PAASUP DIP에서 NIM 카탈로그를 생성하면 Inference 서비스 배포도 가능""Fine-tuning에는 GPU 1개, NIM 배포에도 GPU 1개 필요""이번 실습은 튜닝 완료 후 NIM 배포를 진행하여 하나의 GPU를 사용"
튜닝에 사용할 데이터는 NeMo Microservices에서 요구하는 포맷에 맞춰 변환해야 합니다. 이번 실습에서는 beomi/KoAlpaca-v1.1a를 예시로 사용하며, 필요 시 다른 한국어 데이터셋도 변환해 적용할 수 있습니다.
3.1 NeMo Microservices 데이터 형식 이해
NeMo Microservices의 Chat 모델은 messages
포맷(JSONL)을 사용합니다.
각 항목은 대화의 **역할(role)**과 **내용(content)**을 포함해야 합니다.
포맷 예시:
{
"messages": [
{"role": "system", "content": "<시스템 메시지>"},
{"role": "user", "content": "<사용자 메시지>"},
{"role": "assistant", "content": "<어시스턴트 응답>"}
]
}
3.2 KoAlpaca 데이터 변환
KoAlpaca-v1.1a 원본 구조
{
"instruction": "한국의 수도는 어디인가요?",
"output": "한국의 수도는 서울입니다.",
"url": "..." // 학습에 불필요
}
변환 후 구조
{
"messages": [
{
"role": "user",
"content": "한국의 수도는 어디인가요?"
},
{
"role": "assistant",
"content": "한국의 수도는 서울입니다."
}
]
}
3.3 단계별 구현
Step 0: 환경 설정
config.py
에 서비스 URL, 토큰, 데이터셋 이름, 베이스 모델 정보를 지정합니다.NDS_URL
, NEMO_URL
, NIM_URL
은 배포된 NeMo 카탈로그 상세정보의 카탈로그 주소에서 확인할 수 있습니다.

# (Required) NeMo Microservices URLs
NDS_URL = "https://data-store-nemo.gke.paasup.io" # Data Store
NEMO_URL = "https://nemo-nemo.gke.paasup.io" # Customizer, Entity Store, Evaluator, Guardrails
NIM_URL = "https://nim-nemo.gke.paasup.io" # NIM
# (Required) Hugging Face Token
HF_TOKEN = "your_hf_token"
# (Optional) To observe training with WandB
WANDB_API_KEY = "" # WandB 로깅용
# (Optional) Modify if you've configured a NeMo Data Store token
NDS_TOKEN = "token"
# (Optional) Use a dedicated namespace and dataset name for tutorial assets
NMS_NAMESPACE = "KoAlpaca-ft-ns" # 프로젝트 전용 NS
DATASET_NAME = "KoAlpaca-v1.1a"
# (Optional) Configure the base model. Must be one supported by the NeMo Customizer deployment!
BASE_MODEL = "meta/llama-3.2-3b-instruct"
BASE_MODEL_VERSION = "v1.0.0+A100"
주의HF_TOKEN
은 Hugging Face Hub에서 발급받아야 합니다.
Step 1: 데이터 다운로드
필요 패키지 설치 후 Hugging Face에서 KoAlpaca 데이터셋을 로드합니다.
!pip install huggingface_hub datasets jinja2>=3.1.0 openai nemo-microservices
import os
import json
from pprint import pprint
import numpy as np
from datasets import load_dataset
from config import HF_TOKEN
# 환경변수 설정
os.environ["HF_TOKEN"] = HF_TOKEN
os.environ["HF_ENDPOINT"] = "https://huggingface.co"
# Download from Hugging Face
dataset = load_dataset("beomi/KoAlpaca-v1.1a")
pprint(example)
Step 2: 데이터 변환 및 전처리
KoAlpaca 데이터를 messages
포맷 JSONL로 변환합니다.
from typing import Iterable
def convert_to_messages(example):
"""KoAlpaca 예제를 NeMo 학습용 messages 포맷으로 변환"""
instruction = example.get("instruction", "").strip()
output_text = example.get("output", "").strip()
# 빈 데이터 필터링
if not instruction or not output_text:
return None
return {
"messages": [
{"role": "user", "content": instruction},
{"role": "assistant", "content": output_text}
]
}
def save_jsonl(data: Iterable[dict], path: str):
"""JSONL 형식으로 저장"""
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
for item in data:
if item is not None:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
print(f"✅ 저장 완료: {path}")
dataset = load_dataset("beomi/KoAlpaca-v1.1a", split="train")
split_dataset = dataset.train_test_split(test_size=0.05, seed=42)
train_set = split_dataset["train"]
val_set = split_dataset["test"]
save_jsonl(map(convert_to_messages, train_set), "./data/customization/training.jsonl")
save_jsonl(map(convert_to_messages, val_set), "./data/validation/validation.jsonl")
save_jsonl(map(convert_to_messages, val_set), "./data/evaluation/evaluation.jsonl")
3.4 다양한 한국어 데이터셋 변환 예시
nlpai-lab/kullm-v2
- 구조: instruction + input + output
- 변환 시 instruction과 input 결합 가능
dataset = load_dataset("nlpai-lab/kullm-v2")
def convert_to_messages(example):
instruction = example.get("instruction", "").strip()
input_text = example.get("input", "").strip()
output_text = example.get("output", "").strip()
if not output_text:
return None
prompt = instruction if not input_text else f"{instruction}\n\n{input_text}"
return {
"messages": [
{"role": "user", "content": prompt},
{"role": "assistant", "content": output_text}
]
}
junelee/sharegpt_deepl_ko
- 구조: instruction + input + output
- 변환 시 input이 있으면 instruction과 결합
dataset = load_dataset("junelee/sharegpt_deepl_ko", data_files="ko_alpaca_style_dataset.json", split="train")
def convert_to_messages(example):
instruction = example.get("instruction", "").strip()
input_text = example.get("input", "").strip()
output_text = example.get("output", "").strip()
if not output_text:
return None
prompt = instruction if not input_text else f"{instruction}\n\n{input_text}"
return {
"messages": [
{"role": "user", "content": prompt},
{"role": "assistant", "content": output_text}
]
}
파인튜닝을 시작하기 전에 Entity Store와 Data Store에서 사용할 네임스페이스가 올바르게 등록되어 있어야 합니다.
- Entity Store 네임스페이스는 NeMo 카탈로그 배포 시 자동으로 생성됩니다.
- Data Store 네임스페이스는 데이터셋 repository를 생성할 때 자동으로 등록됩니다.
따라서 사용자가 별도로 네임스페이스를 직접 생성할 필요는 없지만, SDK와 REST API를 이용해 두 네임스페이스가 정상적으로 준비되었는지 확인하는 과정이 필요합니다.
4.1 서비스 엔드포인트 확인
먼저 NeMo Microservices SDK 클라이언트를 초기화하고, Data Store / Entity Store / NIM 등 각 서비스의 엔드포인트가 올바르게 연결되어 있는지 확인합니다.
import os
import json
import random
import requests
from openai import OpenAI
from nemo_microservices import NeMoMicroservices
from config import *
# Initialize NeMo Microservices SDK client
nemo_client = NeMoMicroservices(
base_url=NEMO_URL,
inference_base_url=NIM_URL,
)
print(f"Data Store endpoint: {NDS_URL}")
print(f"Entity Store, Customizer, Evaluator endpoint: {NEMO_URL}")
print(f"NIM endpoint: {NIM_URL}")
print(f"Namespace: {NMS_NAMESPACE}")
print(f"Base Model for Customization: {BASE_MODEL}@{BASE_MODEL_VERSION}")
4.2 데이터 경로 구성
앞서 준비한 학습 데이터의 경로를 설정하고, 파일이 존재하는지 검증합니다.
# Path where data preparation notebook saved finetuning and evaluation data
DATA_ROOT = os.path.join(os.getcwd(), "data")
CUSTOMIZATION_DATA_ROOT = os.path.join(DATA_ROOT, "customization")
VALIDATION_DATA_ROOT = os.path.join(DATA_ROOT, "validation")
EVALUATION_DATA_ROOT = os.path.join(DATA_ROOT, "evaluation")
# Sanity checks
train_fp = f"{CUSTOMIZATION_DATA_ROOT}/training.jsonl"
assert os.path.exists(train_fp), f"The training data at '{train_fp}' does not exist. Please ensure that the data was prepared successfully."
val_fp = f"{VALIDATION_DATA_ROOT}/validation.jsonl"
assert os.path.exists(val_fp), f"The validation data at '{val_fp}' does not exist. Please ensure that the data was prepared successfully."
test_fp = f"{EVALUATION_DATA_ROOT}/evaluation.jsonl"
assert os.path.exists(test_fp), f"The test data at '{test_fp}' does not exist. Please ensure that the data was prepared successfully."
4.3 Data Store Repository 생성 및 네임스페이스 확인
먼저 NeMo Data Store에 데이터셋 repository를 생성하고, 이후 네임스페이스가 올바르게 등록되었는지 검증합니다.
NeMo Data Store는 Hugging Face Hub API(HfApi
)와 호환되는 방식으로 데이터 관리를 지원합니다.
참고: 이 과정은 Hugging Face 서버와 직접 통신하지 않으며, HfApi
클라이언트를 통해 NeMo Data Store에만 접근합니다.
from huggingface_hub import HfApi
repo_id = f"{NMS_NAMESPACE}/{DATASET_NAME}"
hf_api = HfApi(endpoint=f"{NDS_URL}/v1/hf", token="")
# Create repo
hf_api.create_repo(
repo_id=repo_id,
repo_type='dataset',
)
Data Store와 Entity Store 모두 네임스페이스가 정상적으로 등록·조회되는지 확인합니다.
# Verify Namespace in Data Store (using requests as SDK doesn't cover Data Store)
response = requests.get(f"{NDS_URL}/v1/datastore/namespaces/{NMS_NAMESPACE}")
print(f"Data Store - Status Code: {response.status_code}\nResponse JSON: {response.json()}")
# Verify Namespace in Entity Store
namespace_obj = nemo_client.namespaces.retrieve(namespace_id=NMS_NAMESPACE)
print(f"\nEntity Store - Namespace: {namespace_obj.id}")
print(f"Created at: {namespace_obj.created_at}")
print(f"Description: {namespace_obj.description}")
print(f"Project: {namespace_obj.project}")

※ 참고네임스페이스 목록 조회:
Data Store →requests.get(f"{NDS_URL}/v1/datastore/namespaces/")
Entity Store →nemo_client.namespaces.list()
네임스페이스 삭제:
Data Store →requests.delete(f"{NDS_URL}/v1/datastore/namespaces/{namespace}")
Entity Store →nemo_client.namespaces.delete(namespace)
준비한 데이터셋을 NeMo Data Store에 업로드한 뒤, Entity Store에 등록해 파인튜닝 작업에서 참조할 수 있도록 합니다.
5.1 Data Store에 데이터 업로드
- 데이터 파일 업로드 – 학습·검증·평가 데이터 각각 업로드
training/
폴더 내 모든 JSONL → 학습 데이터validation/
폴더 내 모든 JSONL → 검증 데이터
hf_api.upload_file(path_or_fileobj=train_fp,
path_in_repo="training/training.jsonl",
repo_id=repo_id,
repo_type='dataset',
)
hf_api.upload_file(path_or_fileobj=val_fp,
path_in_repo="validation/validation.jsonl",
repo_id=repo_id,
repo_type='dataset',
)
hf_api.upload_file(path_or_fileobj=test_fp,
path_in_repo="testing/evaluation.jsonl",
repo_id=repo_id,
repo_type='dataset',
)
print("All data files uploaded successfully!")
- 업로드 확인 – 저장소 내 파일 트리 조회
import requests
url = f"{NDS_URL}/v1/hf/api/datasets/{repo_id}/tree/main"
headers = {"Authorization": f"Bearer {''}"} # token이 있다면 입력
response = requests.get(url, headers=headers)
if response.status_code == 200:
tree = response.json()
for entry in tree:
print(entry)
else:
print("요청 실패:", response.status_code, response.text)

5.2 Entity Store에 데이터셋 등록
Data Store에 업로드된 데이터셋을 Entity Store에 등록하면, 네임스페이스와 데이터셋 이름으로 참조할 수 있습니다.
- 데이터셋 생성
# Create dataset
dataset = nemo_client.datasets.create(
name=DATASET_NAME,
namespace=NMS_NAMESPACE,
description="Evaluation set for KoAlpaca fine-tuned LLaMA3",
files_url=f"hf://datasets/{NMS_NAMESPACE}/{DATASET_NAME}",
project="korean-finetune",
)
print(f"Created dataset: {dataset.namespace}/{dataset.name}")
dataset
- 등록 확인
# Verify dataset registration
dataset_obj = nemo_client.datasets.retrieve(namespace=NMS_NAMESPACE, dataset_name=DATASET_NAME)
print("Files URL:", dataset_obj.files_url)
assert dataset_obj.files_url == f"hf://datasets/{repo_id}"


KoAlpaca 데이터셋을 이용해 Supervised Fine-Tuning(SFT) + LoRA 방식으로 학습을 진행합니다.
이 과정에서 하이퍼파라미터를 설정하고 학습 Job을 생성·모니터링하며, 완료된 Custom 모델을 확인합니다.
6.1 학습 Job 생성
먼저 학습 매개변수를 설정하고 커스터마이징 Job을 생성합니다.
만약 Weights & Biases(WandB) 로깅을 사용한다면, API 키를 헤더에 포함해 전송합니다.
# Create customization job
if WANDB_API_KEY:
client_with_wandb = nemo_client.with_options(default_headers={"wandb-api-key": WANDB_API_KEY})
else:
client_with_wandb = nemo_client
customization = client_with_wandb.customization.jobs.create(
name="llama-3.2-3b-koalpaca-ft",
output_model=f"{NMS_NAMESPACE}/llama-3.2-3b-koalpaca-run",
config=BASE_MODEL,
#config=f"{BASE_MODEL}@{BASE_MODEL_VERSION}",
dataset={"name": DATASET_NAME, "namespace": NMS_NAMESPACE},
hyperparameters={
"training_type": "sft", # Supervised Fine-Tuning
"finetuning_type": "lora", # Low-Rank Adaptation
"epochs": 3,
"batch_size": 16,
"learning_rate": 5e-5,
"lora": {
"adapter_dim": 32, # LoRA rank
"adapter_dropout": 0.05 # LoRA dropout rate
}
}
)
print(f"Created customization job: {customization.id}")
customization

※ 프로덕션 환경 권장사항
표준화된 설정을 재사용하려면, 사전에 Customization targets과 Customization configs를 등록해두고 설정 이름만 참조하는 방식이 좋습니다.
예:config = "my-custom-config"
Job ID와 사용자 정의 모델 이름을 변수로 저장합니다.
# To track status
JOB_ID = customization.id
customization = nemo_client.customization.jobs.retrieve(JOB_ID)
# This will be the name of the model that will be used to send inference queries to
CUSTOMIZED_MODEL = customization.output_model
※ 작업 취소
잘못 예약된 Job은nemo_client.customization.jobs.cancel(job_id=JOB_ID)
로 취소할 수 있습니다.
6.2 학습 상태 모니터링
Job의 진행 상태를 조회합니다.
job_status = nemo_client.customization.jobs.status(job_id=JOB_ID)
print("Percentage done:", job_status.percentage_done)
print("Job Status:", json.dumps(job_status.model_dump(), indent=2, default=str))
percentage_done: 100.0
"status": "completed"
이 두 값을 확인하면 학습이 완료된 것입니다.
학습이 진행되면 training loss와 validation loss가 단계별로 출력됩니다.
Percentage done: 100.0
Job Status: {
"created_at": "2025-08-08 02:25:19.783115",
"status": "completed",
"updated_at": "2025-08-08 06:56:31.684977",
"best_epoch": 2,
"elapsed_time": null,
"epochs_completed": 3,
"metrics": {
"keys": [
"train_loss",
"val_loss"
],
"metrics": {
"train_loss": [
{
"step": 9,
"timestamp": "2025-08-08T02:37:22.090967",
"value": 2.6579394340515137
},
{
"step": 19,
"timestamp": "2025-08-08T02:38:02.087267",
"value": 2.5957703590393066
},
{
"step": 29,
"timestamp": "2025-08-08T02:38:41.759720",
"value": 2.602855682373047
},
...
6.3 Custom 모델 확인
학습이 완료되면 Entity Store에서 생성된 모델을 확인합니다.
모델 목록 조회
models_page = nemo_client.models.list(
filter={"namespace": NMS_NAMESPACE},
sort="-created_at"
)
# Print models information
print(f"Found {len(models_page.data)} models in namespace {NMS_NAMESPACE}:")
for model in models_page.data:
print(f"\nModel: {model.name}")
print(f" Namespace: {model.namespace}")
print(f" Base Model: {model.base_model}")
print(f" Created: {model.created_at}")
if model.peft:
print(f" Fine-tuning Type: {model.peft.finetuning_type}")

특정 모델의 상세 조회
model = nemo_client.models.retrieve(namespace=NMS_NAMESPACE, model_name=CUSTOMIZED_MODEL.split("/")[1])
print(f"Model: {model.namespace}/{model.name}")
print(f"Base Model: {model.base_model}")
print(f"Status: {model.artifact.status}")
LoRA 파인튜닝이 완료된 Custom 모델은, 해당 모델의 Base 모델(예: meta/llama-3.2-3b-instruct
)이 이미 NIM 서비스로 배포되어 있다면 자동으로 등록됩니다.
만약 Base 모델의 NIM 서비스가 없는 환경이라면, Custom 모델이 추론(Inference)에 사용될 수 있도록 NIM 서비스를 직접 배포해야 합니다.
7.1 Base 모델 NIM 배포
Base 모델을 NIM 서비스로 배포하려면 NeMo Deployment API를 호출합니다. 아래 예시는 llama-3.2-3b-instruct
모델을 배포하는 코드입니다.
url = f"{NEMO_URL}/v1/deployment/model-deployments"
payload = {
"name": "llama-3.2-3b-instruct",
"namespace": "meta",
"config": {
"model": "meta/llama-3.2-3b-instruct",
"nim_deployment": {
"image_name": "nvcr.io/nim/meta/llama-3.2-3b-instruct",
"image_tag": "1.10.1",
"pvc_size": "25Gi",
"gpu": 1,
"additional_envs": {
"NIM_GUIDED_DECODING_BACKEND": "outlines",
"NGC_API_KEY": "YOUR_NGC_API_KEY", # NGC API 키 입력
},
"tolerations": [
{
"key": "nvidia.com/gpu",
"operator": "Equal",
"value": "present",
"effect": "NoSchedule"
}
]
}
}
}
headers = {
"accept": "application/json",
"Content-Type": "application/json",
}
resp = requests.post(url, headers=headers, json=payload, verify=False)
resp.raise_for_status()
print("Create deployment response:")
print(json.dumps(resp.json(), indent=2))
주의NGC_API_KEY
는 NVIDIA NGC에서 발급받아야 합니다.
7.2 배포 확인
NIM 배포가 완료되면 NIM 엔드포인트를 통해 모델 목록을 확인할 수 있습니다. 목록에 Custom 모델 이름이 표시되면, Inference 준비가 완료된 것입니다.
# Check if the custom LoRA model is hosted by NVIDIA NIM
models = nemo_client.inference.models.list()
model_names = [model.id for model in models.data]
print(model_names)

참고Base 모델 NIM이 이미 배포된 경우, Custom 모델은 별도 배포 과정 없이 즉시 Inference 가능
모델의 NIM 엔드포인트에 프롬프트를 전송하여 Base 모델과 과 파인튜닝된 Custom 모델의 추론 성능을 비교합니다.
8.1 Inference 클라이언트 설정
추론을 위해 사용할 Base 모델과 학습된 Custom 모델을 지정합니다.
BASE_MODEL = "meta/llama-3.2-3b-instruct"
print(f"Base Model: {BASE_MODEL}")
print(f"Custom Model: {CUSTOMIZED_MODEL}")
방법 1 — OpenAI 호환 API 사용
inference_client = OpenAI(
base_url = f"{NIM_URL}/v1",
api_key = "None"
)
messages = [{'role': 'user', 'content': 'Your comments.'}]
completion = inference_client.chat.completions.create(
model = CUSTOMIZED_MODEL,
messages = messages,
temperature = 0.3,
top_p = 0.8,
max_tokens = 256,
frequency_penalty=0.9,
presence_penalty=0.3,
stream = False
)
completion.choices[0].message.content
방법 2 — nemo_microservices
라이브러리 사용
completion = nemo_client.chat.completions.create(
model = BASE_MODEL,
messages = messages,
temperature = 0.3,
top_p = 0.8,
max_tokens = 256,
frequency_penalty=0.9,
presence_penalty=0.3,
stream = False
)
print(completion.choices[0].message.content)
8.2 예시 프롬프트
다음 5가지 예시 프롬프트를 사용하여 Base 모델과 Custom 모델의 응답을 비교 분석했습니다.
# 예시 1: 간단한 정보 요구
messages = [{'role': 'user', 'content': '사과의 효능 두 가지만 말해줘'}]
# 예시 2: 개념 설명 요구
messages = [{'role': 'user', 'content': '크리스마스에 대해 설명해줘.'}]
# 예시 3: 지역 정보 요구
messages = [{'role': 'user', 'content': '인천은 무슨 도시인가요?'}]
# 예시 4: 문화 설명 요구
messages = [{'role': 'user', 'content': '한국의 아이돌 문화에 대해 알려주세요.'}]
# 예시 5: 구조화된 데이터(JSON) 형식 요구
messages=[
{
"role": "user",
"content": """요구 스키마:
{
"title": "string",
"pros": ["string", "string"],
"cons": ["string", "string"]
}
스마트폰 대비 태블릿의 장단점을 pros/cons 배열에 각각 2개씩 채워 JSON으로 작성하세요."""
}
]
9.1 추론 결과 분석
각 프롬프트에 대한 Base 모델과 Custom 모델의 응답을 비교하여 개선점을 분석했습니다.
예시 1: 사과의 효능 두 가지만 말해줘
- Base 모델 응답:
사과는 다양한 건강에益을 주는 효능이 있습니다. 두 가지 주요 효능은 다음과 같습니다.
1. 항산화 효과: 사과에는 항산화 성분인 폴리페놀이 풍부하게 함유되어 있습니다. 이 성분은 세포의 손상을 방지하고, 세포의 산화를 막아주어 노화와 질병의 원인이 되는 산화물질을 제거합니다.
2. 혈압 조절: 사과는 혈압을 낮추고 혈관을 확장시켜 혈액순환을 촉진시키는 효과가 있습니다. 이로 인해 심장병, 고혈압 등의 질병에 효과적입니다.
또한, 사과에는 다양한 영양소가 풍부하게 함유되어 있어 건강에 좋은 영향을 미칠 수 있습니다.
오류/문제:한자(益) 혼입설명이 간단하고 성분·효과가 구체적이지 않음
- Custom 모델 응답:
사과는 다양한 건강에 좋은 효능을 가지고 있습니다.
1. 항산화 효과: 사과에는 항산화 성분인 비타민 C가 풍부하게 함유되어 있어 체내에서 산소를 방지하여 체내 산소를 방지하는 역할을 합니다. 또한, 사과에는 항산화 성분인 비타민 E가 많이 함유되어 있어 체내에서 산소를 방지하여 체내 산소를 방지하는 역할을 합니다.
2. 혈압 조절: 사과는 혈압을 조절하는 데에도 효과적입니다. 사과에는 비타민 C와 비타민 E가 많이 함유되어 있어 혈관을 확장시켜 혈압을 낮추는 역할을 합니다. 또한, 사과에는 항산화 성분이 많아 체내에서 산소를 방지하여 혈압을 낮추는 역할을 합니다.
위의 두 가지 효능 외에도, 사과는 신장 건강에 좋고, 피부 건강에도 좋은 효과를 가지고 있습니다.
→ 개선점: 불필요한 한자 제거, 성분과 효과를 더 구체화, 응답 확장
예시 2: 크리스마스에 대해 설명해줘
- Base 모델 응답:
크리스마스는 기독교의 기념일로, 기독교의 성인 예수 그리스도와 그의 어머니 마리아의 생일을 기념하는 날입니다. 크리스마스는 25일과 26일을 기념하는 날로, 25일은 예수 그리스도의 태어날을 기념하고, 26일은 그의 죽음을 기념합니다.
크리스마스는 크리스마스 트리, 크리스마스 선물, 크리스마스 음악, 크리스마스 식사 등 다양한 전통과 관습으로 알려져 있습니다. 크리스마스 트리는 주로 evergreen 나무를 사용하여 만든 트리이며, 그 위에 장식품과ไฟ를 장식합니다. 크리스마스 선물은 가족과 친구에게 선물하는 물건으로, 종종 예술品이나 전시品을 포함합니다.
크리스마스의 전통은 유럽에서 시작되었으며, 현재 세계적으로 많은 국가에서 celebrations를 합니다. 크리스마스는 가족과 친구와 함께 시간을 보내는 날로, 많은 사람들은 크리스마스 트리 подneath에 선물을 받고, 크리스마스 음악을 듣
오류/문제:사실 오류: 26일은 죽음을 기념하는 날이 아님외국어 혼입(evergreen
)서술 흐름 불명확
- Custom 모델 응답:
크리스마스는 기독교의 성경에서 언급된 예수 그리스도와 그의 어머니 마리아의 탄생을 기념하는 기념일입니다. 크리스마스는 기독교의 주일 중 하나로, 예수 그리스도의 탄생을 기념하는 날입니다. 크리스마스 전날인 '크리스마스 전야'는 예수 그리스도의 탄생을 기념하는 날이며, 크리스마스날에는 가족과 친구들과 함께 즐기며, 선물 주고 받는 등 다양한 행사들이 이루어집니다. 크리스마스는 세계적으로 많은 나라에서 기념되며, 크리스마스 트리와 크리스마스 카트, 크리스마스 선물 등 다양한 문화적 요소가 있습니다.
→ 개선점: 날짜 오류 수정, 불필요한 외국어 제거, 흐름 개선 날짜 오류 수정
예시 3: 인천은 무슨 도시인가요?
- Base 모델 응답:
인천은 한국의 도시 중 하나로, 인천광역시로 구성되어 있습니다. 인천은 한국의 가장 큰 해상도시 중 하나로, 동아시아에서 가장 큰 해상 무역 센터 중 하나입니다. 인천은 한국의 수도인 서울과 함께 수도권 지역의 중심지로, 많은 산업과 경제 활동을 수행합니다.
인천은 해상 무역과 항구 활동이 중요한 역할을 하고 있으며, 세계의 많은 국가와의 무역을 facilitated합니다. 또한, 인천은 한국의 문화와 예술을 전파하는 중요한 역할을 하고 있으며, 많은 예술가와 문화인들이 거주하고 있습니다.
인천은 또한 한국의 교육 및 연구 활동에 중요한 역할을 하고 있으며, 많은 대학과 연구소가 있습니다. 인천은 또한 한국의 tourism industry에 중요한 역할을 하고 있으며, 많은 관광객들이 인천을 방문합니다.
总之, 인천은 한국의 중요한 도시로, 경제, 문화, 교육 및 tourism industry에 중요한 역할을 하고 있습니다.
오류/문제:영문 혼입(facilitated
), 중국어 혼입(总之
)핵심 정보 없이 산만한 나열
- Custom 모델 응답:
인천은 한국의 제1도시 중 하나입니다. 인천은 한국의 수도인 서울과는 다른 지역으로, 서울에서 남쪽으로 약 50km 떨어져 있습니다. 인천은 해안가에 위치해 있으며, 해양과학, 항공, 해상운송 등 다양한 산업이 발달하고 있습니다. 인천은 세계적인 항구로 자리 잡고 있으며, 세계적인 항공사들이 인천공항을 이용합니다. 또한, 인천은 문화와 관광이 풍부한 도시로, 인도네시아, 태국, 베트남 등 동남아시아 국가들과의 교류가 많습니다.
→ 개선점: 외국어 제거, 산업·공항·관광 3축으로 정보 구조화
예시 4: 한국의 아이돌 문화에 대해 알려주세요
- Base 모델 응답:
한국의 아이돌 문화는 한국에서 가장 인기를 끌고 있는 문화 중 하나입니다. 아이돌은 한국에서 "아이다"라고도 불리며, 한국의 음악, 댄스, 연기 등 다양한 분야에서 활동하는 예술가입니다.
한국의 아이돌 문화는 1990년대에 시작되었으며, 이 시기에는 K-POP이 처음으로 발전하기 시작했습니다. K-POP은 한국의 음악을 세계로 전파하는 데 중요한 역할을했습니다. 현재, 한국의 아이돌은 세계적으로 많은 인기를 끌고 있습니다.
한국의 아이돌은 일반적으로 다음 세 가지 분야에 분할됩니다.
1. **음악**: 음악은 한국의 아이돌 문화에서 가장 중요한 분야입니다. K-POP은 한국의 음악을 세계로 전파하는 데 중요한 역할을했습니다. K-POP 그룹은 일반적으로 4-6 명의 멤버로 구성되어 있으며, 각 멤버가 다른ดนตร리와 보컬을 연주합니다.
2. **연기**: 연기는 한국의 아이돌 문화에서 두 번째로 중요한 분야입니다. 한국의 아이돌은 일반적으로 영화와 텔레비전 드라마
오류/문제:잘못된 용어(아이다
)태국어 혼입(ดนตร리
)구체성 부족
- Custom 모델 응답:
한국의 아이돌 문화는 1990년대 초반부터 시작되었습니다. 이 시기에는 한국에서 처음으로 아이돌이 등장하였으며, 이후 미국에서 유명한 K-POP이 발전하게 되었습니다. 한국의 아이돌은 주로 여성 아이돌 그룹으로 구성되어 있으며, 대표적인 그룹으로는 S.E.S, Shinhwa, H.O.T, TVXQ, Girls' Generation 등이 있습니다. 이들 그룹들은 한국에서 큰 인기를 얻었으며, 이후 세계적으로도 유명해졌습니다.
한국의 아이돌 문화는 현재까지도 매우 활발하게 진행되고 있습니다. 최근에는 K-POP이 세계적으로 유명해졌으며, 한국의 아이돌은 세계적으로 많은 인기를 얻고 있습니다. 또한, 한국의 아이돌은 다양한 장르를 포함하며, 음악뿐만 아니라 댄스, 뮤지컬 등 다양한 분야에서도 활동하고 있습니다.
한국의 아이돌 문화는 다양한 요인들이 작용하여 발전하게 되었습니다. 예를 들어, 1990년대 초반에는 한국에서 처음으로 아이돌이 등장하였으며, 이후 미국에서 유명한 K-POP이 발
→ 개선점: 용어 수정, 외국어 제거, 시간 순 정리, 대표 그룹 예시 포함
예시 5: 구조화된 응답 (JSON 형식)
- Base 모델 응답:
{"title": "스마트폰 대비 태블릿", "pros": ["대용량 스토리지", "장기 사용 가능"], "cons": ["작은 màn화", "가볍지 않음"]}
오류/문제:베트남어 혼입(màn
)단순 key-value, 설명 부족
- Custom 모델 응답:
스마트폰 대비 태블릿의 장단점을 pros/cons 배열에 각각 2개씩 채워 JSON으로 작성한 결과는 다음과 같습니다.
\```json
{
"title": "스마트폰 대비 태블릿",
"pros": [
{
"point": "대형 화면",
"description": "대형 화면으로 영화나 게임을 즐길 수 있습니다."
},
{
"point": "장기 사용 가능",
"description": "장기 사용 가능하여 휴대용이 아닌 집에서 사용할 수 있습니다."
}
],
"cons": [
{
"point": "무게와 크기",
"description": "무게와 크기가 큰 단점이 있습니다."
},
{
"point": "사용자 수용성",
"description": "사용자 수용성이 낮아 휴대가 어렵습니다."
}
]
}
\```
위 결과는 스마트폰 대비 태블릿의 장단점을 JSON 형태로 표현한 것입니다. 단, 실제로 사용자 수용성이 낮은 점은 스마트폰 대비 태블릿이 일반적으로 사용자가
→ 개선점: 구조화 수준 향상, 설명 필드 추가, 지시문 의도 반영
9.2 예시별 개선 포인트 요약
예시 | Base 모델 | Custom 모델 | 개선 포인트 |
---|---|---|---|
#1 | 외래어(益) 혼합, 간단 설명 | 순수 한국어, 성분·효과 구체화 | 표현 일관성, 정보량 증가 |
#2 | 날짜 오류(26일 기념일), 외국어 혼입 | 정확한 날짜, 자연스러운 흐름 | 사실 정확도 ↑, 서술 개선 |
#3 | 영문·중국어 혼입, 산만한 정보 나열 | 산업·공항·관광 3축 정리 | 불필요 정보 제거, 한국어 일관성 |
#4 | 시기·용어 부정확, 일반론 | 시간 순·그룹 예시 포함 | 맥락 강화, 구체성 향상 |
#5 | 단순 key-value | point/description 포함 | 지시문 이행도↑, 가독성↑ |
9.3 전반적인 변화 및 종합 평가
파인튜닝 전후 응답 비교를 통해 다음과 같은 공통적인 개선점을 확인했습니다.
- 정확성 향상: 잘못된 날짜, 지명, 개념 등 사실관계 오류가 줄고 내용의 신뢰도가 높아졌습니다.
- 한국어 자연스러움: 외래어 및 한자 혼용이 감소하고 문장 연결이 매끄러워져 가독성이 향상되었습니다.
- 지시문 충실도: JSON 형식 생성, 단계별 안내 등 사용자가 요구한 특정 포맷을 더 정확하게 이행했습니다.
- 정보 구조화: 중복 서술을 피하고 핵심 정보 위주로 간결하게 내용을 구성하는 능력이 개선되었습니다.
10. 결론
KoAlpaca 데이터셋을 활용한 LoRA 파인튜닝을 통해, LLaMA 3.2 3B 모델의 한국어 질의응답 품질과 지시문 이행 능력이 전반적으로 향상됨을 확인했습니다. 특히, Base 모델에서 발견된 사실 오류(날짜·지명·개념), 외국어·한자 혼입, 지시문 미준수 등이 크게 줄었으며, 응답이 더 구조화되고 자연스러운 문장으로 변했습니다.
이는 상대적으로 작은 규모의 모델과 효율적인 파인튜닝만으로도 실제 서비스 환경에서 활용 가능한 고품질 한국어 모델을 구현할 수 있음을 보여줍니다.
- 향상된 부분
- 표현 품질: 외래어 및 한자 혼용이 줄고, 문장의 연결과 가독성이 개선됨
- 정확성: 날짜, 지명 등 사실 기반 정보의 오류 감소
- 지시문 충실성: JSON, 단계별 설명 등 복잡한 요구사항의 이행률 상승
- 정보 구조화: 중복·불필요한 서술이 줄고 핵심 정보 위주의 구성
- 활용 가능성
- 동일한 방식으로 의료, 금융, 법률 등 도메인 특화 소형 LLM(sLLM) 제작 가능.
- 리소스 제약이 있는 환경에서도 고품질 한국어 모델 운영 가능.
- 향후 개선 방향
- 학습량 확대: Epoch 또는 Step 수를 늘려 복잡한 질의와 장문 응답 품질 개선
- 하이퍼파라미터 최적화:
- 학습률(Learning rate) 스케줄링
- 배치 크기 조정(Batch size)
- LoRA 랭크(adapter_dim) 변경
- 데이터 보강: Base 모델에서 자주 발생한 오류 유형을 타겟으로 한 고품질 데이터 추가
- 도메인 특화 튜닝: 의료·금융·법률 등 특정 분야 데이터셋으로 추가 파인튜닝
➡ 종합적으로, Custom 모델은 Base 모델 대비 한국어 자연스러움, 사실 정확도, 지시문 준수율, 정보 구조화 측면에서 뚜렷한 향상을 보였습니다.
※ 참고 이번 글에서는 주로 정성적 비교를 통해 튜닝 효과를 확인하였습니다. 평가 지표(ROUGE, F1 등)를 활용한 정량적 성능 분석은 추후 별도의 포스팅에서 더 깊이 다룰 예정입니다.