Thomas Piacentini
← Atelier

Fiche

Evaluating LLMs

Texte original · anglais

Specify tasks, score them, judge with a rubric, calibrate, rank with intervals and test long context.

End-to-end eval runner

rendertextcode_execscorescoreconfidencereporttasks.jsonlvalidate_fileModelAdaptergenerate(prompt, task)post_processone rule per taskscoremetric_name dispatchCalibrationReportece, brieraggregateJSON + markdownEvalRunone per model, taskrun_candidatesubprocess, 3 s
Nodes and connections
NodeRoleKind
tasks.jsonlvalidate_file
ModelAdaptergenerate(prompt, task)
post_processone rule per task
scoremetric_name dispatch
CalibrationReportece, brier
aggregateJSON + markdown
EvalRunone per model, task
run_candidatesubprocess, 3 s
FromToConnection
tasks.jsonlModelAdapterrender
ModelAdapterpost_processtext
post_processscore
post_processrun_candidatecode_exec
scoreEvalRunscore
run_candidateEvalRunscore
ModelAdapterCalibrationReportconfidence
EvalRunaggregate
CalibrationReportaggregatereport
Sources

Score one task

  1. Render few-shot examples, then the prompt, with a blank line between.
  2. Adapter generate(prompt, task) returns text and confidence. The runner times it.
  3. post_process applies the single rule of the task.
  4. Dispatch score for text metrics, run_candidate for code_exec.
  5. EvalRun one record with the score and the metric name.
  6. Calibration buffer appends the (confidence, correct) pair.
  7. Correct score of 1 for exact_match, accuracy, code_exec. At least 0.5 for graded metrics.
Sources

Pick and run a metric

TaskCode
Score arithmetic"exact_match" with "strip_whitespace"
Score multiple choice"exact_match" with "extract_letter"
Score generated code"code_exec" with "extract_code_block"
Score a summary"rouge_l" or "bleu_4"
Interval on a pass ratewilson_confidence_interval(successes, total, z=1.96)
Rank by pairwise winsELOTracker(k=32, initial_rating=1500)
Plant a needlebuild_haystack(filler_text, needle, depth_ratio, total_tokens)
Run a public benchmarklm_eval --model hf --model_args pretrained=<model> --tasks mmlu
Sources

Harness API

tasks, errors = validate_file("tasks.jsonl")do not run if errors
s = score("f1", prediction, targets)best F1 over the targets
s = score_code_exec(prediction, assertions)passed / total, 3 s limit
p = pass_at_k(n=10, c=3, k=5)unbiased estimator
report = CalibrationReport.from_predictions(confidences, correct)
report.ece, report.brier, report.populated_binsbins=10 by default
ppl = perplexity(neg_log_probs, token_counts)exp(total nll / tokens)
rows = aggregate(runs, b=500, alpha=0.05)mean, bootstrap CI, win rate
print(render_markdown(rows))table for a PR comment
Sources

Post-process rules

CommandWhat it does
noneReturns the string unchanged
strip_whitespaceStrips leading and trailing whitespace
lowerLowercases the string
extract_letterFirst character in [A-E]. For MCQ
extract_code_blockBody of the first fenced block. For code
extract_first_lineFirst non-empty line. For summary classes
Sources

Key formulas

Perplexity
\mathrm{PPL} = \exp\left(-\frac{1}{N} \sum_i \log P(\text{token}_i \mid \text{context})\right)
Token F1
\frac{2 P R}{P + R}. P and R are token overlap over prediction and target.
BLEU-4
brevity penalty times the geometric mean of p_1 to p_4.
Brevity penalty
1 if c \ge r, else \exp(1 - r / c).
ROUGE-L
LCS over reference length and over candidate length, then F1.
pass@k
1 - \binom{n - c}{k} / \binom{n}{k} for c passes in n samples.
ECE
confidence minus accuracy per bin, weighted by bin size, summed.
Brier
\mathrm{mean}((p_i - y_i)^2). Penalizes spread that ECE misses.
Sources

