본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 7장: 파인튜닝 모델 평가와 벤치마킹
2026년 1월 26일·AI / ML·

7장: 파인튜닝 모델 평가와 벤치마킹

파인튜닝된 모델의 성능을 자동 메트릭, LLM 평가, 인간 평가를 통해 다각적으로 측정하고 벤치마킹하는 체계적인 방법을 다룹니다.

16분864자8개 섹션
llmtrainingmlopsdata-engineering
공유
fine-tuning7 / 10
12345678910
이전6장: 학습 파이프라인 구축과 하이퍼파라미터 최적화다음8장: 모델 레지스트리와 버전 관리

파인튜닝 평가가 특별한 이유

파인튜닝된 모델의 평가는 일반적인 머신러닝 모델 평가보다 복잡합니다. 단순히 정확도 하나로 성능을 판단할 수 없으며, 모델이 사전 학습에서 획득한 범용 능력을 유지하면서 동시에 새로운 능력을 얼마나 잘 습득했는지를 함께 측정해야 합니다.

text
파인튜닝 평가의 세 가지 축:
 
  1. 타겟 성능 (Target Performance)
     - 파인튜닝 목표 작업에서의 성능
     - "원래 하려던 것을 잘 하는가?"
 
  2. 범용 능력 보존 (General Capability Retention)
     - 사전 학습에서 획득한 지식과 능력 유지 여부
     - "기존에 할 수 있던 것을 잃어버리지 않았는가?"
 
  3. 안전성과 정렬 (Safety & Alignment)
     - 유해 콘텐츠 생성, 편향, 환각 등의 문제
     - "위험하거나 잘못된 응답을 하지 않는가?"

자동 평가 메트릭

학습 기반 메트릭

학습 과정에서 자동으로 추적되는 기본 메트릭입니다.

python
import numpy as np
from transformers import Trainer
 
 
def compute_perplexity(eval_pred):
    """퍼플렉시티 계산"""
    logits, labels = eval_pred
    # 손실에서 퍼플렉시티 계산
    loss = Trainer.compute_loss(logits, labels)
    perplexity = np.exp(loss)
    return {"perplexity": perplexity}
text
주요 학습 메트릭:
 
  Training Loss:
    - 학습 데이터에 대한 손실
    - 지속적으로 감소해야 함
    - 급격한 감소 후 정체는 정상
 
  Validation Loss:
    - 검증 데이터에 대한 손실
    - train_loss와의 갭이 과적합 지표
    - 증가 시 학습 중단 고려
 
  Perplexity:
    - exp(loss)로 계산
    - 낮을수록 좋음
    - 모델이 데이터를 얼마나 잘 예측하는지 측정
    - 도메인 간 비교에 유용

작업별 평가 메트릭

파인튜닝의 목표 작업에 따라 적절한 메트릭을 선택합니다.

python
from evaluate import load
 
 
def evaluate_classification(predictions, references):
    """분류 작업 평가"""
    accuracy = load("accuracy")
    f1 = load("f1")
 
    results = {}
    results.update(accuracy.compute(
        predictions=predictions, references=references
    ))
    results.update(f1.compute(
        predictions=predictions, references=references, average="weighted"
    ))
    return results
 
 
def evaluate_generation(predictions, references):
    """텍스트 생성 작업 평가"""
    rouge = load("rouge")
    bleu = load("bleu")
 
    results = {}
    results.update(rouge.compute(
        predictions=predictions, references=references
    ))
    results.update(bleu.compute(
        predictions=predictions, references=[[r] for r in references]
    ))
    return results
text
작업별 권장 메트릭:
 
  분류/추출:
    - Accuracy, F1 Score, Precision, Recall
    - 정확한 매칭 기반 평가
 
  텍스트 생성:
    - ROUGE (요약 품질)
    - BLEU (번역 품질)
    - BERTScore (의미적 유사도)
 
  대화/지시 따르기:
    - LLM-as-Judge (GPT-4, Claude 기반 평가)
    - 인간 평가 (가장 신뢰할 수 있음)
 
  코드 생성:
    - pass@k (실행 성공률)
    - CodeBLEU (구문/의미 유사도)

LLM-as-Judge 평가

LLM을 평가자로 활용하는 방법은 인간 평가의 확장성 문제를 해결하면서도 높은 품질의 평가를 제공합니다.

단일 응답 평가

python
import anthropic
 
