Blog
llm-evaluationbenchmarkragasmmluaitesting

LLM 평가(Evaluation) 체계 가이드 - 벤치마크부터 자체 평가 구축까지

LLM 평가의 핵심 개념, 주요 벤치마크(MMLU, HumanEval, MT-Bench), 자동 평가(LLM-as-Judge), RAG 평가(RAGAS), 자체 평가 체계 구축, A/B 테스트 전략을 체계적으로 정리합니다.

Data Dynamics2026년 4월 16일18 min read

선생님이 없는 시험을 생각해보세요. 학생이 얼마나 잘하는지 알려면 시험지를 채점해야 하는데, 채점 기준도 만들어야 하고 심지어 채점관이 또 다른 AI일 수도 있습니다. LLM 평가가 딱 이렇습니다.

어떤 모델이 더 나은지, Fine-Tuning이 효과가 있는지, 프로덕션에서 품질이 떨어지고 있는지 — 이 모든 것을 숫자로 측정하는 것이 평가입니다. 이 글에서는 벤치마크의 의미부터 자체 평가 체계 구축까지 차근차근 살펴봅니다.

이 글에서 배우는 것

  • MMLU·HumanEval·MT-Bench 등 주요 벤치마크의 의미와 한계
  • LLM-as-Judge: 강력한 LLM을 평가자로 써서 자동 채점하는 방법
  • RAG 품질을 측정하는 RAGAS 프레임워크와 각 지표 해석
  • 여러분 도메인에 맞는 자체 평가 파이프라인 구축 방법
  • A/B 테스트로 프로덕션에서 모델 성능을 지속적으로 모니터링하는 전략

1. LLM 평가가 중요한 이유

평가가 필요한 상황

언제 평가가 필요할까요? 사실 LLM을 쓰는 모든 순간에 필요합니다. 모델을 고를 때도, Fine-Tuning 후에도, 프로덕션 배포 후에도요.

상황평가 목적평가 방법
모델 선택GPT-4 vs Claude vs LLaMA 비교벤치마크, 도메인 태스크 평가
Fine-Tuning학습 전후 성능 비교태스크별 정확도, 형식 준수율
RAG 파이프라인검색+생성 품질 측정RAGAS, Recall@K
프롬프트 최적화프롬프트 변형 간 비교A/B 테스트, LLM-as-Judge
프로덕션 모니터링서비스 품질 지속 관리사용자 피드백, 자동 평가

평가의 어려움

그런데 LLM 평가는 유독 까다롭습니다. 왜 그럴까요?

  • 비결정성: 같은 입력에 다른 출력 (Temperature > 0)
  • 주관성: "좋은 답변"의 기준이 모호
  • 다차원: 정확도, 유창성, 유용성, 안전성 등 다양한 축
  • 도메인 의존: 범용 벤치마크와 실제 성능 괴리

이런 어려움 때문에 "단 하나의 점수"로 LLM을 평가하기는 어렵고, 목적에 맞는 여러 지표를 조합해야 합니다.


2. 주요 벤치마크

범용 벤치마크

"이 모델 벤치마크 점수가 높던데요" — 그 벤치마크가 무엇을 재는지 알아야 의미 있는 비교가 됩니다. 주요 벤치마크와 각자 측정하는 것을 정리했습니다.

벤치마크평가 대상형식문제 수특징
MMLU범용 지식4지선다15,90857개 주제, 가장 널리 사용
MMLU-Pro범용 지식 (고난도)10지선다12,032MMLU 업그레이드, CoT 필요
HellaSwag상식 추론4지선다10,042상황 후속 문장 선택
ARC-Challenge과학 추론4지선다1,172초등~중등 과학 문제
Winogrande상식2지선다1,267대명사 지시 대상 판별
TruthfulQA사실성생성형817할루시네이션 평가
GSM8K수학생성형1,319초등 수학 문제
MATH수학 (고난도)생성형5,000경시대회 수준 수학

코드 벤치마크

벤치마크평가 대상언어문제 수
HumanEval함수 생성Python164
HumanEval+강화된 테스트Python164
MBPP기본 프로그래밍Python500
SWE-bench실제 이슈 해결다중2,294
LiveCodeBench실시간 코딩다중지속 갱신

대화/지시 벤치마크

벤치마크평가 대상평가 방식
MT-Bench멀티턴 대화GPT-4가 1~10점 채점
AlpacaEval지시 따르기GPT-4 승률 비교
Chatbot Arena실사용자 선호도ELO 레이팅 (블라인드)
IFEval지시 따르기 정확도형식 준수 정확도