Sample size and confidence

Test cases at 90%95% CI width
5019 points. Cannot detect a 5% regression
10012 points. Barely
2009 points. Yes
5005 points. Confidently
10003 points. Precisely
Sources

Defaults in the code

SettingDefaultNote
DEFAULT_TIMEOUT_S3.0Per task. MAX_TIMEOUT_S is 30.0
OUTPUT_CAP_BYTES256 * 1024Then the result is "output overflow"
FEW_SHOT_MAX8Few-shot examples per task
ECE bins1015 is the comparison convention
Bootstrap b500Low-level helpers use 1000
alpha0.0595% percentile interval
max_workers8Thread pool in run_eval
ELO k32Start rating 1500
Sources

Troubleshooting

SymptomCauseFix
Perplexity below 1Adapter returns log p, not -log pNegate. The function rejects it
Perplexity too goodPadding counted in the denominatorCount only non-pad tokens
Judge scores go missingBad JSON gives NaN, mean drops itCatch it as an explicit failure
Judge rates its own model highSelf-evaluation adds 10-20%Judge with a different model family
Pairwise judge picks the firstPosition biasRandomize order and run both
Mean 0.85, users complainMean hides 5% catastrophic failuresInspect the bottom quantile
Passes NIAH, fails real docsNIAH tests retrieval onlyAdd RULER or a multi-hop test
One task dominates the meanScore in [0, 100]Return fractions in [0, 1]
Sources

Abbreviations

EM
Exact Match. 1 or 0 per task after normalization.
ECE
Expected Calibration Error. Gap between confidence and accuracy.
MMLU
Massive Multitask Language Understanding. 15,908 questions, 57 subjects.
HumanEval
164 Python function-completion problems.
SWE-bench
2,294 GitHub issues from 12 Python repos.
NIAH
Needle in a Haystack. Plant a fact in filler, ask for it.
MRCR
Multi-Round Coreference Resolution. 8, 24 or 100 needles.
RAGAS
Retrieval-Augmented Generation ASsessment. Four RAG metrics.
Sources

Questions

How many test cases?
At least 200 for a deploy decision. 500 or more for close systems.
Why report pass@1 too?
pass@k hides a model that is right once in twenty tries.
When is a difference real?
When the bootstrap interval of the paired difference excludes zero.
When can I trust a judge?
Spearman rho against human labels of 0.7 or more.
How long is the real context?
Reasoning-effective is often 25-50% of the advertised window.
Sources

Tips

  • Anchor each rubric score to observable behavior. Judge variance drops 30-40%.
  • Freeze the judge model and its version.
  • Pin task files by hash in the leaderboard.
  • Diff predictions, not only scores. Use --include-per-example.
  • Keep eval cases apart from prompt and training examples.
  • Test needle depths 0, 0.25, 0.5, 0.75 and 1.0.
Sources
Original source
---
id: llm-evaluation
title: Evaluating LLMs
code: EL
subtitle: Specify tasks, score them, judge with a rubric, calibrate, rank with intervals and test long context.
hue: 80
sources: phases/10-llms-from-scratch/10-evaluation, phases/11-llm-engineering/10-evaluation, phases/05-nlp-foundations-to-advanced/27-llm-evaluation-frameworks, phases/05-nlp-foundations-to-advanced/28-long-context-evaluation, phases/19-capstone-projects/27-eval-harness-fixture-tasks, phases/19-capstone-projects/41-eval-pipeline, phases/19-capstone-projects/49-lm-eval-harness, phases/19-capstone-projects/70-task-spec-format, phases/19-capstone-projects/71-classical-metrics, phases/19-capstone-projects/72-code-exec-metric, phases/19-capstone-projects/73-perplexity-calibration, phases/19-capstone-projects/74-leaderboard-aggregation, phases/19-capstone-projects/75-end-to-end-eval-runner
source_hash: f41ffc5fe2795abd
---