client = anthropic.Anthropic()
 
 
def evaluate_with_llm(
    instruction: str,
    response: str,
    criteria: list[str]
) -> dict:
    """LLM을 사용한 응답 품질 평가"""
    criteria_text = "\n".join(
        str(i + 1) + ". " + c for i, c in enumerate(criteria)
    )
 
    prompt = (
        "다음 지시문에 대한 응답의 품질을 평가해 주세요.\n\n"
        "지시문:\n" + instruction + "\n\n"
        "응답:\n" + response + "\n\n"
        "평가 기준:\n" + criteria_text + "\n\n"
        "각 기준에 대해 1~5점으로 점수를 매기고, "
        "간단한 이유를 설명해 주세요.\n"
        "마지막에 종합 점수 (1~5)를 제시해 주세요.\n\n"
        "형식:\n"
        "기준 1: [점수]/5 - [이유]\n"
        "기준 2: [점수]/5 - [이유]\n"
        "...\n"
        "종합: [점수]/5"
    )
 
    result = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}]
    )
 
    return {"evaluation": result.content[0].text}

쌍별 비교 (Pairwise Comparison)

두 모델의 응답을 직접 비교하는 방법입니다. 절대적 점수보다 상대적 선호도가 더 신뢰할 수 있는 경우가 많습니다.

python
def pairwise_comparison(
    instruction: str,
    response_a: str,
    response_b: str
) -> dict:
    """두 응답의 쌍별 비교"""
    prompt = (
        "다음 지시문에 대한 두 응답을 비교해 주세요.\n\n"
        "지시문:\n" + instruction + "\n\n"
        "응답 A:\n" + response_a + "\n\n"
        "응답 B:\n" + response_b + "\n\n"
        "평가 기준:\n"
        "1. 정확성: 정보가 사실에 부합하는가\n"
        "2. 완전성: 질문에 충분히 답변했는가\n"
        "3. 명확성: 설명이 이해하기 쉬운가\n"
        "4. 유용성: 실질적으로 도움이 되는가\n\n"
        "각 기준에서 어떤 응답이 더 나은지, "
        "그리고 종합적으로 어떤 응답이 더 나은지 판단해 주세요.\n\n"
        "결론은 다음 중 하나로 제시해 주세요:\n"
        "A >> B (A가 확실히 우수)\n"
        "A > B (A가 약간 우수)\n"
        "A = B (동등)\n"
        "B > A (B가 약간 우수)\n"
        "B >> A (B가 확실히 우수)"
    )
 
    result = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}]
    )
 
    return {"comparison": result.content[0].text}
Warning

LLM-as-Judge에는 위치 편향(Position Bias)이 존재합니다. 응답 A와 B의 순서를 바꿔서 두 번 평가하고, 결과가 일관된 경우만 채택하는 것이 좋습니다. 또한 자기 자신이 생성한 응답에 대한 평가는 편향될 수 있으므로, 가능하면 다른 모델을 평가자로 사용합니다.

벤치마크 평가

범용 벤치마크

파인튜닝이 모델의 범용 능력에 미친 영향을 측정하기 위해 표준 벤치마크를 사용합니다.

text
주요 LLM 벤치마크:
 
  지식/추론:
    - MMLU: 57개 과목의 객관식 문제
    - ARC: 초등학교~중학교 과학 문제
    - HellaSwag: 상식 추론
 
  수학:
    - GSM8K: 초등학교 수학 문제
    - MATH: 고등학교~대학교 수학
 
  코드:
    - HumanEval: Python 코드 생성
    - MBPP: 간단한 프로그래밍 문제
 
  한국어:
    - KMMLU: 한국어 MMLU
    - KoBEST: 한국어 자연어 이해

lm-evaluation-harness 활용

EleutherAI의 lm-evaluation-harness는 LLM 벤치마크 평가를 위한 표준 도구입니다.

bash
# lm-evaluation-harness 설치
pip install lm-eval
 
# 기본 벤치마크 실행
lm_eval --model hf \
    --model_args pretrained=./merged-model,dtype=float16 \
    --tasks mmlu,arc_easy,arc_challenge,hellaswag \
    --batch_size 8 \
    --output_path ./eval_results
python
# Python API로 벤치마크 실행
import lm_eval
 
results = lm_eval.simple_evaluate(
    model="hf",
    model_args="pretrained=./merged-model,dtype=float16",
    tasks=["mmlu", "arc_easy", "hellaswag"],
    batch_size=8,
)
 
# 결과 출력
for task, metrics in results["results"].items():
    acc = metrics.get("acc,none", metrics.get("acc_norm,none", "N/A"))
    print(task + ": " + str(acc))

파인튜닝 전후 비교 프레임워크

체계적인 비교를 위한 평가 프레임워크를 구축합니다.

python
import json
from dataclasses import dataclass
from datetime import datetime
 
 
@dataclass
class EvaluationResult:
    model_name: str
    timestamp: str
    target_metrics: dict
    benchmark_metrics: dict
    qualitative_samples: list[dict]
 
 
