본문으로 건너뛰기
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. 5장: 인간 평가와 어노테이션 설계
2026년 1월 23일·AI / ML·

5장: 인간 평가와 어노테이션 설계

LLM 평가에서 인간 평가의 역할, 어노테이션 가이드라인 설계, 평가자 간 일치도 관리 방법을 체계적으로 다룹니다.

20분743자8개 섹션
llmevaluationmonitoringobservabilitytesting
공유
llm-evaluation5 / 10
12345678910
이전4장: LLM-as-Judge - LLM으로 LLM 평가하기다음6장: A/B 테스트와 온라인 실험

인간 평가가 여전히 필요한 이유

자동 메트릭과 LLM-as-Judge가 발전했음에도, 인간 평가는 LLM 평가 체계에서 대체 불가능한 역할을 합니다.

첫째, 자동 메트릭의 유효성 검증(Validation)에 인간 평가가 필요합니다. 자동 메트릭이 실제로 의미 있는 것을 측정하고 있는지 확인하려면, 인간 판단과의 상관관계를 분석해야 합니다. 상관관계가 낮다면 그 메트릭은 신뢰할 수 없습니다.

둘째, 미묘한 품질 차이의 판단에 인간이 필요합니다. 문화적 맥락, 어조의 적절성, 은유나 비유의 자연스러움 등은 현재 자동 평가로 정확히 포착하기 어렵습니다.

셋째, 새로운 유형의 문제 발견에 인간이 뛰어납니다. 자동 평가는 미리 정의된 기준만 측정할 수 있지만, 인간 평가자는 예상치 못한 품질 문제를 발견하고 보고할 수 있습니다.

text
평가 방법별 역할:
 
자동 메트릭      --> 대규모 스크리닝, CI/CD 게이트
LLM-as-Judge    --> 중간 규모 품질 평가, 프롬프트 비교
인간 평가        --> 최종 품질 검증, 자동 메트릭 캘리브레이션, 문제 발견

인간 평가의 유형

절대 평가 (Absolute Rating)

각 응답에 독립적으로 점수를 부여하는 방식입니다. 리커트 척도(Likert Scale)를 주로 사용합니다.

python
# 절대 평가 가이드라인 구조
absolute_rating_schema = {
    "task": "응답 품질 평가",
    "scale": {
        1: "매우 나쁨 - 질문과 무관하거나, 완전히 부정확한 정보를 포함",
        2: "나쁨 - 부분적으로 관련되나, 주요 오류 또는 중요한 누락이 있음",
        3: "보통 - 질문에 답변하지만, 부정확성이나 불완전함이 있음",
        4: "좋음 - 정확하고 관련성 있는 답변이지만, 개선의 여지가 있음",
        5: "매우 좋음 - 정확하고 완전하며, 잘 구성된 답변",
    },
    "dimensions": [
        "정확성 - 사실적으로 올바른가",
        "완전성 - 질문의 모든 측면을 다루는가",
        "명확성 - 이해하기 쉬운가",
        "유용성 - 질문자에게 실질적 도움이 되는가",
    ]
}

상대 평가 (Comparative Rating)

두 개 이상의 응답을 비교하여 순위를 매기는 방식입니다. 절대 평가보다 평가자 간 일치도가 높은 것으로 알려져 있습니다.

python
# 상대 평가 가이드라인 구조
comparative_rating_schema = {
    "task": "두 응답 비교 평가",
    "options": [
        "A가 확실히 더 나음",
        "A가 약간 더 나음",
        "비슷함 (동점)",
        "B가 약간 더 나음",
        "B가 확실히 더 나음",
    ],
    "instruction": "두 응답을 전반적 품질 관점에서 비교하세요. "
                   "정확성, 완전성, 명확성을 종합적으로 고려하세요."
}

Best-of-N 선택

여러 응답 중 가장 좋은 것을 선택하는 방식입니다. RLHF(인간 피드백 기반 강화학습) 데이터 수집에 주로 사용됩니다.

어노테이션 가이드라인 설계