## End-to-end eval runner
type: architecture
from: phases/19-capstone-projects/75-end-to-end-eval-runner/docs/en.md, phases/19-capstone-projects/75-end-to-end-eval-runner/code/main.py, phases/19-capstone-projects/72-code-exec-metric/code/main.py

```arch
node | tasks | 1,1 | tasks.jsonl | validate_file
node | adapter | 2,1 | ModelAdapter | generate(prompt, task)
node | post | 3,1 | post_process | one rule per task
node | metric | 4,1 | score | metric_name dispatch
node | calib | 1,2 | CalibrationReport | ece, brier
node | board | 2,2 | aggregate | JSON + markdown
node | runs | 3,2 | EvalRun | one per model, task
node | exec | 4,2 | run_candidate | subprocess, 3 s
edge | tasks | adapter | render
edge | adapter | post | text
edge | post | metric |
edge | post | exec | code_exec
edge | metric | runs | score
edge | exec | runs | score
edge | adapter | calib | confidence
edge | runs | board |
edge | calib | board | report
```

## Score one task
type: flow
from: phases/19-capstone-projects/75-end-to-end-eval-runner/docs/en.md, phases/19-capstone-projects/75-end-to-end-eval-runner/code/main.py, phases/19-capstone-projects/70-task-spec-format/docs/en.md

1. **Render**: few-shot examples, then the prompt, with a blank line between.
2. **Adapter**: `generate(prompt, task)` returns text and confidence. The runner times it.
3. **post_process**: applies the single rule of the task.
4. **Dispatch**: `score` for text metrics, `run_candidate` for `code_exec`.
5. **EvalRun**: one record with the score and the metric name.
6. **Calibration buffer**: appends the (confidence, correct) pair.
7. **Correct**: score of 1 for exact_match, accuracy, code_exec. At least 0.5 for graded metrics.

## Pick and run a metric
type: task-table
span: 2
from: phases/19-capstone-projects/70-task-spec-format/code/main.py, phases/19-capstone-projects/71-classical-metrics/code/main.py, phases/11-llm-engineering/10-evaluation/code/eval_framework.py, phases/10-llms-from-scratch/10-evaluation/docs/en.md, phases/05-nlp-foundations-to-advanced/28-long-context-evaluation/docs/en.md

| Task | Code |
|---|---|
| Score arithmetic | `"exact_match"` with `"strip_whitespace"` |
| Score multiple choice | `"exact_match"` with `"extract_letter"` |
| Score generated code | `"code_exec"` with `"extract_code_block"` |
| Score a summary | `"rouge_l"` or `"bleu_4"` |
| Interval on a pass rate | `wilson_confidence_interval(successes, total, z=1.96)` |
| Rank by pairwise wins | `ELOTracker(k=32, initial_rating=1500)` |
| Plant a needle | `build_haystack(filler_text, needle, depth_ratio, total_tokens)` |
| Run a public benchmark | `lm_eval --model hf --model_args pretrained=<model> --tasks mmlu` |

## Harness API
type: annotated-code
lang: python
from: phases/19-capstone-projects/70-task-spec-format/code/main.py, phases/19-capstone-projects/70-task-spec-format/docs/en.md, phases/19-capstone-projects/71-classical-metrics/code/main.py, phases/19-capstone-projects/72-code-exec-metric/code/main.py, phases/19-capstone-projects/73-perplexity-calibration/code/main.py, phases/19-capstone-projects/74-leaderboard-aggregation/code/main.py

```python
tasks, errors = validate_file("tasks.jsonl")     # do not run if errors
s = score("f1", prediction, targets)             # best F1 over the targets
s = score_code_exec(prediction, assertions)      # passed / total, 3 s limit
p = pass_at_k(n=10, c=3, k=5)                    # unbiased estimator
report = CalibrationReport.from_predictions(confidences, correct)
report.ece, report.brier, report.populated_bins  # bins=10 by default
ppl = perplexity(neg_log_probs, token_counts)    # exp(total nll / tokens)
rows = aggregate(runs, b=500, alpha=0.05)        # mean, bootstrap CI, win rate
print(render_markdown(rows))                     # table for a PR comment
```

