평가 하네스
laya.evals는 레이블이 붙은 데이터셋을 반복 가능한 점수로, 기준선을 통과/실패 게이트로 바꾸어,
품질 변화가 수작업 확인이 아니라 검토 가능한 diff가 되게 합니다.
지표 계산과 데이터셋 파서는 순수 Python과 numpy로만 되어 있고 torch를 임포트하지 않으므로 가중치 없이 실행됩니다. 데이터셋을 체크포인트에 대해 실행하려면 체크포인트가 필요하고 평소의 로드 시간이 걸립니다.
빠른 시작
# check the format without a model
laya-evals validate research/evals/fixture.jsonl
# score a labelled set on one checkpoint, with thresholds and a baseline
laya-evals run data.jsonl --model english --device cpu \
--min-accuracy 0.8 --max-ece 0.05 --score-within 0.25 --slice language \
--json report.json --markdown report.md
# compare a saved report to a baseline
laya-evals compare report.json --baseline baseline.json --tolerance choice_accuracy=0.02
laya eval ...은 메인 CLI를 통한 같은 기능이므로 laya eval validate data.jsonl도 동작합니다.
종료 코드: 성공 시 0, 임계값이나 기준선 허용 오차가 실패하면 1, 사용법 오류 시 2입니다.
run은 전체 지표와 요청된 슬라이스를 stdout에 출력하고, --json / --markdown이 주어지면 전체
보고서와 Markdown 요약을 씁니다.
숏리스트 오류 귀속
레이블이 붙은 고카디널리티 choice 집합에 대해, laya.evals_shortlist.evaluate_shortlist는 기존
predict_shortlist 경로와 일반 평가 하네스를 사용합니다. 이것은 두 가지 별개의 질문에 답합니다.
검색이 정답 레이블을 유지했는가, 그리고 그것이 존재할 때 Laya가 그것을 선택했는가? 이것은 choice
레이블을 위한 선택적(opt-in) Python API이며, 일반적인 laya-evals run 보고서는 바뀌지 않습니다.
import laya
from laya.evals import Dataset
from laya.evals_shortlist import evaluate_shortlist
from laya.shortlist import embed_fn_from_agent
agent = laya.load()
dataset_path = "intents.jsonl"
dataset = Dataset.from_jsonl(dataset_path)
report = evaluate_shortlist(
agent, dataset, embed_fn_from_agent(agent), k=20,
checkpoint_id="my-checkpoint@revision", embedder_id="my-encoder@revision",
dataset_path=dataset_path,
)
print(report.overall)
print(report.cases[0]["shortlist_status"])
측정 대상 배포와 같은 임베딩 함수와 체크포인트를 사용하십시오. 두 식별자는 호출자가 제공하며 변경
불가능한 리비전을 가리켜야 합니다. 보고서는 임의의 콜러블 뒤에 있는 가중치를 추론할 수 없습니다.
dataset_path는 기존 질문 지문과 함께 파일의 SHA256을 기록합니다. 각 케이스는 실제 숏리스트
레이블과 correct, retrieval_miss, decision_miss 중 하나를 유지합니다. shortlist_recall_at_k는
유지된 정답 레이블의 비율입니다. shortlist_accuracy_on_recalled는 유지된 케이스로 나눈 올바른
의사결정이며, 하나도 유지되지 않으면 생략됩니다. 기존 choice_accuracy는 검색 실패를 포함한 모든
케이스에 대한 종단 간 정확도로 남습니다. 숏리스트 지표는 동일한 언어, 모델, 질문, 태그 슬라이스에
나타납니다. 요청 지연 시간은 임베딩과 의사결정 호출을 포함하며, 보고서는 단계별 타이밍을 분리하지
않습니다. k >= n이면 원래 질문이 그대로 통과하고 임베더를 호출하지 않은 채 검색 재현율은 1입니다.
이것은 이슈 #102의 BANKING77 결과를 재현하지 않습니다. 그 수치들은 그 데이터셋, 체크포인트, 바이인코더에 의존합니다. 이 API는 같은 종류의 진단을 호출자 자신의 레이블 집합에서 반복 가능하게 만듭니다.
ONNX 익스포트 평가
run --onnx PATH는 torch Router 대신 ONNXAgent를 통해 익스포트된 ONNX 모델을 채점하므로, ONNX
배포(scripts/export_onnx.py --quantize로 만든 INT8 사본 포함)도 torch 경로와 같은 임계값과
기준선으로 게이팅됩니다:
python scripts/export_onnx.py --model convaiinnovations/laya --output laya.onnx --quantize
laya-evals run data.jsonl --onnx laya.int8.onnx --max-ece 0.05
--model은 익스포트가 나온 체크포인트를 지정합니다 —— Hub id나 로컬 경로이며, 이 경로에는 Router가
없으므로 english 같은 Router 단축 이름이 아닙니다(기본값 convaiinnovations/laya). 설정과
토크나이저는 그곳에서 로드됩니다. 에이전트는 체크포인트 하나를 서빙하므로, 데이터셋 행의 model
필드가 다른 것을 지정하면 잘못된 모델이 조용히 답하는 대신 명확한 오류로 실패합니다. --device는
적용되지 않습니다. --batch-size는 에이전트에 배치 API가 있으면 그것을 쓰고, 없으면 상태마다 한 번
호출하는 방식으로 폴백합니다. --sort-by-length는 그 배치 API로 전달되며, 상태별 폴백에는 재정렬할
그룹이 없습니다. --calibration PATH를 전달하면 적합된 캘리브레이션 맵을 ONNXAgent에
로드하므로, --max-ece 같은 캘리브레이션 게이트가 캘리브레이션된 확률에 대해 평가합니다. 보고서의
config 블록은 onnx 경로와 calibration 경로(설정된 경우)를 기록합니다.
research/evals/fixture.jsonl에서 측정(레이블 12행, 영어 체크포인트, CPU):
| runner | choice_acc | noul_acc | score_mae | ece | mean_conf | p50 ms |
|---|---|---|---|---|---|---|
| torch Router | 0.75 | 1.00 | 1.3418 | 0.1596 | 0.7304 | 116.8 |
--onnx fp32 |
0.75 | 1.00 | 1.3418 | 0.1596 | 0.7304 | 66.3 |
--onnx int8 |
0.75 | 1.00 | 1.3512 | 0.1658 | 0.7304 | 46.3 |
fp32 익스포트는 torch 수치를 정확히 재현하고, 양자화 사본은 score_mae를 0.009, ece를 0.006만큼
움직입니다 —— compare --tolerance가 게이팅하도록 만들어진 바로 그런 종류의 드리프트입니다.
데이터셋 형식
줄마다 JSON 객체 하나(JSONL)입니다. 빈 줄과 #로 시작하는 줄은 무시됩니다.
| 필드 | 필수 | 의미 |
|---|---|---|
state |
예 | 의사결정 대상인 텍스트, 이메일, 티켓 또는 JSON 문서 |
questions |
예 | Router.predict가 받는 그대로의 Laya 질문 딕셔너리 |
expected |
예 | 질문 id로 키가 잡힌 정답: choice는 레이블, score는 숫자, noul은 true/false |
tags |
아니오 | 슬라이스 기준이 될 문자열 |
language |
아니오 | 슬라이스 기준이 될 코드 |
model |
아니오 | 이 행에 체크포인트를 강제합니다. --model이 이를 덮어씁니다. 아무것도 강제하지 않은 행은 Router가 답한 체크포인트로 표시됩니다 |
research/evals/dataset.template.jsonl에 주석이 달린 예제가 있습니다.
지표
각 지표는 적용되는 답마다 계산되고 데이터셋 전체에 집계됩니다:
| 지표 | 적용 대상 | 의미 |
|---|---|---|
choice_accuracy |
choice |
선택한 레이블이 일치하는 비율 |
noul_accuracy |
noul |
불리언(확률 >= 0.5)이 일치하는 비율 |
score_mae |
score |
평균 절대 오차 |
score_within_<tol> |
score |
절대 허용 오차 이내인 비율 |
ece |
신뢰도가 있는 모든 답 | 기대 캘리브레이션 오차, 15개 구간, Laya가 모든 답 타입에 대해 보고하는 캘리브레이션된 확률인 answer["answer_confidence"]로 계산 |
brier |
신뢰도와 알려진 레이블이 있는 모든 답 | P(correct)로서의 신뢰도의 Brier 점수, mean((confidence - correct)**2); 낮을수록 좋음 |
aurc |
신뢰도와 알려진 레이블이 있는 모든 답 | 리스크-커버리지 곡선 아래의 면적: 서로 다른 신뢰도 수준마다 리스크 값 하나이며, 각각 그 수준이 포괄하는 답들로 가중됩니다. 낮을수록 좋고, 단지 캘리브레이션된 것이 아니라 옳고 그름을 순위 매기는 신뢰도에 보상을 줍니다 |
selective_accuracy@50, selective_accuracy@80 |
신뢰도와 알려진 레이블이 있는 모든 답 | 50% / 80% 커버리지 지점의 신뢰도 임계값이 받아들이는 답들에 대한 정확도 —— 가장 신뢰도가 낮은 꼬리를 포기함으로써 얻는 것입니다. 임계값은 같은 신뢰도의 그룹을 나눌 수 없으므로, 명시된 비율보다 더 많이 포괄할 수 있습니다. 커버리지 컷 참고 |
mean_confidence |
신뢰도가 있는 모든 답 | 보고된 answer["answer_confidence"]의 평균 |
latency_p50_ms, latency_p95_ms |
요청별 | 각 요청이 기다린 실제 시간, 정보 제공용 —— 배치 처리와 타이밍 참고 |
cost_per_decision_p50_ms, cost_per_decision_p95_ms |
의사결정별 | 호출의 실제 시간을 그것이 실어 나른 행 수로 나눈 값, 정보 제공용 |
커버리지 컷과 동점
두 커버리지 지표 모두 신뢰도 임계값에서 자릅니다. 임계값은 각 답을 자기 자신의 신뢰도에서
받아들입니다. 따라서 컷은 같은 신뢰도를 공유하는 답 그룹을 결코 나누지 못합니다. coverage * n이
그런 그룹 안에 떨어지면, 그 그룹의 모든 구성원이 받아들여집니다. 그 수치 뒤에 있는 답의 개수는
따라서 명시된 비율이 아니라 그룹의 위쪽 경계입니다 —— 신뢰도가 모두 같은 슬라이스에서의
selective_accuracy@50은 그 슬라이스의 더 나은 절반이 아니라 그 슬라이스 자체의 정확도입니다.
게이트가 출력하는 개수(규칙의 실패 메시지에서 n=)는 받아들여진 크기가 아니라 슬라이스의
크기이므로, 아주 넓은 그룹은 메시지만으로는 보이지 않습니다.
동점은 예외가 아니라 정상적인 경우입니다. 적합된 temperature는 버킷이 점 질량(point mass)을
보고하게 남겨둘 수 있습니다. 출시된 choice:11+에 대해 laya.common.answer_confidence가
기록하는 것이 바로 그것이며, 실제 체크포인트는 열두 개의 답 중 정확히 1.0에서 여섯 행 그룹을
만들어냈습니다. 대신 행 인덱스에서 자르면 두 지표 모두 데이터셋이 도착한 순서에 의존하게
되었습니다 —— 같은 행들을 섞으면 selective_accuracy@50이 0.000과 1.000 사이를 움직였습니다.
aurc는 서로 다른 각 수준마다 리스크 값 하나를 적분하며, 그 수준이 포괄하는 답들로 가중합니다.
따라서 크기가 고르지 않은 점들의 평균이 아니라 리스크-커버리지 곡선 아래의 면적으로 남습니다.
계획해 둘 만한 두 가지 결과가 있습니다:
- 숫자는 재정렬이 일으킬 수 있는 것보다 크게, 어느 방향으로든 움직일 수 있습니다. 그룹이 컷에 걸쳐 있으면, 임계값 판독은 같은 데이터의 모든 행 인덱스 판독과 다릅니다:
400,001개의 동점 형태 데이터셋에 걸쳐 측정했을 때
selective_accuracy@50은 최대 0.500,aurc는 0.351입니다. 위의 열두 답 형태에서 —— 여섯 개 정답, 모두 신뢰도 1.0 ——aurc는 0.327(0.173에서 0.500) 움직입니다. 통과하던 게이트가 실패할 수 있고, 실패하던 게이트가 통과할 수 있습니다. 이전 판정은 행 순서에 의존했으며, 이는 단일 실행을 읽기 때문에 아무것도 거부하지 않는 절대적min또는max한계의 경우도 마찬가지였습니다. - 커밋된 기준선을 다시 생성하십시오.
config.coverage_metric_definition은 어떤 정의가 보고서를 만들었는지 기록합니다. 서로 다른 두 정의에 걸쳐 커버리지 지표를 비교하는 것은 기준선을 빼는 양쪽 게이트에서 거부됩니다 ——--baseline --tolerance(EvalReport.compare)와--gate-policy아래의 상대 규칙(max_drop/max_increase) —— 그리고 기준선뿐 아니라 어느 한쪽이 오래된 경우에도 거부됩니다. 더 오래된laya가 만든 후보는 진실보다 더 좋게 읽힐 수 있는 행 순서 아티팩트를 지니므로, 올바르게 다시 생성된 기준선에 대해 게이팅하면 올바르게 채점된 보고서가 실패하는 회귀를 통과시킬 것입니다. 그런 거부가 없으면 오래된 기준선이 실제 회귀를 숨깁니다. 옛 정의에서 0.033으로 기록된 슬라이스가 이 정의에서는 0.517로 읽히므로, 실제로 0.217 하락한 후보가 0.05의max_drop을 통과할 것입니다.ece와brier는 자르지 않으며 비교 가능하게 남습니다.
데이터에 동점이 없으면 답마다 수준이 하나씩이고, 두 지표는 항상 그래 왔던 것과 정확히 같습니다 —— 단지 가까운 것이 아니라 비트 단위로 동일합니다.
허용 오차 지표를 위해서는 평가자 목록에 ScoreWithin(0.25)를 추가하십시오. 기본 집합은
choice_accuracy, noul_accuracy, score_mae, mean_confidence에 ece를 더한 것입니다. CLI에서는
같은 것이 플래그 하나입니다. laya-evals run data.jsonl --score-within 0.25는 기본값 옆에
score_within_0.25를 보고하며, 플래그는 반복할 수 있어 --score-within 0.25 --score-within 0.5는
둘 다 보고합니다.
허용 오차 지표는 숫자 레이블이 있는 score 답이 필요하므로, 그것이 없는 데이터셋에서는 값이
없습니다. run은 조용히 0을 게시하는 대신 계산할 수 없었던 지표를 이름으로 밝히고, 그 지표를
지정한 --min / --max 게이트는 결측으로 실패합니다. 실행이 요청받은 허용 오차는 보고서의 config
블록에 기록되므로, 검토된 기준선은 자기가 기대하는 열을 말해 줍니다.
배치 처리와 타이밍
--batch-size N은 체크포인트와 질문 스키마를 공유하는 연속된 최대 N개 행을 한 번의 호출로
채점합니다. 두 타이밍 지표는 같은 측정에서 나오지만 서로 다른 질문에 답합니다. 배치의 모든 행은
배치가 끝날 때 함께 반환되므로 그 latency는 호출 전체이고, cost_per_decision은 그것의
1/N입니다. 따라서 배치 처리는 바뀌지 않은 의사결정 집합에 대해 latency_*를 높이고
cost_per_decision_*을 낮춥니다. --max latency_p50_ms=...는 요청이 빠르게 서빙되었는지를 묻는
것이지 실행이 저렴했는지를 묻는 것이 아닙니다. --batch-size가 없으면 둘이 일치합니다.
compare는 허용 오차가 이름으로 지정하지 않는 한 모든 *_ms 지표를 무시하므로, 타이밍 잡음 때문에
기준선이 실패하는 일은 없습니다. 하네스가 실제로 한 일 —— 요청된 배치 크기, 그것이 결정한 러너 형태,
한 호출을 공유한 행 수, 가장 큰 청크 —— 은 보고서의 config.timing에 기록됩니다. 플래그만으로는
무엇이든 배치되었는지 알 수 없기 때문입니다. 이 카운터들은 반환된 호출이 아니라 발행된 호출을
기록합니다. laya-evals run --on-error skip이면 호출이 예외를 던진 청크도 config.errored의 항목 옆에
rows_grouped와 max_chunk로 계속 집계됩니다. 기본값은 --on-error fail이며, 이는 반환된 호출만을
포괄하는 지표를 가진 보고서를 게시하는 대신 예외를 다시 던집니다. 두 *_ms 지표는 반환된 호출만
세므로, 실패한 호출은 측정하지도 않은 지연 시간을 기여하지 않습니다.
배치 안에서 행 묶기
--sort-by-length는 비슷한 크기의 행들을 같은 포워드 패스로 묶어, 각 패스가 그 안에서 가장 긴
행이 아니라 더 짧은 최댓값까지 패딩되게 합니다. 이것은 호출의 형태이지 답이 아닙니다. 결과는 같은
순서로 돌아오고 동일하게 채점되며, 그래서 research/는 의사결정이 하나도 바뀌지 않은 채 티켓
10,000건에서 2.15배를 보고할 수 있습니다.
재정렬하려면 패스가 둘 이상이어야 하므로, 실행이 묶는 행 수보다 작은 --batch-size N이 있을 때만
효과가 있습니다. config.timing은 두 주장을 분리해 둡니다. sort_by_length는 명령줄이 말한 것이고,
sort_by_length_sent는 러너에 도달한 것입니다. --batch-size가 없는 실행은 일어날 수 없는 것을
요청하며 sent: false로 그렇게 말합니다. predict_batch가 이 기능보다 먼저 나온 러너는 긴 실행
도중에 TypeError를 내는 대신 정렬 없이 채점됩니다.
임계값에서의 기권 게이트
--min-confidence T는 코어의 선택적 기권 임계값(#361)을 실행이 하는 모든 호출로 전달하므로,
Router와 ONNXAgent는 answer_confidence가 T 미만인 답을 하네스가 보기 전에
low_confidence: True로 표시합니다. 묶기와 달리 이것은 채점되는 답을 바꿉니다. T=0과 T=0.7에서의
같은 실행은 서로 다른 실험이며, precision@coverage 스윕은 표류하는 단일 기준선이 아니라 이런
것들의 연속입니다.
허용되는 범위는 여기서 복사한 것이 아니라 코어의 laya.confidence.check_min_confidence입니다 ——
[0.0, 1.0], 유한하고, bool이 아닙니다. 따라서 게이트 자체가 거부할 값은 체크포인트가 로드되기 전에
사용법 오류(종료 2)로 실패합니다. 0.0은 합법적인 요청입니다. 그것은 precision@coverage 스윕의
대조군이며, 이를 떨어뜨리는 검사는 스윕 자체의 바닥을 숨길 것입니다.
predict가(배치 실행의 경우 predict_batch가) 이 게이트보다 먼저 나온 러너는 임계값 없이 채점되는
대신 명명된 EvalError로 거부됩니다. 채점 제어를 조용히 떨어뜨리는 것은 이 하네스가 방지하려고
존재하는 종류의 거짓말입니다. 보고서는 결코 실행되지 않은 정책에 대한 precision@coverage 수치를
게시하게 될 것입니다. config.timing은 요청과 사실을 모두 기록합니다. min_confidence는 요청된
임계값이고, min_confidence_sent는 이 실행이 한 어떤 호출이 실제로 그것을 실었는지 말합니다.
슬라이스
compare와 run은 전체 수치를 보고하고, --slice language|model|qid|tag에 대해서는 슬라이스
값별로 같은 지표를 보고합니다. 따라서 한 언어나 한 질문의 회귀가 집계치를 읽지 않고도 드러납니다.
model 슬라이스는 각 행에 답한 체크포인트를 담습니다. 요청별 Router 자체의 선택, 또는 라우팅하지
않는 러너의 경우 러너의 model입니다.
선택적 슬라이스 게이트
전체 기준선 게이트는 통과하면서 더 작은 언어나 질문 슬라이스가 회귀할 수 있습니다. 검토된 슬라이스
하나를 CI 요구 사항으로 만들려면, gates.json 같은 JSON 정책을 저장하십시오:
{
"version": 1,
"rules": [
{"slice": {"language": "zh"}, "metric": "choice_accuracy",
"min_count": 50, "max_drop": 0.05},
{"slice": {"qid": "intent"}, "metric": "ece",
"min_count": 50, "max": 0.10}
]
}
laya-evals run data.jsonl --baseline baseline.json --tolerance choice_accuracy=0.02 \
--gate-policy gates.json --json report.json
laya-evals compare report.json --baseline baseline.json \
--tolerance choice_accuracy=0.02 --gate-policy gates.json
각 규칙은 language, model, qid, tag 값 중 정확히 하나를 선택하고, 슬라이스 보고서에
나타나는 그대로 지표를 지정합니다. 규칙에는 양의 min_count와 정확히 하나의 한계가 있습니다.
min 또는 max는 후보 값을 검사하고, max_drop은 기준선으로부터의 그 감소를 최대 허용하며,
max_increase는 그 증가를 최대 허용합니다. 뒤의 둘은 --baseline이 필요합니다. 개수는 선택된
슬라이스에서 그 지표에 대해 채점된 답의 수이며, 상대 규칙의 경우 양쪽 보고서 모두에서입니다.
ece의 경우, 유한한 신뢰도와 불리언 correct 값을 가진 답의 수입니다. 슬라이스나 지표가 없거나,
채점된 답이 너무 적거나, 건너뛴/오류 케이스가 있으면 선택된 게이트가 실패합니다. 상대 규칙은 또한
두 보고서가 일치하는 실행 정체성을 지닐 것을 요구하므로, 증거 부족이 통과로 나타날 수 없습니다.
측정된 회귀는 슬라이스, 지표, 개수, 값, 한계를 보고합니다. 잘못된 정책 구문은 체크포인트가
로드되기 전에 종료 2로, 품질 실패는 종료 1로 끝납니다. 정책은 run --json 보고서의
config.gate_policy에 기록됩니다. compare --gate-policy는 그 명령줄에서 제공된 정책을 저장된
측정에 적용합니다. 보고서에 기록된 정책과 다르면 compare가 그렇게 말합니다. 새 정책 아래의
명시적 재검사는 원래 실행이 이루어진 정책을 바꾸지 않습니다.
일반적인 전체 비교는 여전히 적용되며, 그 허용 오차와 레거시 기준선 동작을 포함합니다.
--gate-policy가 없으면 슬라이스 보고와 비교는 이전과 같이 동작합니다.
실행 정체성
run은 측정한 것을 보고서의 config 블록에 기록하므로, 검토자가 읽는 산출물만으로 검토가
가능합니다:
| 키 | 의미 |
|---|---|
schema |
보고서 형태, laya-evals-report/1이며, 소비자가 읽을 수 없는 것을 거부할 수 있게 합니다 |
dataset |
입력한 그대로의 경로 —— 이름이지 해시가 아닙니다 |
dataset_sha256 |
파싱된 데이터셋 바이트의 sha256 |
questions_sha256 |
질문 스키마의 지문: 데이터셋 전체에 걸친 모든 질문의 id, type, instructions, criteria |
laya_version |
수치를 계산한 laya |
coverage_metric_definition |
이 보고서를 만든 aurc / selective_accuracy@*의 정의 (커버리지 컷 참고). 둘 중 하나에 대한 상대 게이트 규칙은 같은 것을 의미하지 않는 숫자들을 빼는 대신, 다른 정의 아래 기록된 기준선을 거부합니다 |
gate_policy |
run --gate-policy가 적용한 선택적 슬라이스 게이트 정책 |
thresholds |
이 실행이 적용한 게이트: min, max, baseline_tolerance |
revisions |
답한 각 체크포인트가 로드된 커밋 (아래 참고) |
dataset은 경로이고, 경로는 정체성이 아닙니다. 데이터셋은 제자리에서 편집되거나, 옮겨지거나, 같은
이름으로 다시 받아질 수 있고, CI 캐시는 두 실행에 같은 파일 이름과 다른 바이트를 건넬 수 있습니다.
questions_sha256은 행이 몇 개였는지가 아니라 무엇을 물었는지를 포괄하므로, 바뀌지 않은 질문
집합에 상태를 추가해도 지문은 그대로입니다 —— dataset_sha256은 여전히 움직이며, 행을 추가하는
것은 질문이 아니라 데이터의 변경입니다.
instructions도 포괄합니다. instruction 텍스트가 곧 프롬프트이기 때문입니다. build_sequence는
"<type> question: <instructions>"를 토큰화된 head로 렌더링하고, Agent는 이것이 없는 질문을
거부하며(“모델이 답해야 할 텍스트를 추가하십시오”), Laya 자체의 질문 정체성도 이미 이것을 셉니다.
Router._question_schema와 이 하네스의 배치 묶기가 모두 전체 questions 딕셔너리를 키로 삼고,
tests/test_router_batch.py는 instructions만 다시 써도 행이 자기만의 배치 그룹으로 옮겨진다고
고정합니다. 그렇다면 다시 쓴 instruction은 여전히 기준선과 동등하게 비교될까요? 아닙니다 —— 그리고
그것이 요점입니다. “환불이 정당한지 판단하라”와 “보수적으로 행동하고 명시적인 환불 요청만
승인하라”는 서로 다른 질문이며, 지표 게이트는 그 차이가 우연히 당신이 지정한 허용 오차보다 더 큰
숫자 이동을 일으킬 때만 알아챌 수 있습니다. choice 선택지에 이름을 붙이는 것도 같은 논리입니다.
criteria는 의사결정 공간이고, research/eval/metamorphic.py의 변형 검사는 레이블 이름을 바꾸면
답이 뒤집히기 때문에 존재합니다.
instruction 텍스트에 대해 정규화되는 것은 엔진 자체가 적용하는 한 단계뿐입니다. 문자열이 아닌
instructions는 json.dumps(ins, ensure_ascii=False)로 해싱되며, 이는 Agent._to_internal과
일치합니다. 따라서 공백과 문구가 모두 계산에 들어가고, 사람이 교정으로 여기는 문구 변경도 새
실험으로 취급됩니다. 이것이 정직한 기본값입니다 —— 대안은 실행과 기준선 사이에 유사도 휴리스틱을
세우는 것이고, 널리 쓰이는 평가 시스템 중에 그런 것을 가진 것은 없습니다.
시간에 관한 것은 아무것도 기록되지 않으므로, 고정된 러너에 대해 보고서는 여전히 바이트 단위로 재현 가능합니다.
REPORT_SCHEMA, questions_fingerprint(dataset), file_fingerprint(path)는 공개되어 있으므로,
laya.evals.evaluate를 직접 구동하는 호출자도 CLI 실행과 같은 정체성을 얻습니다.
기준선과 CI 게이트
- 데이터셋, 기준선 보고서(검토한
--json출력), 허용 오차를 함께 커밋해 두면 변경이 검토 가능한 diff가 됩니다.--tolerance METRIC=VALUE는 그 지표에 허용되는 최대 절대 드리프트입니다. laya-evals run ... --baseline baseline.json --tolerance ...는 드리프트가 있으면 0이 아닌 코드로 종료하므로 CI에 그대로 들어갑니다.laya.evals.EvalReport.compare와assert_regression은 테스트를 위해 같은 로직을 노출합니다.
지표 게이트는 “숫자가 움직였는가”에 답합니다. “이 숫자들이 같은 숫자였는가”에는 답할 수 없습니다.
compare가 overall만, 오직 overall만 읽기 때문입니다 —— 그래서 한 데이터셋에 대해 기록한 기준선이 다른
데이터셋에서 채점한 후보를 통과시킬 수 있고, 산술은 동일합니다. EvalReport.comparable_to가 이것을
막습니다. schema, dataset_sha256, questions_sha256을 비교하며, run --baseline과 compare는
여전히 모든 델타를 출력한 뒤 키와 두 값을 밝히며 0이 아닌 코드로 실패합니다:
FAIL: baseline is not comparable: dataset_sha256 (dataset bytes): baseline is <sha>, this run is <sha>
한쪽에 없는 키는 충돌이 아니라 알 수 없음이므로, 이 정체성이 생기기 전에 작성된 모든 보고서는
이전과 똑같이 비교됩니다. 아래의 예약 게이트 기준선도 여기 포함되며, 이는 research/eval/에서 오고
config.schema가 전혀 없습니다.
두 CI 표면이 이것을 사용합니다:
.github/workflows/ci.yml의 가중치 없는 잡이tests/test_evals.py와tests/test_evals_api.py를 실행하므로, 체크포인트를 내려받지 않고도 모든 PR에서 지표 계산, 데이터셋 파싱, CLI가 커버됩니다..github/workflows/evals.yml은 매주, 릴리스 전, 그리고 요청 시 실행됩니다. 영어 체크포인트를 MASSIVE 영어 스위트에서 평가하고research/evals/thresholds.json의 허용 오차로research/results/eval_english_51_languages.json과 비교합니다. 보고서를 아티팩트로 업로드하며 PR을 막지 않습니다.
하네스는 고정된 체크포인트 리비전에 대해 결정론적이므로 보고서는 재현 가능합니다. run은 데이터셋,
모델, 장치와 함께 실행의 타이밍 사실을 보고서의 config 블록에 기록하고,
revisions에는 답한 각 체크포인트가 실제로 로드된 커밋을 기록합니다. --revision <SHA>는 실행이
로드하는 모든 체크포인트에 대해 그 커밋을 고정하고, --revision english=<SHA>는 체크포인트 하나를
고정합니다(반복 가능). 세 체크포인트가 세 저장소이고 하나의 커밋이 셋 모두에 존재할 수 없으므로,
이것이 자동 라우팅 실행이 원하는 형태입니다. 고정하지 않으면 실행은 체크포인트의 기본 브랜치를
취하고, 보고서는 여전히 어느 커밋이 답했는지 말하므로, 기준선 드리프트를 가중치 탓인지 코드 탓인지
귀속시킬 수 있습니다. laya/revisions.py는 선택적으로 쓰려는 호출자를 위해 PINNED_REVISIONS에
검토된 커밋 SHA를 게시합니다. --onnx에서는 맨 --revision <SHA>만 설정과 토크나이저 다운로드에
적용됩니다.
실제 레이블 데이터셋 추가
research/evals/에 JSONL을 넣고 그 옆에 검토된 기준선을 두고, 워크플로(또는
research/evals/check_regression.py)가 둘 다를 가리키게 하십시오. 형식은 fixture와 같으며, 하네스의
어느 부분도 MASSIVE에 대해 알지 못합니다.