MTEB (Massive Text Embedding Benchmark)

임베딩 모델의 종합 평가:

태스크설명평가 지표
Retrieval검색 관련도nDCG@10
Classification텍스트 분류Accuracy
Clustering텍스트 클러스터링V-measure
STS (Semantic Similarity)의미 유사도Spearman 상관
Reranking재순위화MAP

3. 자동 평가: LLM-as-Judge

개념

사람이 모든 응답을 하나하나 채점하면 비용과 시간이 너무 많이 듭니다. GPT-4나 Claude처럼 강력한 LLM을 평가자(Judge)로 활용해 다른 모델의 응답 품질을 자동으로 채점하는 방식이 LLM-as-Judge입니다.

한 문장으로: LLM-as-Judge는 채점관도 AI인 방식으로, 대규모 자동 평가를 가능하게 하되 평가자 LLM의 편향을 주의해야 합니다.

import anthropic
 
client = anthropic.Anthropic()
 
def llm_judge(question: str, response: str, criteria: list) -> dict:
    """LLM을 평가자로 활용"""
    eval_prompt = f"""다음 응답을 평가하세요.
 
## 질문
{question}
 
## 응답
{response}
 
## 평가 기준 (각 1~5점)
{chr(10).join(f"- {c}" for c in criteria)}
 
## 채점 규칙
- 1점: 매우 부족
- 2점: 부족
- 3점: 보통
- 4점: 우수
- 5점: 매우 우수
 
JSON 형식으로 반환:
{{"scores": {{"기준명": 점수}}, "average": 평균, "reasoning": "평가 근거", "improvements": "개선 사항"}}
"""
    
    result = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        temperature=0.0,
        messages=[{"role": "user", "content": eval_prompt}]
    )
    return json.loads(result.content[0].text)
 
# 사용
scores = llm_judge(
    question="Kafka에서 컨슈머 랙이 증가하는 원인은?",
    response="...(평가 대상 응답)...",
    criteria=["정확성", "완전성", "실용성", "코드 예시 품질"]
)

Pairwise 비교

점수를 매기는 것보다 "A와 B 중 어느 게 낫나?"를 묻는 게 더 정확할 때가 많습니다. Chatbot Arena 방식처럼 두 응답을 나란히 보여주는 Pairwise 비교입니다.

def pairwise_compare(question: str, response_a: str, response_b: str) -> str:
    """두 응답 중 더 나은 것을 선택"""
    prompt = f"""두 응답을 비교하여 더 나은 것을 선택하세요.
 
질문: {question}
 
응답 A: {response_a}
 
응답 B: {response_b}
 
더 나은 응답을 선택하고 이유를 설명하세요.
반환 형식: {{"winner": "A" 또는 "B" 또는 "tie", "reason": "이유"}}
"""
    result = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=512,
        temperature=0.0,
        messages=[{"role": "user", "content": prompt}]
    )
    return json.loads(result.content[0].text)

LLM-as-Judge의 한계와 보완

한계설명보완 방법
위치 편향첫 번째 응답을 선호하는 경향순서를 바꿔 2회 평가
장문 편향긴 응답을 높게 평가하는 경향"간결함" 기준 추가
자기 편향같은 모델의 응답을 선호다른 모델로 평가
지시 편향평가 기준 순서에 민감기준 순서 랜덤화

4. RAG 평가: RAGAS

RAGAS 프레임워크

RAG를 만들었는데 "잘 동작하는지" 어떻게 알 수 있을까요? RAGAS는 검색 품질과 생성 품질을 각각 측정하는 지표 모음입니다. 네 가지 숫자를 보면 RAG의 어느 부분이 약한지 바로 알 수 있습니다.

from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
    context_utilization
)
from datasets import Dataset
 
# 평가 데이터 구성
eval_data = {
    "question": [
        "Spark에서 셔플 파티션을 조정하는 방법은?",
        "Kafka 컨슈머 그룹의 리밸런싱이란?"
    ],
    "answer": [
        "spark.sql.shuffle.partitions 설정을 변경합니다...",
        "컨슈머 그룹 내에서 파티션 할당을 재조정하는 과정입니다..."
    ],
    "contexts": [
        ["Spark 셔플 파티션은 spark.sql.shuffle.partitions로 설정..."],
        ["Kafka 리밸런싱은 컨슈머 그룹의 멤버 변경 시 발생..."]
    ],
    "ground_truth": [
        "spark.sql.shuffle.partitions 설정으로 셔플 파티션 수를 조정합니다.",
        "리밸런싱은 컨슈머 그룹 내 파티션 재할당 과정입니다."
    ]
}
 