가이드라인의 품질이 인간 평가의 품질을 결정합니다. 모호한 가이드라인은 평가자 간 불일치를 유발하고, 결과의 신뢰도를 떨어뜨립니다.

가이드라인 구성 요소

text
효과적인 어노테이션 가이드라인 구성:
 
1. 과제 설명
   - 평가의 목적과 맥락
   - 대상 시스템에 대한 배경 정보
 
2. 평가 기준 정의
   - 각 평가 차원의 명확한 정의
   - 점수 척도와 각 수준의 상세 설명
 
3. 예시 (Anchor Examples)
   - 각 점수 수준에 해당하는 구체적 예시
   - 경계 사례(Borderline Case)에 대한 판단 지침
 
4. 주의사항
   - 흔한 실수와 피해야 할 편향
   - 애매한 상황에서의 처리 방법
 
5. 프로세스 설명
   - 평가 도구 사용법
   - 작업 흐름과 소요 시간 안내

앵커 예시 작성

앵커 예시(Anchor Example)는 각 점수 수준의 기준을 구체화하는 핵심 요소입니다.

python
anchor_examples = {
    "dimension": "정확성",
    "examples": [
        {
            "score": 5,
            "question": "파이썬의 GIL이란 무엇인가요?",
            "answer": "GIL(Global Interpreter Lock)은 CPython 인터프리터에서 "
                      "한 번에 하나의 스레드만 파이썬 바이트코드를 실행하도록 "
                      "제한하는 뮤텍스입니다. 멀티스레딩 환경에서 메모리 관리의 "
                      "스레드 안전성을 보장하기 위해 도입되었습니다. "
                      "CPU 바운드 작업에서는 병렬 처리가 제한되지만, "
                      "I/O 바운드 작업에서는 GIL이 해제되므로 "
                      "멀티스레딩의 이점을 얻을 수 있습니다.",
            "rationale": "GIL의 정의, 존재 이유, 영향 범위를 정확하게 설명함",
        },
        {
            "score": 3,
            "question": "파이썬의 GIL이란 무엇인가요?",
            "answer": "GIL은 파이썬에서 멀티스레딩을 제한하는 잠금 장치입니다. "
                      "이 때문에 파이썬은 여러 스레드를 동시에 사용할 수 없습니다.",
            "rationale": "기본 개념은 맞지만, CPython 한정이라는 점을 빠뜨렸고, "
                         "I/O 바운드에서의 동작을 설명하지 않아 불완전하며 "
                         "다소 오해를 줄 수 있는 표현이 포함됨",
        },
        {
            "score": 1,
            "question": "파이썬의 GIL이란 무엇인가요?",
            "answer": "GIL은 파이썬의 가비지 컬렉션 시스템입니다. "
                      "메모리를 자동으로 관리하여 프로그래머가 "
                      "메모리 할당과 해제를 신경 쓰지 않아도 됩니다.",
            "rationale": "GIL을 가비지 컬렉션과 혼동하여 "
                         "근본적으로 부정확한 정보를 제공함",
        },
    ]
}

평가자 간 일치도 (Inter-Annotator Agreement)

복수의 평가자가 참여할 때, 평가 결과가 얼마나 일치하는지를 측정하는 것은 평가 품질 관리의 핵심입니다.

Cohen's Kappa

두 평가자 간의 일치도를 우연에 의한 일치를 보정하여 측정합니다.

python
from sklearn.metrics import cohen_kappa_score
 
def compute_agreement(
    annotator_a: list,
    annotator_b: list
) -> dict:
    """두 평가자 간의 일치도를 계산합니다."""
    # 정확 일치율
    raw_agreement = sum(
        1 for a, b in zip(annotator_a, annotator_b) if a == b
    ) / len(annotator_a)
 
    # Cohen's Kappa
    kappa = cohen_kappa_score(annotator_a, annotator_b)
 
    # 가중 Kappa (순서형 척도에 적합)
    weighted_kappa = cohen_kappa_score(
        annotator_a, annotator_b, weights="quadratic"
    )
 
    return {
        "raw_agreement": round(raw_agreement, 3),
        "cohen_kappa": round(kappa, 3),
        "weighted_kappa": round(weighted_kappa, 3),
    }

