메트릭과 평가
메트릭과 평가 (Metrics and evaluation)
DSPy 메트릭은 예측을 최적화기가 쫓을 수 있는 숫자로 바꾸는 함수이고, dspy.Evaluate 는 그 메트릭을 데이터셋 전체에 걸쳐 병렬로 실행하는 하네스(harness)입니다. 이 페이지는 최적화기가 기대하는 메트릭 계약, DSPy에 내장된 문자열·LLM 심사위원 메트릭, 그리고 Evaluate 가 채점 실행을 조율하는 방식을 다룹니다.
출처: 문서
본문
자신의 메트릭을 작성하거나, 규칙 기반 메트릭을 LLM-as-judge로 바꿔 끼우거나, Evaluate 의 옵션을 고르거나, 최적화기가 왜 당신의 메트릭을 그렇게 읽는지 문제 해결을 할 때 이 문서를 읽으세요.
설계 결정
1. 메트릭은 (gold, pred, ...) 를 받아 점수를 반환하는 그 어떤 callable이든 된다
계약은 덕 타이핑(duck-typed)입니다. 서브클래싱할 기본 클래스도, 메트릭을 등록하는 데코레이터도, @metric 마커도 없어요. Evaluate 는 당신이 넘긴 무엇이든 호출하고, 최적화기도 마찬가지입니다. 장점: NLP나 QA 라이브러리의 기존 채점 함수를 한 줄로 DSPy 메트릭으로 감쌀 수 있어요. 대가: 계약이 강제되지 않는다는 것입니다 — 잘못된 타입을 반환하는 callable은 등록 시점이 아니라 평가 시점에 실패합니다.
2. 최적화기-대면 전체 시그니처는 (gold, pred, trace=None, pred_name=None, pred_trace=None) 이다
대부분의 메트릭은 (gold, pred) 만 선언하며, Python의 기본 인자 규칙 덕분에 최적화기가 나머지를 무해하게 넘길 수 있어요. 최적화기는 프로그램이 답에 도달한 방법을 메트릭이 보길 원할 때 trace 를 채우고, predictor별 채점을 원할 때 pred_name/pred_trace 를 채웁니다(GEPA는 둘 다 합니다). 오늘 (gold, pred) 에 맞춰 쓴 메트릭은 미래의 최적화기가 더 많은 것을 원해도 계속 동작합니다 — 그래서 추가 인자들이 꼬리 위치 기본값인 것이에요.
3. 반환 타입은 bool, float, 또는 dspy.Prediction(score, feedback) 일 수 있고, 각각 다르게 처리된다
Evaluate 는 불리언을 백분율로, float를 평균으로 집계합니다. Prediction 반환은 언팩됩니다. score 는 float처럼 평균되고, feedback 은 GEPA만 읽습니다. 세 가지 모양의 반환이 중요한 이유는, 가장 단순한 메트릭이 출력을 의례적으로 Prediction 으로 감싸지 않아도 되면서도, 더 풍부한 신호가 필요한 최적화기가 잠기지 않도록 하기 위함이에요.
4. feedback 채널은 Evaluate 의 점수가 아니라 GEPA를 위한 것이다
메트릭은 모든 점수에 자연어 설명을 붙일 수 있고, 그것을 읽는 것은 GEPA의 반성적 루프뿐입니다. Evaluate 는 무시해요. 그 분리는 두 관심사를 깔끔하게 유지합니다. "이 예측이 얼마나 좋았는가"는 하나의 신호이고, "유용한 비판은 무엇인가"는 다른 신호이며, 최적화기가 듣지 않을 때 생략해도 아무 손해가 없습니다.
5. trace 는 메트릭이 최적화기 안에서 한 방식, 평가 시점에 다른 방식으로 동작하게 하는 장치다
SemanticF1 과 CompleteAndGrounded 심사위원은 trace 가 None 일 때(평가) 원시 F1을 반환하고, trace 가 None 이 아닐 때(최적화) score >= threshold 로 이진화된 값을 반환합니다. 패턴: 평가 시점에는 연속 점수를, 최적화 시점에는 탐색이 쫓을 수 있는 깔끔한 통과/실패 신호를 원하는 것이에요. trace 인자는 두 개의 별도 함수를 강제하지 않고 메트릭이 어느 모드인지 알려 줍니다.
6. 내장 문자열 메트릭은 normalize_text 에 집중된다
EM, F1, HotPotF1, 그리고 메트릭-모양 래퍼들은 모두 예측과 참조 둘 다에 normalize_text 를 호출합니다. NFD 유니코드, 소문자, 영어 관사(a/an/the) 제거, 구두점 제거, 공백 축약. 하나의 정식 정규화가 토큰 수준 F1과 정확 일치가 동일한 입력에 대해 동작하게 하며, answer_exact_match 가 frac 인자에 따라 그것들 사이를 전환하므로 중요합니다.
7. LLM 심사위원은 평범한 함수가 아니라 dspy.Module 서브클래스다
SemanticF1 과 CompleteAndGrounded 는 dspy.Module 을 서브클래싱해서, 다른 DSPy 프로그램처럼 검사·트레이싱·최적화될 수 있어요. 심사위원을 dspy.Predict 주변의 자유 함수로 쓸 수도 있지만, Module이 공짜로 주는 트레이스와 히스토리를 잃게 됩니다. 패턴: score: float(그리고 선택적으로 feedback: str)를 출력에 포함한 dspy.Signature 를 쓰고, forward 가 Prediction(score, feedback) 을 반환하는 Module로 감싸세요.
8. Evaluate 는 자체 스레드 풀이 아니라 ParallelExecutor 를 통해 병렬화한다
dspy.Parallel 과 Module.batch 가 쓰는 것과 같은 실행자입니다. 장점: 설정 전파, 오류 집계, 지연(straggler) 감지, 진행 보고가 하나의 구현입니다. Evaluate 호출을 둘러싼 dspy.context(lm=...) 블록은 ParallelExecutor 가 thread_local_overrides 를 스냅샷하고 다시 적용하므로 모든 워커 안에서 존중됩니다.
9. 메트릭 실패는 예외가 아니라 failure_score 를 얻는다
메트릭이 예외를 던지거나(None 반환) 하면 Evaluate 는 그 예시에 failure_score(기본 0.0)를 대신하고 계속합니다. 이유: 긴 평가에서 나쁜 예시 하나가 다른 999개의 점수를 잃게 만들면 안 되니까요. max_errors 옵션이 하네스가 멈추기 전에 견딜 실패 수를 제한합니다.
10. EvaluationResult 가 단일 반환 형태다
.score 는 집계 값(불리언은 True 비율, float와 Prediction.score 는 평균)이고, .results 는 (example, prediction, score) 삼중항들의 리스트입니다. 기존 return_outputs/return_all_scores 생성자 kwargs는 제거되었고, 넘기면 마이그레이션 메시지와 함께 ValueError 가 발생합니다. 하나의 반환 형태라 하류 코드가 호출자가 어떤 키워드를 썼는지에 따라 분기할 필요가 없습니다.
API 살펴보기
하려는 일에 따라 묶었어요.
메트릭 구조
DSPy가 메트릭을 어떻게 호출하고 무엇을 기대하는지.
호출 시그니처 — metric(gold, pred, trace=None, pred_name=None, pred_trace=None)
gold 는 레이블된 dspy.Example 이고, pred 는 프로그램이 만든 dspy.Prediction 입니다. trace 는 실행 전체를 아우르는 (predictor, inputs, outputs) 삼중항들의 리스트로, 최적화기가 프로그램이 어떻게 도달했는지 메트릭이 보길 원할 때 채워집니다. pred_name 과 pred_trace 는 같은 뷰를 predictor 하나로 좁히며, GEPA는 다단계 프로그램의 단일 단계를 채점할 때 이것들을 씁니다.
dspy.Prediction(score, feedback) — 점수와 피드백 반환 형태
최소한 score 필드가 있고, 선택적으로 feedback 필드가 있는 Prediction 입니다. Evaluate 는 score 만 읽고, GEPA는 둘 다 읽습니다. 평범한 메트릭에서 Prediction(score=...) 을 반환해도 잘 동작합니다. feedback 은 선택이고, 점수만 원할 때 래퍼는 거의 아무 비용이 없어요.
문자열·토큰용 내장 메트릭
dspy/evaluate/metrics.py 의 규칙 기반 메트릭 표준 라이브러리.
dspy.evaluate.normalize_text(s: str) → str
NFD 유니코드, 소문자, 영어 관사 a/an/the 제거, 구두점 제거, 공백 축약. 아래의 모든 문자열 비교 메트릭이 공유하는 단일 정식 정규화입니다.
dspy.evaluate.EM(prediction, answers_list) → bool
양쪽에 normalize_text 를 적용한 뒤의 정확 일치(exact match). answers_list 의 어떤 참조와 일치하면 True. answers_list 가 리스트가 아니면 예외를 던집니다. 가장 작은 메트릭 기본 요소로, 커스텀 래퍼의 구성 요소로 유용합니다.
dspy.evaluate.answer_exact_match(example, pred, trace=None, frac=1.0) → bool
메트릭-모양 래퍼입니다. pred.answer 와 example.answer 를 읽고 둘 다 정규화한 뒤, frac >= 1.0 일 때 EM 으로, frac < 1.0 일 때 F1-임계값 일치로 분기합니다. example.answer 가 단일 문자열이거나 허용 참조 리스트인 경우를 처리합니다.
dspy.evaluate.answer_passage_match(example, pred, trace=None) → bool
검색 평가입니다. pred.context 의 어떤 문단이 example.answer 의 어떤 참조를 포함하면 True. 문단에는 더 많은 텍스트를 보존하는 DPR 스타일 정규화기를 쓰면서, 답에는 여전히 normalize_text 를 사용합니다.
F1 / HotPotF1 — 토큰 수준 채점기(내부)
F1 은 정규화된 문자열에 대한 토큰 수준 F1을 계산하며 참조들 중 최대를 고릅니다. HotPotF1 은 HotPotQA 고유 규칙 하나를 더합니다. 정규화된 양쪽이 yes/no/noanswer 이고 둘이 다르면 0 을 반환합니다. 둘 다 answer_exact_match 에 공급되며 직접 호출은 드뭅니다.
LLM-as-judge 메트릭
규칙 기반 채점이 올바른 정확성 개념을 잡지 못하는 작업을 위한 것입니다. 둘 다 dspy/evaluate/auto_evaluation.py 에 있고 dspy.Module 서브클래스입니다.
dspy.evaluate.SemanticF1(threshold=0.66, decompositional=False)
Module입니다. forward(example, pred, trace=None) 는 SemanticRecallPrecision(또는 분해적 변형) 위에서 ChainOfThought 를 실행하고 example.response 와 pred.response 에 대한 precision 과 recall 을 LM에게 요청합니다. f1_score(precision, recall) 를 계산합니다. 평가 시점에는 Prediction(score=f1) 를, 최적화 시점에는 Prediction(score=(f1 >= threshold)) 를 반환하며, 같은 인스턴스가 두 모드를 모두 제공합니다.
dspy.evaluate.CompleteAndGrounded(threshold=0.66)
두 번의 ChainOfThought 호출을 실행합니다. 하나는 완전성(completeness, pred.response 가 example.response 를 커버하는가), 하나는 근거성(groundedness, pred.response 가 pred.context 에 뒷받침되는가)입니다. SemanticF1 과 같은 트레이스 기반 이진화로 Prediction(score=f1_score(groundedness, completeness)) 를 반환합니다. 검색 증강 프로그램용으로 설계되었습니다.
일반 LLM-judge 패턴. 출력에 score: float(그리고 선택적으로 feedback: str)를 포함하는 dspy.Signature 를 쓰세요. 그것을 forward(example, pred, trace=None) 가 judge predictor를 호출하고 Prediction(score, feedback) 을 반환하는 dspy.Module 로 감싸세요. 최적화 안에서 다른 동작을 원할 때 trace is None 토글을 사용하세요. 그것이 전부입니다 — 두 내장 judge는 이 모양 위에 있는 20줄짜리 모듈입니다.
Evaluate 하네스
프로그램 + 메트릭 + 데이터셋을 받아 점수를 만드는 실행자.
dspy.Evaluate(*, devset, metric=None, num_threads=None, display_progress=False, display_table=False, max_errors=None, provide_traceback=None, failure_score=0.0, save_as_csv=None, save_as_json=None)
키워드 전용 생성자입니다. metric 은 여기 또는 호출 시점에 넘길 수 있어요. display_table=N 은 표시되는 DataFrame을 N 행으로 자르고, display_table=True 는 전체를 보여주며, False 는 아무것도 보여주지 않습니다. save_as_csv/save_as_json 은 예시별 결과를 디스크에 써서 나중에 검사하게 합니다. 제거된 return_outputs kwargs를 넘기면 ValueError 가 발생합니다.
Evaluate.__call__(program, metric=None, devset=None, ...) → EvaluationResult
(program, example) 쌍을 ParallelExecutor 에 제출합니다. 각 워커는 부모의 thread_local_overrides 를 다시 적용하고 program(**example.inputs()) 를 실행한 뒤 (example, prediction) 으로 메트릭을 호출합니다. 집계 값이 .score 가 됩니다. 불리언 반환은 True 비율, float 반환은 평균, Prediction 반환은 score 의 평균. 예시별 결과는 .results 에 (example, prediction, score) 삼중항으로 담깁니다.
dspy.evaluate.EvaluationResult
.score(집계)와 .results(삼중항 리스트) 두 필드를 가진 dspy.Prediction 서브클래스입니다. repr 은 전체 리스트를 덤프하지 않고 점수와 결과 수를 보여주므로, EvaluationResult 를 기록해도 수 메가바이트의 출력을 찍지 않습니다.
크로스링크
- 설정과
context()—Evaluate의 워커가 호출을 둘러싼dspy.context(...)오버라이드를 어떻게 상속하는지. - 내장 모듈 변형들 —
dspy.Parallel과Module.batch가 내부에서 같은ParallelExecutor를 쓰는지. - 최적화기: 하나 고르기 — 모든 최적화기가 여기에 정의된 메트릭에 맞춰 컴파일하며, GEPA가 기대하는
Prediction(score, feedback)형태는 위에 문서화되어 있음.