## Post-process rules
type: command-table
from: phases/19-capstone-projects/70-task-spec-format/docs/en.md, phases/19-capstone-projects/70-task-spec-format/code/main.py

| Command | What it does |
|---|---|
| `none` | Returns the string unchanged |
| `strip_whitespace` | Strips leading and trailing whitespace |
| `lower` | Lowercases the string |
| `extract_letter` | First character in `[A-E]`. For MCQ |
| `extract_code_block` | Body of the first fenced block. For code |
| `extract_first_line` | First non-empty line. For summary classes |

## Key formulas
type: concepts
from: phases/10-llms-from-scratch/10-evaluation/docs/en.md, phases/19-capstone-projects/71-classical-metrics/docs/en.md, phases/19-capstone-projects/72-code-exec-metric/docs/en.md, phases/19-capstone-projects/73-perplexity-calibration/docs/en.md

- **Perplexity**: $\mathrm{PPL} = \exp\left(-\frac{1}{N} \sum_i \log P(\text{token}_i \mid \text{context})\right)$
- **Token F1**: $\frac{2 P R}{P + R}$. P and R are token overlap over prediction and target.
- **BLEU-4**: brevity penalty times the geometric mean of $p_1$ to $p_4$.
- **Brevity penalty**: 1 if $c \ge r$, else $\exp(1 - r / c)$.
- **ROUGE-L**: LCS over reference length and over candidate length, then F1.
- **pass@k**: $1 - \binom{n - c}{k} / \binom{n}{k}$ for $c$ passes in $n$ samples.
- **ECE**: confidence minus accuracy per bin, weighted by bin size, summed.
- **Brier**: $\mathrm{mean}((p_i - y_i)^2)$. Penalizes spread that ECE misses.

## Sample size and confidence
type: command-table
from: phases/11-llm-engineering/10-evaluation/docs/en.md

| Test cases at 90% | 95% CI width |
|---|---|
| 50 | 19 points. Cannot detect a 5% regression |
| 100 | 12 points. Barely |
| 200 | 9 points. Yes |
| 500 | 5 points. Confidently |
| 1000 | 3 points. Precisely |

## Defaults in the code
type: defaults
from: phases/19-capstone-projects/72-code-exec-metric/code/main.py, phases/19-capstone-projects/70-task-spec-format/code/main.py, phases/19-capstone-projects/73-perplexity-calibration/code/main.py, phases/19-capstone-projects/73-perplexity-calibration/docs/en.md, phases/19-capstone-projects/74-leaderboard-aggregation/code/main.py, phases/19-capstone-projects/75-end-to-end-eval-runner/code/main.py, phases/10-llms-from-scratch/10-evaluation/docs/en.md

| Setting | Default | Note |
|---|---|---|
| `DEFAULT_TIMEOUT_S` | 3.0 | Per task. `MAX_TIMEOUT_S` is 30.0 |
| `OUTPUT_CAP_BYTES` | 256 * 1024 | Then the result is "output overflow" |
| `FEW_SHOT_MAX` | 8 | Few-shot examples per task |
| ECE `bins` | 10 | 15 is the comparison convention |
| Bootstrap `b` | 500 | Low-level helpers use 1000 |
| `alpha` | 0.05 | 95% percentile interval |
| `max_workers` | 8 | Thread pool in `run_eval` |
| ELO `k` | 32 | Start rating 1500 |

## Troubleshooting
type: troubleshooting
from: phases/19-capstone-projects/73-perplexity-calibration/docs/en.md, phases/19-capstone-projects/41-eval-pipeline/docs/en.md, phases/05-nlp-foundations-to-advanced/27-llm-evaluation-frameworks/docs/en.md, phases/05-nlp-foundations-to-advanced/28-long-context-evaluation/docs/en.md, phases/19-capstone-projects/74-leaderboard-aggregation/docs/en.md