Fleiss' Kappa

셋 이상의 평가자가 참여할 때 사용합니다.

python
def fleiss_kappa(ratings_matrix) -> float:
    """Fleiss' Kappa를 계산합니다.
 
    ratings_matrix: N x K 행렬
    N = 평가 대상 수, K = 카테고리 수
    각 셀은 해당 카테고리를 선택한 평가자 수
    """
    import numpy as np
    matrix = np.array(ratings_matrix)
    N, K = matrix.shape
    n = matrix.sum(axis=1)[0]  # 각 항목의 평가자 수
 
    # 항목별 일치도
    p_i = (np.sum(matrix ** 2, axis=1) - n) / (n * (n - 1))
    P_bar = np.mean(p_i)
 
    # 우연 일치도
    p_j = np.sum(matrix, axis=0) / (N * n)
    P_e = np.sum(p_j ** 2)
 
    if P_e == 1:
        return 1.0
 
    kappa = (P_bar - P_e) / (1 - P_e)
    return round(float(kappa), 3)

일치도 해석 기준

Kappa 값해석필요 조치
0.81 - 1.00거의 완전 일치이상적인 상태
0.61 - 0.80상당한 일치대부분의 평가에 적합
0.41 - 0.60보통 일치가이드라인 보강 필요
0.21 - 0.40약한 일치가이드라인 재설계 및 재교육 필요
0.00 - 0.20미미한 일치평가 체계 전면 재검토 필요
Warning

Kappa 값이 0.6 미만이면 해당 평가 결과를 신뢰할 수 없습니다. 이 경우 가이드라인을 보강하고, 앵커 예시를 추가하고, 평가자 교육을 실시한 후 재평가해야 합니다. 낮은 일치도의 원인은 대부분 모호한 가이드라인에 있습니다.

인간 평가 프로세스 설계

파일럿 평가

본격적인 평가 전에 소규모 파일럿(Pilot)을 실시하여 가이드라인의 명확성과 평가 프로세스의 실행 가능성을 확인합니다.

python
def run_pilot_evaluation(
    sample_cases: list,
    annotators: list,
    guideline: dict
) -> dict:
    """파일럿 평가를 실시하고 결과를 분석합니다."""
    # 1. 소규모 샘플 준비 (20-50건)
    pilot_size = min(50, len(sample_cases))
    pilot_cases = sample_cases[:pilot_size]
 
    # 2. 모든 평가자가 동일 케이스 평가 (중복 평가)
    all_ratings = {}
    for annotator in annotators:
        ratings = annotator.evaluate(pilot_cases, guideline)
        all_ratings[annotator.id] = ratings
 
    # 3. 일치도 분석
    agreement = compute_pairwise_agreement(all_ratings)
 
    # 4. 불일치 케이스 분석
    disagreements = find_disagreements(all_ratings, threshold=2)
 
    # 5. 가이드라인 개선점 도출
    improvements = analyze_disagreement_patterns(disagreements)
 
    return {
        "agreement_scores": agreement,
        "disagreement_cases": disagreements,
        "suggested_improvements": improvements,
        "ready_for_main": agreement["average_kappa"] >= 0.6,
    }

교육과 캘리브레이션 세션

파일럿 결과를 바탕으로 평가자 교육을 실시합니다.

text
캘리브레이션 세션 프로세스:
 
1. 가이드라인 설명 (30분)
   - 평가 목적, 기준, 척도 설명
   - 앵커 예시 검토
 
2. 독립 평가 실습 (30분)
   - 10-20건의 예시를 각자 평가
   - 다른 평가자의 결과를 보지 않고 독립적으로 수행
 
3. 결과 비교 토론 (30분)
   - 불일치 케이스에 대해 토론
   - 각자의 판단 근거를 공유
   - 합의점 도출
 
4. 가이드라인 업데이트 (15분)
   - 토론 결과를 가이드라인에 반영
   - 새로운 앵커 예시 추가
 