def comprehensive_evaluation(
    model,
    tokenizer,
    test_dataset,
    model_name: str
) -> EvaluationResult:
    """종합 평가 실행"""
 
    # 1. 타겟 작업 평가
    target_metrics = evaluate_target_task(
        model, tokenizer, test_dataset
    )
 
    # 2. 정성적 샘플 생성
    samples = generate_qualitative_samples(
        model, tokenizer, test_dataset[:10]
    )
 
    return EvaluationResult(
        model_name=model_name,
        timestamp=datetime.now().isoformat(),
        target_metrics=target_metrics,
        benchmark_metrics={},
        qualitative_samples=samples,
    )
 
 
def evaluate_target_task(model, tokenizer, test_dataset):
    """타겟 작업 성능 평가"""
    predictions = []
    references = []
 
    for example in test_dataset:
        # 입력 생성
        messages = example["messages"][:-1]  # 마지막 응답 제외
        inputs = tokenizer.apply_chat_template(
            messages, return_tensors="pt", add_generation_prompt=True
        )
 
        # 응답 생성
        outputs = model.generate(
            inputs.to(model.device),
            max_new_tokens=512,
            temperature=0.1,
            do_sample=False,
        )
        prediction = tokenizer.decode(
            outputs[0][inputs.shape[1]:],
            skip_special_tokens=True
        )
        reference = example["messages"][-1]["content"]
 
        predictions.append(prediction)
        references.append(reference)
 
    # 메트릭 계산
    results = evaluate_generation(predictions, references)
    return results
 
 
def generate_qualitative_samples(model, tokenizer, examples):
    """정성적 평가를 위한 샘플 생성"""
    samples = []
 
    for example in examples:
        messages = example["messages"][:-1]
        inputs = tokenizer.apply_chat_template(
            messages, return_tensors="pt", add_generation_prompt=True
        )
 
        outputs = model.generate(
            inputs.to(model.device),
            max_new_tokens=512,
            temperature=0.7,
        )
        generated = tokenizer.decode(
            outputs[0][inputs.shape[1]:],
            skip_special_tokens=True
        )
 
        samples.append({
            "input": example["messages"][-2]["content"],
            "expected": example["messages"][-1]["content"],
            "generated": generated,
        })
 
    return samples

비교 보고서 생성

python
def generate_comparison_report(
    base_eval: EvaluationResult,
    ft_eval: EvaluationResult
) -> str:
    """파인튜닝 전후 비교 보고서 생성"""
    report_lines = [
        "파인튜닝 평가 보고서",
        "=" * 50,
        "",
        "베이스 모델: " + base_eval.model_name,
        "파인튜닝 모델: " + ft_eval.model_name,
        "평가 일시: " + ft_eval.timestamp,
        "",
        "--- 타겟 작업 성능 ---",
    ]
 
    for metric in ft_eval.target_metrics:
        base_val = base_eval.target_metrics.get(metric, "N/A")
        ft_val = ft_eval.target_metrics[metric]
        if isinstance(base_val, float) and isinstance(ft_val, float):
            diff = ft_val - base_val
            sign = "+" if diff >= 0 else ""
            report_lines.append(
                "  " + metric + ": "
                + str(round(base_val, 4)) + " -> "
                + str(round(ft_val, 4))
                + " (" + sign + str(round(diff, 4)) + ")"
            )
 
    report_lines.extend([
        "",
        "--- 정성적 샘플 ---",
    ])
 
    for i, sample in enumerate(ft_eval.qualitative_samples[:3]):
        report_lines.extend([
            "",
            "샘플 " + str(i + 1) + ":",
            "  입력: " + sample["input"][:100] + "...",
            "  기대: " + sample["expected"][:100] + "...",
            "  생성: " + sample["generated"][:100] + "...",
        ])
 
    return "\n".join(report_lines)

과적합 진단과 대응

파인튜닝에서 가장 흔한 문제인 과적합을 진단하고 대응하는 방법입니다.

