리포트 평가자
리포트 평가자 (Report Evaluators)
리포트 평가자는 개별 케이스가 아니라 실험 전체의 결과를 분석해요. 혼동 행렬, 정밀도-재현율 곡선, 정확도 점수, 또는 커스텀 요약 테이블 같은 실험 전반의 통계를 계산하는 데 사용해요.
출처: 문서
본문
리포트 평가자는 어떻게 동작하나요?
일반 평가자는 케이스마다 한 번 실행되며 개별 출력을 평가해요. 리포트 평가자는 모든 케이스가 평가된 후에 실험당 한 번 실행되며, 전체 EvaluationReport를 입력으로 받아요.
Cases executed → Case evaluators run → Report evaluators run → Final report
리포트 평가자의 결과는 보고서의 analyses로 저장되고, Logfire가 구성되면 시각화를 위해 구조화된 속성으로 실험 스팬에 첨부돼요.
리포트 평가자 사용하기
report_evaluators 매개변수를 통해 Dataset에 리포트 평가자를 전달해요:
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import ConfusionMatrixEvaluator
def my_classifier(text: str) -> str:
text = text.lower()
if 'cat' in text or 'meow' in text:
return 'cat'
elif 'dog' in text or 'bark' in text:
return 'dog'
return 'unknown'
dataset = Dataset(
name='animal_classifier',
cases=[
Case(name='cat', inputs='The cat goes meow', expected_output='cat'),
Case(name='dog', inputs='The dog barks', expected_output='dog'),
],
report_evaluators=[
ConfusionMatrixEvaluator(
predicted_from='output',
expected_from='expected_output',
title='Animal Classification',
),
],
)
report = dataset.evaluate_sync(my_classifier)
# report.analyses에 ConfusionMatrix 결과가 들어 있음
내장 리포트 평가자
ConfusionMatrixEvaluator
모든 케이스에 걸쳐 예측 vs 기대 라벨을 비교하는 혼동 행렬을 만들어요.
from pydantic_evals.evaluators import ConfusionMatrixEvaluator
ConfusionMatrixEvaluator(
predicted_from='output',
expected_from='expected_output',
title='My Confusion Matrix',
)
매개변수:
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
predicted_from |
'expected_output' | 'output' | 'metadata' | 'labels' |
'output' |
예측 값의 출처 |
predicted_key |
str | None |
None |
metadata 또는 labels 사용 시 추출할 키 |
expected_from |
'expected_output' | 'output' | 'metadata' | 'labels' |
'expected_output' |
기대/실제 값의 출처 |
expected_key |
str | None |
None |
metadata 또는 labels 사용 시 추출할 키 |
title |
str |
'Confusion Matrix' |
보고서에 표시되는 제목 |
반환: ConfusionMatrix
데이터 출처:
'output'-- 작업의 실제 출력 (문자열로 변환)'expected_output'-- 케이스의 기대 출력 (문자열로 변환)'metadata'-- 케이스 metadata 딕셔너리의 값 (key필요)'labels'-- 케이스 수준 평가자의 라벨 결과 (key필요)
예시 -- 기대 출력이 있는 분류:
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import ConfusionMatrixEvaluator
dataset = Dataset(
name='animal_sounds',
cases=[
Case(inputs='meow', expected_output='cat'),
Case(inputs='woof', expected_output='dog'),
Case(inputs='chirp', expected_output='bird'),
],
report_evaluators=[
ConfusionMatrixEvaluator(
predicted_from='output',
expected_from='expected_output',
),
],
)
예시 -- 평가자 라벨 사용:
케이스 수준 평가자가 predicted_class 같은 라벨을 만들면 그것을 참조할 수 있어요:
from dataclasses import dataclass
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import (
ConfusionMatrixEvaluator,
Evaluator,
EvaluatorContext,
)
@dataclass
class ClassifyOutput(Evaluator):
def evaluate(self, ctx: EvaluatorContext) -> dict[str, str]:
# 출력을 범주로 분류
return {'predicted_class': categorize(ctx.output)}
def categorize(output: str) -> str:
return 'positive' if 'good' in output.lower() else 'negative'
dataset = Dataset(
name='labels_example',
cases=[Case(inputs='test', expected_output='positive')],
evaluators=[ClassifyOutput()],
report_evaluators=[
ConfusionMatrixEvaluator(
predicted_from='labels',
predicted_key='predicted_class',
expected_from='expected_output',
),
],
)
PrecisionRecallEvaluator
숫자 점수와 이진 ground-truth 라벨로부터 AUC(곡선 아래 면적)가 있는 정밀도-재현율 곡선을 계산해요.
from pydantic_evals.evaluators import PrecisionRecallEvaluator
PrecisionRecallEvaluator(
score_from='scores',
score_key='confidence',
positive_from='assertions',
positive_key='is_correct',
)
매개변수:
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
score_key |
str |
(필수) | scores 또는 metrics 딕셔너리의 키 |
positive_from |
'expected_output' | 'assertions' | 'labels' |
(필수) | ground-truth 이진 라벨의 출처 |
positive_key |
str | None |
None |
assertions 또는 labels 딕셔너리의 키 |
score_from |
'scores' | 'metrics' |
'scores' |
숫자 점수의 출처 |
title |
str |
'Precision-Recall Curve' |
보고서에 표시되는 제목 |
n_thresholds |
int |
100 |
곡선의 임계값 포인트 수 |
반환: PrecisionRecall + ScalarResult (AUC)
AUC는 정확성을 위해 전체 해상도(고유 점수마다 임계값으로)로 계산된 후, 곡선 포인트는 표시를 위해 n_thresholds로 다운샘플링돼요. AUC는 차트 렌더링용으로 곡선에, 그리고 쿼리·정렬용으로 별도의 ScalarResult로 모두 반환돼요.
점수 출처:
'scores'-- 케이스 수준 평가자의 숫자 점수 (score_key로 조회)'metrics'-- 작업 실행 중 설정된 커스텀 메트릭 (score_key로 조회)
Positive 출처:
'assertions'-- 케이스 수준 평가자의 불리언 판정 (positive_key로 조회)'labels'-- 불리언으로 변환된 라벨 결과 (positive_key로 조회)'expected_output'-- 불리언으로 변환된 케이스의 기대 출력
예시:
from dataclasses import dataclass
from typing import Any
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import (
Evaluator,
EvaluatorContext,
PrecisionRecallEvaluator,
)
@dataclass
class ConfidenceEvaluator(Evaluator):
def evaluate(self, ctx: EvaluatorContext) -> dict[str, Any]:
confidence = calculate_confidence(ctx.output)
return {
'confidence': confidence, # 숫자 점수
'is_correct': ctx.output == ctx.expected_output, # 불리언 판정
}
def calculate_confidence(output: str) -> float:
return 0.85 # 자리표시자
dataset = Dataset(
name='precision_recall_example',
cases=[
Case(inputs='test 1', expected_output='cat'),
Case(inputs='test 2', expected_output='dog'),
],
evaluators=[ConfidenceEvaluator()],
report_evaluators=[
PrecisionRecallEvaluator(
score_from='scores',
score_key='confidence',
positive_from='assertions',
positive_key='is_correct',
),
],
)
ROCAUCEvaluator
숫자 점수와 이진 ground-truth 라벨로부터 ROC(Receiver Operating Characteristic) 곡선과 AUC를 계산해요. ROC 곡선은 다양한 임계값에서 참양성률(True Positive Rate)을 위양성률(False Positive Rate)에 대해 플롯하며, 기준선 역할을 하는 점선 무작위 대각선이 포함돼요.
from pydantic_evals.evaluators import ROCAUCEvaluator
ROCAUCEvaluator(
score_key='confidence',
positive_from='assertions',
positive_key='is_correct',
)
매개변수:
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
score_key |
str |
(필수) | scores 또는 metrics 딕셔너리의 키 |
positive_from |
'expected_output' | 'assertions' | 'labels' |
(필수) | ground-truth 이진 라벨의 출처 |
positive_key |
str | None |
None |
assertions 또는 labels 딕셔너리의 키 |
score_from |
'scores' | 'metrics' |
'scores' |
숫자 점수의 출처 |
title |
str |
'ROC Curve' |
보고서에 표시되는 제목 |
n_thresholds |
int |
100 |
곡선의 임계값 포인트 수 |
반환: LinePlot + ScalarResult (AUC)
AUC는 전체 해상도로 계산돼요. 차트에는 시각적 비교를 위해 (0, 0)에서 (1, 1)로 가는 점선 "Random" 기준선 대각선이 포함돼요.
점수 및 Positive 출처: PrecisionRecallEvaluator와 동일해요.
KolmogorovSmirnovEvaluator
숫자 점수와 이진 ground-truth 라벨로부터 Kolmogorov-Smirnov 플롯과 KS 통계량을 계산해요. KS 플롯은 positive와 negative 케이스의 점수 분포에 대한 경험적 CDF(누적 분포 함수)를 보여줘요. KS 통계량은 두 CDF 사이의 최대 수직 거리예요 -- 값이 클수록 클래스 분리가 더 잘 된다는 뜻이에요.
from pydantic_evals.evaluators import KolmogorovSmirnovEvaluator
KolmogorovSmirnovEvaluator(
score_key='confidence',
positive_from='assertions',
positive_key='is_correct',
)
매개변수:
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
score_key |
str |
(필수) | scores 또는 metrics 딕셔너리의 키 |
positive_from |
'expected_output' | 'assertions' | 'labels' |
(필수) | ground-truth 이진 라벨의 출처 |
positive_key |
str | None |
None |
assertions 또는 labels 딕셔너리의 키 |
score_from |
'scores' | 'metrics' |
'scores' |
숫자 점수의 출처 |
title |
str |
'KS Plot' |
보고서에 표시되는 제목 |
n_thresholds |
int |
100 |
곡선의 임계값 포인트 수 |
반환: LinePlot + ScalarResult (KS 통계량)
점수 및 Positive 출처: PrecisionRecallEvaluator와 동일해요.
커스텀 리포트 평가자
ReportEvaluator를 상속하고 evaluate 메서드를 구현해서 커스텀 리포트 평가자를 작성해요:
from dataclasses import dataclass
from pydantic_evals.evaluators import ReportEvaluator, ReportEvaluatorContext
from pydantic_evals.reporting.analyses import ScalarResult
@dataclass
class AccuracyEvaluator(ReportEvaluator):
"""Computes overall accuracy as a scalar metric."""
def evaluate(self, ctx: ReportEvaluatorContext) -> ScalarResult:
cases = ctx.report.cases
if not cases:
return ScalarResult(title='Accuracy', value=0.0, unit='%')
correct = sum(
1 for case in cases
if case.output == case.expected_output
)
accuracy = correct / len(cases) * 100
return ScalarResult(title='Accuracy', value=accuracy, unit='%')
ReportEvaluatorContext
evaluate()에 전달되는 컨텍스트는 다음을 담아요:
ctx.name-- 실험 이름ctx.report-- 모든 케이스 결과를 담은 전체EvaluationReportctx.experiment_metadata-- 선택적 실험 수준 메타데이터 딕셔너리
ctx.report.cases를 통해 각 케이스의 입력, 출력, 기대 출력, 점수, 라벨, 판정, 메트릭, 속성에 접근할 수 있어요.
반환 유형
리포트 평가자는 ReportAnalysis 또는 list[ReportAnalysis]를 반환해야 해요. 사용 가능한 분석 유형은 다음과 같아요:
ScalarResult
단일 숫자 통계:
from pydantic_evals.reporting.analyses import ScalarResult
ScalarResult(
title='Accuracy',
value=93.3,
unit='%',
description='Percentage of correctly classified cases.',
)
| 필드 | 유형 | 설명 |
|---|---|---|
title |
str |
표시 이름 |
value |
float | int |
숫자 값 |
unit |
str | None |
선택적 단위 라벨 (예: '%', 'ms') |
description |
str | None |
선택적 더 긴 설명 |
TableResult
일반적인 데이터 테이블:
from pydantic_evals.reporting.analyses import TableResult
TableResult(
title='Per-Class Metrics',
columns=['Class', 'Precision', 'Recall', 'F1'],
rows=[
['cat', 0.95, 0.90, 0.924],
['dog', 0.88, 0.92, 0.899],
],
description='Precision, recall, and F1 per class.',
)
| 필드 | 유형 | 설명 |
|---|---|---|
title |
str |
표시 이름 |
columns |
list[str] |
열 머리글 |
rows |
list[list[str | int | float | bool | None]] |
행 데이터 |
description |
str | None |
선택적 더 긴 설명 |
ConfusionMatrix
혼동 행렬 (보통 ConfusionMatrixEvaluator가 만들지만 직접 구성할 수도 있어요):
from pydantic_evals.reporting.analyses import ConfusionMatrix
ConfusionMatrix(
title='Sentiment',
class_labels=['positive', 'negative', 'neutral'],
matrix=[
[45, 3, 2], # expected=positive
[5, 40, 5], # expected=negative
[1, 2, 47], # expected=neutral
],
)
| 필드 | 유형 | 설명 |
|---|---|---|
title |
str |
표시 이름 |
class_labels |
list[str] |
양 축에 대한 정렬된 라벨 |
matrix |
list[list[int]] |
matrix[expected] = 개수 |
description |
str | None |
선택적 더 긴 설명 |
PrecisionRecall
정밀도-재현율 곡선 데이터 (보통 PrecisionRecallEvaluator가 만들어요):
| 필드 | 유형 | 설명 |
|---|---|---|
title |
str |
표시 이름 |
curves |
list[PrecisionRecallCurve] |
하나 이상의 곡선 |
description |
str | None |
선택적 더 긴 설명 |
각 PrecisionRecallCurve는 name, PrecisionRecallPoint 목록(threshold, precision, recall 포함), 선택적 auc 값을 담아요.
LinePlot
라벨이 붙은 축을 가진 일반 XY 꺾은선 차트로, 여러 곡선을 지원해요. ROC 곡선, KS 플롯, 캘리브레이션 곡선, 또는 어떤 커스텀 꺾은선 차트든 이것을 사용해요:
from pydantic_evals.reporting.analyses import LinePlot, LinePlotCurve, LinePlotPoint
LinePlot(
title='ROC Curve',
x_label='False Positive Rate',
y_label='True Positive Rate',
x_range=(0, 1),
y_range=(0, 1),
curves=[
LinePlotCurve(
name='Model (AUC: 0.95)',
points=[LinePlotPoint(x=0.0, y=0.0), LinePlotPoint(x=0.1, y=0.8), LinePlotPoint(x=1.0, y=1.0)],
),
LinePlotCurve(
name='Random',
points=[LinePlotPoint(x=0, y=0), LinePlotPoint(x=1, y=1)],
style='dashed',
),
],
)
| 필드 | 유형 | 설명 |
|---|---|---|
title |
str |
표시 이름 |
x_label |
str |
x축 라벨 |
y_label |
str |
y축 라벨 |
x_range |
tuple[float, float] | None |
선택적 x축 고정 범위 |
y_range |
tuple[float, float] | None |
선택적 y축 고정 범위 |
curves |
list[LinePlotCurve] |
플롯할 하나 이상의 곡선 |
description |
str | None |
선택적 더 긴 설명 |
각 LinePlotCurve는 name, LinePlotPoint 목록(x, y 포함), 선택적 style('solid' 또는 'dashed'), 그리고 경험적 CDF 같은 계단 함수를 위한 선택적 step 보간 모드('start', 'middle', 또는 'end')를 담아요.
LinePlot은 커스텀 곡선 기반 평가자에 권장되는 반환 타입이에요. LinePlot을 반환하는 평가자는 프론트엔드 변경 없이 Logfire UI에서 꺾은선 차트로 렌더링돼요.
여러 분석 반환
단일 리포트 평가자는 리스트를 반환해 여러 분석을 만들 수 있어요:
from dataclasses import dataclass
from pydantic_evals.evaluators import ReportEvaluator, ReportEvaluatorContext
from pydantic_evals.reporting.analyses import ReportAnalysis, ScalarResult, TableResult
@dataclass
class ClassificationSummary(ReportEvaluator):
"""Produces both a scalar accuracy and a per-class metrics table."""
def evaluate(self, ctx: ReportEvaluatorContext) -> list[ReportAnalysis]:
cases = ctx.report.cases
if not cases:
return []
labels = sorted({str(c.expected_output) for c in cases if c.expected_output})
# Scalar: 전체 정확도
correct = sum(1 for c in cases if c.output == c.expected_output)
accuracy = ScalarResult(
title='Accuracy', value=correct / len(cases) * 100, unit='%'
)
# Table: 클래스별 분석
rows = []
for label in labels:
tp = sum(1 for c in cases if str(c.output) == label and str(c.expected_output) == label)
fp = sum(1 for c in cases if str(c.output) == label and str(c.expected_output) != label)
fn = sum(1 for c in cases if str(c.output) != label and str(c.expected_output) == label)
p = tp / (tp + fp) if (tp + fp) > 0 else 0.0
r = tp / (tp + fn) if (tp + fn) > 0 else 0.0
f1 = 2 * p * r / (p + r) if (p + r) > 0 else 0.0
rows.append([label, round(p, 3), round(r, 3), round(f1, 3)])
table = TableResult(
title='Per-Class Metrics',
columns=['Class', 'Precision', 'Recall', 'F1'],
rows=rows,
)
return [accuracy, table]
비동기 리포트 평가자
리포트 평가자는 비동기 evaluate 메서드를 지원하며, evaluate_async를 통해 자동으로 처리돼요:
from dataclasses import dataclass
from pydantic_evals.evaluators import ReportEvaluator, ReportEvaluatorContext
from pydantic_evals.reporting.analyses import ScalarResult
@dataclass
class AsyncAccuracy(ReportEvaluator):
async def evaluate(self, ctx: ReportEvaluatorContext) -> ScalarResult:
# 여기서 async I/O를 사용할 수 있음 (예: 외부 API 호출)
cases = ctx.report.cases
correct = sum(1 for c in cases if c.output == c.expected_output)
return ScalarResult(
title='Accuracy',
value=correct / len(cases) * 100 if cases else 0.0,
unit='%',
)
직렬화
리포트 평가자는 케이스 수준 평가자와 같은 YAML/JSON 형식을 사용하므로, 이들을 포함한 데이터셋은 직렬화로 왕복(round-trip)할 수 있어요.
리포트 평가자가 있는 YAML 데이터셋 예시:
# yaml-language-server: $schema=./test_cases_schema.json
name: classifier_eval
cases:
- name: cat_test
inputs: The cat meows
expected_output: cat
- name: dog_test
inputs: The dog barks
expected_output: dog
report_evaluators:
- ConfusionMatrixEvaluator
- PrecisionRecallEvaluator:
score_key: confidence
positive_from: assertions
positive_key: is_correct
내장 리포트 평가자(ConfusionMatrixEvaluator, PrecisionRecallEvaluator, ROCAUCEvaluator, KolmogorovSmirnovEvaluator)는 자동으로 인식돼요. 커스텀 리포트 평가자는 custom_report_evaluator_types로 전달해요:
from pydantic_evals import Dataset
dataset = Dataset[str, str, None].from_file(
'test_cases.yaml',
custom_report_evaluator_types=[MyCustomReportEvaluator],
)
마찬가지로 커스텀 리포트 평가자가 있는 데이터셋을 저장할 때는 JSON 스키마에 포함되도록 to_file에 전달해요:
dataset.to_file(
'test_cases.yaml',
custom_report_evaluator_types=[MyCustomReportEvaluator],
)
Logfire에서 analyses 보기
Logfire가 구성되면, analyses는 logfire.experiment.analyses 속성으로 실험 스팬에 자동 첨부돼요. Logfire UI는 이를 대화형 시각화로 렌더링해요:
- 혼동 행렬은 히트맵으로 표시돼요
- 정밀도-재현율 곡선은 범례에 AUC와 함께 꺾은선 차트로 렌더링돼요
- 라인 플롯(ROC 곡선, KS 플롯 등)은 축을 설정할 수 있는 꺾은선 차트로 렌더링돼요
- 스칼라 결과는 라벨이 붙은 값으로 표시돼요
- 테이블은 형식화된 데이터 테이블로 렌더링돼요
Logfire Evals 뷰에서 여러 실험을 비교할 때는 같은 유형의 analyses가 나란히 표시되어 비교가 쉬워요.
완전한 예시
케이스 수준 평가자와 리포트 평가자를 결합한 전체 예시:
from dataclasses import dataclass
from typing import Any
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import (
ConfusionMatrixEvaluator,
Evaluator,
EvaluatorContext,
KolmogorovSmirnovEvaluator,
PrecisionRecallEvaluator,
ReportEvaluator,
ReportEvaluatorContext,
ROCAUCEvaluator,
)
from pydantic_evals.reporting.analyses import ScalarResult
def my_classifier(text: str) -> str:
text = text.lower()
if 'cat' in text or 'meow' in text:
return 'cat'
elif 'dog' in text or 'bark' in text:
return 'dog'
elif 'bird' in text or 'chirp' in text:
return 'bird'
return 'unknown'
# 케이스 수준 평가자: 케이스마다 실행
@dataclass
class ConfidenceEvaluator(Evaluator):
def evaluate(self, ctx: EvaluatorContext) -> dict[str, Any]:
confidence = compute_confidence(ctx.output, ctx.inputs)
is_correct = ctx.output == ctx.expected_output
return {
'confidence': confidence,
'is_correct': is_correct,
}
def compute_confidence(output: str, inputs: str) -> float:
return 0.85 # 자리표시자
# 리포트 수준 평가자: 전체 보고서에 대해 한 번 실행
@dataclass
class AccuracyEvaluator(ReportEvaluator):
def evaluate(self, ctx: ReportEvaluatorContext) -> ScalarResult:
cases = ctx.report.cases
correct = sum(1 for c in cases if c.output == c.expected_output)
return ScalarResult(
title='Accuracy',
value=correct / len(cases) * 100 if cases else 0.0,
unit='%',
)
dataset = Dataset(
name='full_example',
cases=[
Case(inputs='The cat meows', expected_output='cat'),
Case(inputs='The dog barks', expected_output='dog'),
Case(inputs='A bird chirps', expected_output='bird'),
],
evaluators=[ConfidenceEvaluator()],
report_evaluators=[
ConfusionMatrixEvaluator(
predicted_from='output',
expected_from='expected_output',
title='Animal Classification',
),
PrecisionRecallEvaluator(
score_from='scores',
score_key='confidence',
positive_from='assertions',
positive_key='is_correct',
),
ROCAUCEvaluator(
score_from='scores',
score_key='confidence',
positive_from='assertions',
positive_key='is_correct',
),
KolmogorovSmirnovEvaluator(
score_from='scores',
score_key='confidence',
positive_from='assertions',
positive_key='is_correct',
),
AccuracyEvaluator(),
],
)
report = dataset.evaluate_sync(my_classifier)
# analyses에 프로그래밍 방식으로 접근
for analysis in report.analyses:
print(f'{analysis.type}: {analysis.title}')
#> confusion_matrix: Animal Classification
#> precision_recall: Precision-Recall Curve
#> scalar: Precision-Recall Curve AUC
#> line_plot: ROC Curve
#> scalar: ROC Curve AUC
#> line_plot: KS Plot
#> scalar: KS Statistic
#> scalar: Accuracy
다음 단계
- 내장 평가자 -- 케이스 수준 평가자 레퍼런스
- 커스텀 평가자 -- 케이스 수준 평가자 작성
- Logfire 통합 -- Logfire UI에서 analyses 보기
더 알아보기 (Learn more)
- Pydantic Evals 문서: 리포트 평가자