| Symptom | Cause | Fix |
|---|---|---|
| Perplexity below 1 | Adapter returns log p, not -log p | Negate. The function rejects it |
| Perplexity too good | Padding counted in the denominator | Count only non-pad tokens |
| Judge scores go missing | Bad JSON gives NaN, mean drops it | Catch it as an explicit failure |
| Judge rates its own model high | Self-evaluation adds 10-20% | Judge with a different model family |
| Pairwise judge picks the first | Position bias | Randomize order and run both |
| Mean 0.85, users complain | Mean hides 5% catastrophic failures | Inspect the bottom quantile |
| Passes NIAH, fails real docs | NIAH tests retrieval only | Add RULER or a multi-hop test |
| One task dominates the mean | Score in [0, 100] | Return fractions in [0, 1] |

## Abbreviations
type: glossary
from: phases/05-nlp-foundations-to-advanced/27-llm-evaluation-frameworks/docs/en.md, phases/19-capstone-projects/73-perplexity-calibration/docs/en.md, phases/10-llms-from-scratch/10-evaluation/docs/en.md, phases/05-nlp-foundations-to-advanced/28-long-context-evaluation/docs/en.md

- **EM**: Exact Match. 1 or 0 per task after normalization.
- **ECE**: Expected Calibration Error. Gap between confidence and accuracy.
- **MMLU**: Massive Multitask Language Understanding. 15,908 questions, 57 subjects.
- **HumanEval**: 164 Python function-completion problems.
- **SWE-bench**: 2,294 GitHub issues from 12 Python repos.
- **NIAH**: Needle in a Haystack. Plant a fact in filler, ask for it.
- **MRCR**: Multi-Round Coreference Resolution. 8, 24 or 100 needles.
- **RAGAS**: Retrieval-Augmented Generation ASsessment. Four RAG metrics.

## Questions
type: qa
from: phases/11-llm-engineering/10-evaluation/docs/en.md, phases/19-capstone-projects/27-eval-harness-fixture-tasks/docs/en.md, phases/19-capstone-projects/74-leaderboard-aggregation/docs/en.md, phases/05-nlp-foundations-to-advanced/27-llm-evaluation-frameworks/docs/en.md, phases/05-nlp-foundations-to-advanced/28-long-context-evaluation/docs/en.md

- **How many test cases?** At least 200 for a deploy decision. 500 or more for close systems.
- **Why report pass@1 too?** pass@k hides a model that is right once in twenty tries.
- **When is a difference real?** When the bootstrap interval of the paired difference excludes zero.
- **When can I trust a judge?** Spearman rho against human labels of 0.7 or more.
- **How long is the real context?** Reasoning-effective is often 25-50% of the advertised window.

## Tips
type: tips
from: phases/11-llm-engineering/10-evaluation/docs/en.md, phases/05-nlp-foundations-to-advanced/27-llm-evaluation-frameworks/docs/en.md, phases/19-capstone-projects/49-lm-eval-harness/docs/en.md, phases/05-nlp-foundations-to-advanced/28-long-context-evaluation/docs/en.md

- Anchor each rubric score to observable behavior. Judge variance drops 30-40%.
- Freeze the judge model and its version.
- Pin task files by hash in the leaderboard.
- Diff predictions, not only scores. Use `--include-per-example`.
- Keep eval cases apart from prompt and training examples.
- Test needle depths 0, 0.25, 0.5, 0.75 and 1.0.
Code et livrables 1
Ce que vous en retenez

Enregistré dans votre carnet sur cet appareil.

Sources et limites

Les repères de lecture sont déclaratifs. Les résultats de tests sont enregistrés séparément ; ils valent pour les cas exécutés.

AI Engineering from Scratch · 3f9715c · MIT

Télécharger ce fichier ↓