text
과적합 증상과 대응:
 
  증상 1: train_loss는 낮지만 eval_loss가 높거나 증가
    원인: 학습 데이터에만 최적화됨
    대응:
      - 데이터 양 늘리기
      - 드롭아웃 높이기 (0.1 -> 0.2)
      - LoRA 랭크 줄이기
      - 조기 종료 적용
      - weight_decay 높이기
 
  증상 2: 학습 데이터와 유사한 입력에만 잘 응답
    원인: 다양성 부족
    대응:
      - 데이터 다양성 확보
      - 데이터 증강 적용
      - 에포크 수 줄이기
 
  증상 3: 반복적이거나 고정적인 패턴의 응답
    원인: 데이터 내 패턴 편중
    대응:
      - 시작 문구/구조가 다양한 데이터 추가
      - 학습률 낮추기
      - temperature 조절 (추론 시)
 
  증상 4: 범용 벤치마크 성능 하락 (Catastrophic Forgetting)
    원인: 원래 지식이 덮어써짐
    대응:
      - 학습률 낮추기
      - LoRA 랭크 줄이기
      - 범용 데이터를 학습 데이터에 일부 포함
      - 에포크 수 줄이기

평가 자동화 파이프라인

반복 실험에서 평가를 자동으로 수행하는 파이프라인을 구축합니다.

python
# evaluate.py
import json
import sys
from pathlib import Path
 
 
def run_evaluation_pipeline(model_path: str, output_dir: str):
    """자동 평가 파이프라인"""
 
    output = Path(output_dir)
    output.mkdir(parents=True, exist_ok=True)
 
    # 1. 모델 로드
    print("1. 모델 로드 중: " + model_path)
    model, tokenizer = load_model(model_path)
 
    # 2. 타겟 작업 평가
    print("2. 타겟 작업 평가 중...")
    test_data = load_test_data("data/test.jsonl")
    target_results = evaluate_target_task(model, tokenizer, test_data)
 
    # 3. 정성적 샘플 생성
    print("3. 정성적 샘플 생성 중...")
    samples = generate_qualitative_samples(
        model, tokenizer, test_data[:20]
    )
 
    # 4. 결과 저장
    results = {
        "model_path": model_path,
        "target_metrics": target_results,
        "num_samples": len(samples),
        "samples": samples,
    }
 
    results_path = str(output / "eval_results.json")
    with open(results_path, "w") as f:
        json.dump(results, f, ensure_ascii=False, indent=2)
 
    print("평가 완료. 결과: " + results_path)
    return results
 
 
if __name__ == "__main__":
    model_path = sys.argv[1] if len(sys.argv) > 1 else "./best-adapter"
    run_evaluation_pipeline(model_path, "./eval_output")

정리

이번 장에서는 파인튜닝된 모델의 평가와 벤치마킹 방법을 체계적으로 다루었습니다.

  • 타겟 성능, 범용 능력 보존, 안전성의 세 축으로 평가해야 합니다.
  • 자동 메트릭, LLM-as-Judge, 인간 평가를 조합하여 다각적으로 평가합니다.
  • lm-evaluation-harness로 표준 벤치마크 점수를 측정합니다.
  • 과적합의 증상별 대응 전략을 정리했습니다.

다음 장에서는 파인튜닝된 모델을 체계적으로 관리하기 위한 모델 레지스트리와 버전 관리 시스템을 구축합니다. 모델 아티팩트, 메타데이터, 실험 기록을 효과적으로 관리하는 방법을 안내합니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#llm#training#mlops#data-engineering

관련 글

AI / ML

8장: 모델 레지스트리와 버전 관리

파인튜닝된 모델을 체계적으로 관리하기 위한 모델 레지스트리 구축, 버전 관리, 메타데이터 추적, 아티팩트 저장 전략을 다룹니다.

2026년 1월 28일·15분
AI / ML

6장: 학습 파이프라인 구축과 하이퍼파라미터 최적화

파인튜닝 학습 파이프라인의 전체 구조를 설계하고, 학습률, 배치 크기, 스케줄링 등 핵심 하이퍼파라미터를 최적화하는 전략을 다룹니다.

2026년 1월 24일·18분
AI / ML

9장: 학습-평가-배포 자동화 사이클

파인튜닝의 학습, 평가, 배포 전체 과정을 CI/CD 파이프라인으로 자동화하고, 데이터 변경이나 코드 변경 시 자동으로 모델이 업데이트되는 체계를 구축합니다.

2026년 1월 30일·17분
이전 글6장: 학습 파이프라인 구축과 하이퍼파라미터 최적화
다음 글8장: 모델 레지스트리와 버전 관리

댓글

목차

약 16분 남음
  • 파인튜닝 평가가 특별한 이유
  • 자동 평가 메트릭
    • 학습 기반 메트릭
    • 작업별 평가 메트릭
  • LLM-as-Judge 평가
    • 단일 응답 평가
    • 쌍별 비교 (Pairwise Comparison)
  • 벤치마크 평가
    • 범용 벤치마크
    • lm-evaluation-harness 활용
  • 파인튜닝 전후 비교 프레임워크
    • 비교 보고서 생성
  • 과적합 진단과 대응
  • 평가 자동화 파이프라인
  • 정리