5. 재평가 확인 (15분)
   - 추가 예시로 일치도 확인

효율적인 인간 평가 전략

샘플링 전략

전체 데이터를 인간이 평가하는 것은 비현실적입니다. 효과적인 샘플링이 필요합니다.

python
def stratified_sample(
    data: list,
    sample_size: int,
    stratify_key: str,
    priority_filter: str = None
) -> list:
    """계층화 샘플링으로 대표성 있는 평가 샘플을 추출합니다."""
    from collections import defaultdict
    import random
 
    # 계층별 분류
    strata = defaultdict(list)
    for item in data:
        key = item.get("metadata", {}).get(stratify_key, "unknown")
        strata[key].append(item)
 
    # 우선 평가 대상 필터링
    if priority_filter:
        priority_items = [
            item for item in data
            if item.get("auto_eval", {}).get("flag") == priority_filter
        ]
        # 우선 대상에서 50% 할당
        priority_sample = random.sample(
            priority_items,
            min(sample_size // 2, len(priority_items))
        )
    else:
        priority_sample = []
 
    # 나머지는 계층별 비례 배분
    remaining = sample_size - len(priority_sample)
    proportional_sample = []
    total = sum(len(v) for v in strata.values())
 
    for key, items in strata.items():
        n = max(1, int(remaining * len(items) / total))
        proportional_sample.extend(random.sample(items, min(n, len(items))))
 
    return priority_sample + proportional_sample[:remaining]
Tip

자동 평가에서 경계선(Borderline) 점수를 받은 케이스나, LLM-as-Judge의 확신도가 낮은 케이스를 우선 인간 평가 대상으로 선정하면, 제한된 인간 평가 예산을 가장 효과적으로 활용할 수 있습니다.

인간-LLM 하이브리드 평가

python
class HybridEvaluator:
    """인간 평가와 LLM 평가를 결합한 하이브리드 평가 시스템입니다."""
 
    def __init__(self, llm_judge, human_budget: int):
        self.llm_judge = llm_judge
        self.human_budget = human_budget
 
    async def evaluate(self, cases: list) -> list:
        """하이브리드 평가를 실행합니다."""
        # 1단계: 전체를 LLM으로 평가
        llm_results = await self.llm_judge.batch_evaluate(cases)
 
        # 2단계: 인간 평가 대상 선별
        human_candidates = self._select_for_human_review(llm_results)
 
        # 3단계: 선별된 케이스를 인간이 평가
        human_results = await self._request_human_evaluation(
            human_candidates[:self.human_budget]
        )
 
        # 4단계: 결과 통합
        return self._merge_results(llm_results, human_results)
 
    def _select_for_human_review(self, llm_results: list) -> list:
        """인간 리뷰가 필요한 케이스를 선별합니다."""
        candidates = []
        for result in llm_results:
            overall = result.get("overall", 0)
            confidence = result.get("confidence", 1.0)
 
            # 경계선 점수 (2.5-3.5)
            if 2.5 <= overall <= 3.5:
                candidates.append(("borderline", result))
            # 낮은 확신도
            elif confidence < 0.6:
                candidates.append(("low_confidence", result))
            # 기준 차원 간 큰 편차
            elif self._has_high_variance(result):
                candidates.append(("high_variance", result))
 
        # 우선순위에 따라 정렬
        priority = {"low_confidence": 0, "borderline": 1, "high_variance": 2}
        candidates.sort(key=lambda x: priority.get(x[0], 3))
        return [c[1] for c in candidates]
 
    def _has_high_variance(self, result: dict) -> bool:
        scores = list(result.get("scores", {}).values())
        if len(scores) < 2:
            return False
        return max(scores) - min(scores) >= 3

평가자 품질 관리

골드 스탠다드 삽입

평가 작업 중에 정답이 알려진 "골드 스탠다드(Gold Standard)" 항목을 삽입하여, 평가자의 주의력과 정확도를 지속적으로 모니터링합니다.

python
def inject_gold_standards(
    eval_cases: list,
    gold_cases: list,
    injection_rate: float = 0.1
) -> list:
    """평가 케이스에 골드 스탠다드를 삽입합니다."""
    import random
    import math
 
    num_gold = max(1, math.ceil(len(eval_cases) * injection_rate))
    selected_gold = random.sample(gold_cases, min(num_gold, len(gold_cases)))
 
    # 골드 케이스에 표식 (평가자에게는 보이지 않음)
    for g in selected_gold:
        g["_is_gold"] = True
        g["_expected_score"] = g.pop("known_score")
 
    # 랜덤 위치에 삽입
    combined = eval_cases.copy()
    for gold in selected_gold:
        pos = random.randint(0, len(combined))
        combined.insert(pos, gold)
 
    return combined
 
def check_annotator_quality(
    annotations: list,
    tolerance: float = 1.0
) -> dict:
    """골드 스탠다드 결과로 평가자 품질을 확인합니다."""
    gold_items = [a for a in annotations if a.get("_is_gold")]
    if not gold_items:
        return {"status": "no_gold_items"}
 
    correct = 0
    for item in gold_items:
        expected = item["_expected_score"]
        actual = item["annotated_score"]
        if abs(expected - actual) <= tolerance:
            correct += 1
 
    accuracy = correct / len(gold_items)
    return {
        "gold_accuracy": round(accuracy, 3),
        "total_gold": len(gold_items),
        "correct": correct,
        "acceptable": accuracy >= 0.8,
    }

정리

인간 평가는 자동 메트릭의 유효성 검증, 미묘한 품질 판단, 새로운 문제 발견에 필수적입니다. 효과적인 인간 평가를 위해서는 명확한 어노테이션 가이드라인, 앵커 예시, 파일럿 평가와 캘리브레이션 세션이 필요하며, 평가자 간 일치도를 지속적으로 모니터링해야 합니다.

제한된 인간 평가 예산을 효율적으로 활용하기 위해, 자동 평가와 인간 평가를 결합한 하이브리드 접근법이 실무에서 가장 효과적입니다.

다음 장에서는 프로덕션 환경에서 실제 사용자 트래픽을 대상으로 변경 사항의 영향을 측정하는 A/B 테스트와 온라인 실험을 다룹니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#llm#evaluation#monitoring#observability#testing

관련 글

AI / ML

6장: A/B 테스트와 온라인 실험

LLM 애플리케이션에서 A/B 테스트를 설계하고 실행하는 방법, 통계적 유의성 판단, 실험 결과 해석을 다룹니다.

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

4장: LLM-as-Judge - LLM으로 LLM 평가하기

LLM을 평가자로 활용하는 LLM-as-Judge 기법의 원리, 프롬프트 설계, 편향 완화 전략을 체계적으로 다룹니다.

2026년 1월 21일·17분
AI / ML

7장: 프로덕션 로깅과 관찰 가능성

LLM 애플리케이션의 프로덕션 환경에서 구조화된 로깅, 분산 트레이싱, 관찰 가능성을 구축하는 방법을 다룹니다.

2026년 1월 27일·14분
이전 글4장: LLM-as-Judge - LLM으로 LLM 평가하기
다음 글6장: A/B 테스트와 온라인 실험

댓글

목차

약 20분 남음
  • 인간 평가가 여전히 필요한 이유
  • 인간 평가의 유형
    • 절대 평가 (Absolute Rating)
    • 상대 평가 (Comparative Rating)
    • Best-of-N 선택
  • 어노테이션 가이드라인 설계
    • 가이드라인 구성 요소
    • 앵커 예시 작성
  • 평가자 간 일치도 (Inter-Annotator Agreement)
    • Cohen's Kappa
    • Fleiss' Kappa
    • 일치도 해석 기준
  • 인간 평가 프로세스 설계
    • 파일럿 평가
    • 교육과 캘리브레이션 세션
  • 효율적인 인간 평가 전략
    • 샘플링 전략
    • 인간-LLM 하이브리드 평가
  • 평가자 품질 관리
    • 골드 스탠다드 삽입
  • 정리