dataset = Dataset.from_dict(eval_data)
 
results = evaluate(
    dataset,
    metrics=[faithfulness, answer_relevancy, context_precision, context_recall]
)
print(results)
# {'faithfulness': 0.92, 'answer_relevancy': 0.88, 
#  'context_precision': 0.85, 'context_recall': 0.90}

RAGAS 지표 해석

지표의미낮으면개선 방향
Faithfulness응답이 컨텍스트에 근거하는가할루시네이션프롬프트에 "컨텍스트만 사용" 강조
Answer Relevancy응답이 질문에 적절한가엉뚱한 답변프롬프트 개선, 검색 품질 향상
Context Precision검색 결과가 관련성 있는가노이즈 문서리랭킹 추가, 메타데이터 필터
Context Recall필요한 정보를 다 찾았는가정보 누락K값 증가, 하이브리드 검색

5. 자체 평가 체계 구축

평가 데이터셋 설계

범용 벤치마크는 여러분 도메인과 맞지 않을 수 있습니다. 실제로 중요한 것은 "우리 서비스의 실제 질문에 얼마나 잘 답하는가"이기 때문에 자체 평가 데이터셋이 필요합니다.

# 평가 데이터셋 구조
eval_dataset = [
    {
        "id": "eval-001",
        "category": "troubleshooting",
        "difficulty": "medium",
        "question": "Spark executor OOM 에러가 발생합니다. 원인과 해결 방법은?",
        "reference_answer": "executor.memory를 늘리거나, 파티션 수를 증가시키거나...",
        "required_elements": ["executor.memory", "파티션", "데이터 스큐"],
        "evaluation_criteria": {
            "accuracy": "기술적으로 정확한 원인 분석",
            "completeness": "주요 원인 3가지 이상 포함",
            "actionability": "구체적 설정값 또는 코드 포함",
            "format": "구조화된 형식 (번호 목록 등)"
        }
    }
]

자동 평가 파이프라인

class EvaluationPipeline:
    def __init__(self, judge_model: str = "claude-sonnet-4-6"):
        self.judge = anthropic.Anthropic()
        self.judge_model = judge_model
        self.results = []
    
    def evaluate_batch(self, model_fn, eval_dataset: list) -> dict:
        """배치 평가 실행"""
        for item in eval_dataset:
            # 1. 모델 응답 생성
            response = model_fn(item["question"])
            
            # 2. 자동 지표 평가
            auto_scores = self.auto_evaluate(item, response)
            
            # 3. LLM-as-Judge 평가
            judge_scores = self.judge_evaluate(item, response)
            
            self.results.append({
                "id": item["id"],
                "category": item["category"],
                "auto_scores": auto_scores,
                "judge_scores": judge_scores,
                "response": response
            })
        
        return self.aggregate_results()
    
    def auto_evaluate(self, item: dict, response: str) -> dict:
        """규칙 기반 자동 평가"""
        scores = {}
        # 필수 요소 포함 여부
        required = item.get("required_elements", [])
        found = sum(1 for e in required if e.lower() in response.lower())
        scores["element_coverage"] = found / max(len(required), 1)
        
        # 응답 길이 적정성
        word_count = len(response.split())
        scores["length_appropriate"] = 1.0 if 50 < word_count < 500 else 0.5
        
        return scores
    
    def aggregate_results(self) -> dict:
        """결과 집계"""
        categories = {}
        for r in self.results:
            cat = r["category"]
            if cat not in categories:
                categories[cat] = []
            categories[cat].append(r["judge_scores"]["average"])
        
        return {
            "overall_average": sum(r["judge_scores"]["average"] for r in self.results) / len(self.results),
            "by_category": {k: sum(v)/len(v) for k, v in categories.items()},
            "total_evaluated": len(self.results)
        }

6. A/B 테스트와 프로덕션 평가

온라인 A/B 테스트

오프라인 평가로 "좋다"고 나온 모델이 실제 사용자에게도 좋으리라는 보장은 없습니다. 프로덕션에서 절반의 트래픽으로 새 모델을 조용히 테스트하는 A/B 실험이 최종 검증입니다.

import random
from datetime import datetime
 
class ABTestManager:
    def __init__(self):
        self.experiments = {}
    
    def create_experiment(self, name: str, variants: dict, traffic_split: dict):
        """A/B 테스트 실험 생성"""
        self.experiments[name] = {
            "variants": variants,  # {"A": model_a, "B": model_b}
            "traffic_split": traffic_split,  # {"A": 0.5, "B": 0.5}
            "results": {"A": [], "B": []},
            "created_at": datetime.now()
        }
    
    def get_variant(self, experiment_name: str, user_id: str) -> str:
        """사용자별 일관된 변형 할당"""
        # 사용자 ID 기반 결정적 할당 (같은 사용자는 항상 같은 변형)
        hash_val = hash(f"{experiment_name}:{user_id}") % 100
        exp = self.experiments[experiment_name]
        cumulative = 0
        for variant, ratio in exp["traffic_split"].items():
            cumulative += ratio * 100
            if hash_val < cumulative:
                return variant
        return list(exp["variants"].keys())[-1]
    
    def record_feedback(self, experiment_name: str, variant: str, score: float):
        """사용자 피드백 기록"""
        self.experiments[experiment_name]["results"][variant].append(score)
    
    def get_statistics(self, experiment_name: str) -> dict:
        """통계적 유의성 분석"""
        exp = self.experiments[experiment_name]
        stats = {}
        for variant, scores in exp["results"].items():
            if scores:
                stats[variant] = {
                    "mean": sum(scores) / len(scores),
                    "count": len(scores),
                    "positive_rate": sum(1 for s in scores if s > 0) / len(scores)
                }
        return stats

프로덕션 모니터링 지표

지표설명수집 방법목표
사용자 만족도좋아요/싫어요 비율피드백 버튼> 80% 긍정
태스크 완료율사용자가 원하는 결과를 얻었는지후속 질문 분석> 70%
응답 지연시간TTFT, 전체 응답 시간서버 메트릭P95 < 3초
할루시네이션율사실과 다른 응답 비율자동 검증 + 사람 평가< 5%
거부율안전성 가드레일에 의한 차단로그 분석적정 수준 유지
비용토큰당 비용, 일일 비용API 과금예산 내

참고: LLM 평가는 "한 번에 완벽하게"가 아닌 "지속적으로 개선"하는 프로세스입니다. 프로덕션 데이터에서 실패 사례를 수집하고 평가 데이터셋에 추가하는 피드백 루프를 구축하세요.


마치며 — 핵심 요약

  • LLM 평가는 비결정성·주관성·다차원성 때문에 어렵지만, 그래서 더 체계적인 접근이 필요합니다.
  • 벤치마크(MMLU, HumanEval, SWE-bench)는 모델 간 상대 비교에 유용하지만, 여러분 도메인의 실제 성능과 다를 수 있습니다. 항상 직접 테스트를 병행하세요.
  • LLM-as-Judge로 대규모 자동 평가가 가능합니다. 단, 위치 편향·자기 편향을 방지하기 위해 순서를 바꿔 2회 평가하고, 다른 모델을 평가자로 쓰세요.
  • RAGAS 네 지표(Faithfulness·Answer Relevancy·Context Precision·Context Recall)를 보면 RAG의 어느 부분이 약한지 정확히 진단할 수 있습니다.
  • 자체 평가 데이터셋은 처음부터 50~100건이라도 만들어두세요. 프로덕션 실패 케이스를 수집해 계속 추가하는 피드백 루프가 핵심입니다.
  • 평가는 "한 번에 완벽하게"가 아니라 "계속 개선하는 과정"입니다. 오늘 당장 RAGAS로 여러분의 RAG를 측정해보세요 — 숫자가 개선 방향을 알려줄 겁니다.

References

  • Hendrycks, D. et al. (2021). "Measuring Massive Multitask Language Understanding (MMLU)." ICLR
  • Chen, M. et al. (2021). "Evaluating Large Language Models Trained on Code (HumanEval)." arXiv
  • Zheng, L. et al. (2023). "Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena." NeurIPS
  • Es, S. et al. (2024). "RAGAS: Automated Evaluation of Retrieval Augmented Generation." EACL
  • Muennighoff, N. et al. (2023). "MTEB: Massive Text Embedding Benchmark." EACL
  • LMSYS Chatbot Arena — https://chat.lmsys.org/

— Data Dynamics 엔지니어링 팀