evalsuite-python 0.3.1__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/CHANGELOG.md +39 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/PKG-INFO +59 -22
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/README.md +46 -21
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/pyproject.toml +17 -1
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/__init__.py +5 -1
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/benchmarks.py +157 -6
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/cli/main.py +31 -1
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/plot.py +146 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/stats/_resolve.py +23 -3
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/stats/compare.py +7 -3
- evalsuite_python-0.4.0/src/evalsuite/text/__init__.py +152 -0
- evalsuite_python-0.4.0/src/evalsuite/text/_common.py +94 -0
- evalsuite_python-0.4.0/src/evalsuite/text/factuality.py +469 -0
- evalsuite_python-0.4.0/src/evalsuite/text/generation.py +1126 -0
- evalsuite_python-0.4.0/src/evalsuite/text/judge.py +532 -0
- evalsuite_python-0.4.0/src/evalsuite/text/qa.py +108 -0
- evalsuite_python-0.4.0/src/evalsuite/text/rag.py +235 -0
- evalsuite_python-0.4.0/src/evalsuite/text/reasoning.py +215 -0
- evalsuite_python-0.4.0/src/evalsuite/text/report.py +158 -0
- evalsuite_python-0.4.0/src/evalsuite/text/retrieval.py +238 -0
- evalsuite_python-0.4.0/src/evalsuite/text/semantic.py +465 -0
- evalsuite_python-0.4.0/src/evalsuite/text/structured.py +789 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/version.py +1 -1
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/integration/test_readme_examples.py +25 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/output/test_benchmarks.py +14 -2
- evalsuite_python-0.4.0/tests/text/test_against_references.py +256 -0
- evalsuite_python-0.4.0/tests/text/test_text_edges.py +175 -0
- evalsuite_python-0.4.0/tests/text/test_v040.py +835 -0
- evalsuite_python-0.4.0/tests/vision/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/.gitignore +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/CONTRIBUTING.md +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/LICENSE +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/__main__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/api.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/calibration.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/classification/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/classification/_common.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/classification/metrics.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/cli/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/clinical/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/clinical/metrics.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/clinical/report.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/core/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/core/context.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/core/exceptions.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/core/export.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/core/registry.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/core/result.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/core/types.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/core/validation.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/py.typed +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/regression/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/regression/metrics.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/reporting.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/stats/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/stats/effect.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/stats/hypothesis.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/stats/intervals.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/stats/paired.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/stats/results.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/vision/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/vision/detection.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/src/evalsuite/vision/segmentation.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/classification/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/classification/test_against_sklearn.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/clinical/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/clinical/test_outputs.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/clinical/test_v020.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/conftest.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/integration/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/integration/test_qa.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/integration/test_repo_hygiene.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/output/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/output/test_cli.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/output/test_plot.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/output/test_reporting.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/regression/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/regression/test_against_sklearn.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/stats/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/stats/test_branches.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/stats/test_compare.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/stats/test_reference.py +0 -0
- {evalsuite_python-0.3.1/tests/unit → evalsuite_python-0.4.0/tests/text}/__init__.py +0 -0
- {evalsuite_python-0.3.1/tests/vision → evalsuite_python-0.4.0/tests/unit}/__init__.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/unit/test_core.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/unit/test_edges.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/vision/test_detection.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/vision/test_edges.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/vision/test_outputs.py +0 -0
- {evalsuite_python-0.3.1 → evalsuite_python-0.4.0}/tests/vision/test_segmentation.py +0 -0
|
@@ -6,6 +6,45 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.4.0] - 2026-10-09
|
|
10
|
+
|
|
11
|
+
LLM evaluation (the v0.4.0 roadmap). No existing API changed.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- `es.text` and top-level functions for language-model evaluation; 69 new registered metrics, each checked
|
|
15
|
+
against a reference implementation where one exists:
|
|
16
|
+
- Text generation: BLEU, sentence BLEU, chrF/chrF++, TER (sacreBLEU), ROUGE-1/2/L/Lsum (rouge-score),
|
|
17
|
+
METEOR (NLTK, synonyms pluggable), CIDEr-D (pycocoevalcap), perplexity, cross-entropy, Distinct-n,
|
|
18
|
+
Self-BLEU, MAUVE (mauve-text divergence frontier, NumPy k-means).
|
|
19
|
+
- Semantic: BERTScore, embedding cosine / Euclidean / Manhattan, MoverScore (exact transport, = POT), and
|
|
20
|
+
`model_score` to run COMET, BLEURT, BARTScore, AlignScore or any scorer with intervals and comparison.
|
|
21
|
+
- Factuality: faithfulness, hallucination (unsupported-claim) rate, groundedness, citation precision and
|
|
22
|
+
recall (ALCE), claim-verification accuracy, knowledge consistency, answer correctness and relevance
|
|
23
|
+
(RAGAS), abstention accuracy, exact match and token F1 (SQuAD).
|
|
24
|
+
- LLM-as-a-judge: win rate, Bradley–Terry (= choix), Elo, Krippendorff's alpha (= krippendorff), Fleiss'
|
|
25
|
+
kappa (= statsmodels), judge–human agreement, position consistency, verbosity and self-preference bias,
|
|
26
|
+
rubric scores.
|
|
27
|
+
- Reasoning: pass@k, majority-vote accuracy, answer extraction and benchmark accuracy (GSM8K, MATH,
|
|
28
|
+
multiple choice).
|
|
29
|
+
- RAG: Precision/Recall/Hit rate@k, MRR, MAP, NDCG (= ranx), context precision, recall and relevance,
|
|
30
|
+
latency summary, task success rate, failure attribution.
|
|
31
|
+
- Structured output: JSON validity, JSON Schema compliance (built-in validator, agrees with jsonschema),
|
|
32
|
+
XML validity, required-field accuracy, tool selection / argument accuracy and tool-call F1, API-call
|
|
33
|
+
success, instruction compliance, constraint satisfaction, format compliance, multi-turn retention,
|
|
34
|
+
extra-content rate.
|
|
35
|
+
- `es.text_report` and `evalsuite text` (CLI) for the reference-based text metrics in one report.
|
|
36
|
+
- `bootstrap_ci`, `compare` and the paired tests resample whole items (texts, reference lists, rankings) for
|
|
37
|
+
these metrics, so corpus metrics such as BLEU get proper intervals.
|
|
38
|
+
- Benchmark suite `llm` against sacreBLEU, rouge-score, NLTK, ranx, krippendorff and jsonschema.
|
|
39
|
+
- `llm` extra (`nltk`) for METEOR's default Porter stemmer.
|
|
40
|
+
- Plots: `es.plot.ratings` (Bradley–Terry or Elo leaderboard with bootstrap intervals), `es.plot.win_matrix`
|
|
41
|
+
(pairwise win rates) and `es.plot.text_scores` (per-example score distributions by system).
|
|
42
|
+
- Benchmark overall summary counts cases without their size suffixes.
|
|
43
|
+
|
|
44
|
+
### Not included
|
|
45
|
+
- SPICE (needs a Java scene-graph parser); BLEURT, COMET, BARTScore and AlignScore run through
|
|
46
|
+
`es.model_score` with your model rather than being bundled.
|
|
47
|
+
|
|
9
48
|
## [0.3.1] - 2026-10-09
|
|
10
49
|
|
|
11
50
|
Quality-assurance release: release pipeline, packaging checks, edge-case fixes and an overall benchmark.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: evalsuite-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A unified Python framework for machine-learning, clinical, statistical, segmentation, object-detection, uncertainty, and model evaluation.
|
|
5
5
|
Project-URL: Homepage, https://evalsuite-nine.vercel.app
|
|
6
6
|
Project-URL: Documentation, https://evalsuite-nine.vercel.app/docs
|
|
@@ -36,22 +36,34 @@ Requires-Dist: pandas>=1.4
|
|
|
36
36
|
Requires-Dist: scipy>=1.8
|
|
37
37
|
Provides-Extra: all
|
|
38
38
|
Requires-Dist: matplotlib>=3.5; extra == 'all'
|
|
39
|
+
Requires-Dist: nltk>=3.8; extra == 'all'
|
|
39
40
|
Requires-Dist: pillow>=9; extra == 'all'
|
|
40
41
|
Provides-Extra: dev
|
|
41
42
|
Requires-Dist: build; extra == 'dev'
|
|
43
|
+
Requires-Dist: choix>=0.3; extra == 'dev'
|
|
42
44
|
Requires-Dist: hypothesis>=6.80; extra == 'dev'
|
|
45
|
+
Requires-Dist: jsonschema>=4.18; extra == 'dev'
|
|
46
|
+
Requires-Dist: krippendorff>=0.6; extra == 'dev'
|
|
43
47
|
Requires-Dist: matplotlib>=3.5; extra == 'dev'
|
|
44
48
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
49
|
+
Requires-Dist: nltk>=3.8; extra == 'dev'
|
|
45
50
|
Requires-Dist: pandas-stubs; extra == 'dev'
|
|
46
51
|
Requires-Dist: pillow>=9; extra == 'dev'
|
|
47
52
|
Requires-Dist: pip-audit; extra == 'dev'
|
|
53
|
+
Requires-Dist: pot>=0.9; (python_version < '3.14') and extra == 'dev'
|
|
54
|
+
Requires-Dist: pycocoevalcap>=1.2; extra == 'dev'
|
|
48
55
|
Requires-Dist: pycocotools>=2.0.7; extra == 'dev'
|
|
49
56
|
Requires-Dist: pytest-cov>=4; extra == 'dev'
|
|
50
57
|
Requires-Dist: pytest>=7; extra == 'dev'
|
|
58
|
+
Requires-Dist: ranx>=0.3.19; (python_version < '3.14') and extra == 'dev'
|
|
59
|
+
Requires-Dist: rouge-score>=0.1.2; extra == 'dev'
|
|
51
60
|
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
61
|
+
Requires-Dist: sacrebleu>=2.4; extra == 'dev'
|
|
52
62
|
Requires-Dist: scikit-learn>=1.4; extra == 'dev'
|
|
53
63
|
Requires-Dist: statsmodels>=0.13; extra == 'dev'
|
|
54
64
|
Requires-Dist: twine; extra == 'dev'
|
|
65
|
+
Provides-Extra: llm
|
|
66
|
+
Requires-Dist: nltk>=3.8; extra == 'llm'
|
|
55
67
|
Provides-Extra: plot
|
|
56
68
|
Requires-Dist: matplotlib>=3.5; extra == 'plot'
|
|
57
69
|
Provides-Extra: vision
|
|
@@ -65,12 +77,14 @@ Description-Content-Type: text/markdown
|
|
|
65
77
|
[](https://pypi.org/project/evalsuite-python/)
|
|
66
78
|
[](LICENSE)
|
|
67
79
|
|
|
68
|
-
**Unified, reproducible evaluation for machine learning and research.**
|
|
80
|
+
**Unified, reproducible evaluation for machine learning, LLM and research.**
|
|
69
81
|
|
|
70
82
|
EvalSuite brings classification, regression, clinical, statistical, segmentation and object-detection
|
|
71
83
|
evaluation into one consistent, validated, documented framework.
|
|
72
84
|
|
|
73
|
-
> **Status: stable (0.
|
|
85
|
+
> **Status: stable (0.4.0).** Every item on the 0.1.0, 0.2.0, 0.3.0 and 0.4.0 roadmaps is implemented and verified
|
|
86
|
+
> (except SPICE, which needs a Java scene-graph parser).
|
|
87
|
+
> **LLM evaluation is available from 0.4.0**; LLM systems, safety and operations are planned for 0.5.0.
|
|
74
88
|
|
|
75
89
|
## Installation
|
|
76
90
|
|
|
@@ -226,6 +240,22 @@ Models can be compared over the same images with intervals and paired tests, as
|
|
|
226
240
|
`es.compare(y_true, {"unet": masks_a, "deeplab": masks_b})` resamples images; for detection it compares
|
|
227
241
|
mAP.
|
|
228
242
|
|
|
243
|
+
## LLM evaluation
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
es.bleu(references, predictions) # sacreBLEU-identical; also chrf, ter, rouge_l, meteor, cider
|
|
247
|
+
es.text_report(references, predictions) # BLEU, chrF(++), TER, ROUGE, METEOR, EM, token F1 at once
|
|
248
|
+
es.bertscore(ref_token_embs, pred_token_embs) # from your encoder's embeddings; also mauve, moverscore
|
|
249
|
+
es.model_score(refs, preds, scorer=comet_fn) # any learned metric or judge, with intervals and compare
|
|
250
|
+
es.faithfulness(claim_verdicts) # also hallucination_rate, citation_recall, answer_correctness
|
|
251
|
+
es.bradley_terry(comparisons, scale="elo") # also win_rate, elo_ratings, krippendorff_alpha
|
|
252
|
+
es.ndcg_at_k(relevant, retrieved, k=10) # also mrr, context_precision, context_recall
|
|
253
|
+
es.json_schema_compliance(outputs, schema) # also tool_call_f1, instruction_compliance_rate
|
|
254
|
+
es.pass_at_k(n_samples, n_correct, k=10) # also benchmark_accuracy(..., style="gsm8k")
|
|
255
|
+
es.compare(references, {"a": preds_a, "b": preds_b}, metrics=["bleu", "rouge_l"]) # paired over examples
|
|
256
|
+
es.plot.ratings(comparisons) # Bradley–Terry leaderboard with intervals; also win_matrix, text_scores
|
|
257
|
+
```
|
|
258
|
+
|
|
229
259
|
## Classification report
|
|
230
260
|
|
|
231
261
|
```python
|
|
@@ -283,28 +313,35 @@ Input files can be CSV, TSV, Parquet or JSON. Output format follows `--format` o
|
|
|
283
313
|
## Performance
|
|
284
314
|
|
|
285
315
|
Benchmarked against reference implementations on the same data (fastest of 5 runs; Python 3.12, NumPy 2.5,
|
|
286
|
-
Linux x86_64). **Overall:
|
|
287
|
-
difference
|
|
288
|
-
|
|
289
|
-
segmentation; 1,000 images for detection and 50 for Hausdorff), in
|
|
316
|
+
Linux x86_64). **Overall: 63 of 63 rows agree with the reference** (largest
|
|
317
|
+
difference 1.8e-14), 42 are faster, and the geometric-mean speed-up across all
|
|
318
|
+
21 cases and three sizes is **2.12×**. At 1,000,000 samples (pixels for
|
|
319
|
+
segmentation; 1,000 images for detection and 50 for Hausdorff; 10,000 examples for the LLM cases), in
|
|
320
|
+
alphabetical order:
|
|
290
321
|
|
|
291
322
|
| Case | Reference | EvalSuite (ms) | Reference (ms) | Speed-up |
|
|
292
323
|
| --- | --- | ---: | ---: | ---: |
|
|
293
|
-
| 10 classes: macro F1 | scikit-learn |
|
|
294
|
-
|
|
|
295
|
-
| binary:
|
|
296
|
-
|
|
|
297
|
-
|
|
|
298
|
-
| clinical:
|
|
299
|
-
|
|
|
300
|
-
|
|
|
301
|
-
|
|
|
302
|
-
|
|
|
303
|
-
|
|
|
304
|
-
|
|
|
305
|
-
|
|
|
306
|
-
|
|
|
307
|
-
| statistics:
|
|
324
|
+
| 10 classes: macro F1 | scikit-learn | 23.8 | 135.5 | **5.7×** |
|
|
325
|
+
| agreement: Krippendorff's alpha, interval (4 raters × 10000 items) | krippendorff | 3.5 | 3.4 | 0.96× |
|
|
326
|
+
| binary: 8 label metrics via evaluate() | scikit-learn | 28.5 | 1023.8 | **36.0×** |
|
|
327
|
+
| binary: ROC AUC | scikit-learn | 187.4 | 327.3 | **1.7×** |
|
|
328
|
+
| calibration: slope and intercept | statsmodels | 205.7 | 1173.7 | **5.7×** |
|
|
329
|
+
| clinical: diagnostic report (7 CIs) | statsmodels | 19.6 | 4.9 | 0.25× |
|
|
330
|
+
| clinical: sensitivity, specificity, LR+, LR− | scikit-learn | 75.2 | 411.5 | **5.5×** |
|
|
331
|
+
| decision curve: 99 thresholds | NumPy loop | 165.0 | 201.9 | **1.2×** |
|
|
332
|
+
| detection: COCO evaluation (1000 images) | pycocotools | 838.7 | 848.6 | **1.0×** |
|
|
333
|
+
| multiple testing: Hochberg (n p-values) | statsmodels | 72.1 | 79.0 | **1.1×** |
|
|
334
|
+
| regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn | 22.9 | 10.6 | 0.47× |
|
|
335
|
+
| retrieval: MRR, MAP@20, NDCG@10 (10000 queries) | ranx | 170.4 | 861.0 | **5.1×** |
|
|
336
|
+
| segmentation: Dice and IoU per class (n = pixels) | scikit-learn | 34.3 | 256.0 | **7.5×** |
|
|
337
|
+
| segmentation: Hausdorff distance (50 images) | SciPy | 24.2 | 19.8 | 0.82× |
|
|
338
|
+
| statistics: Cramér's V (5×5 table) | SciPy | 0.2 | 0.2 | **1.0×** |
|
|
339
|
+
| statistics: Mann–Whitney U | SciPy | 353.5 | 340.6 | 0.96× |
|
|
340
|
+
| statistics: Welch t-test | SciPy | 16.1 | 8.3 | 0.52× |
|
|
341
|
+
| structured: JSON Schema compliance (10000 documents) | jsonschema | 144.6 | 330.5 | **2.3×** |
|
|
342
|
+
| text: corpus BLEU and chrF (10000 sentences) | sacreBLEU | 2789.8 | 3051.8 | **1.1×** |
|
|
343
|
+
| text: METEOR, exact and stem matches (10000 sentences) | NLTK | 488.1 | 626.7 | **1.3×** |
|
|
344
|
+
| text: ROUGE-1, ROUGE-2, ROUGE-L (10000 sentences) | rouge-score | 1290.1 | 1095.6 | 0.85× |
|
|
308
345
|
|
|
309
346
|
`evaluate()` validates inputs once and builds the confusion matrix once for all metrics, which is where most
|
|
310
347
|
of the speed-up comes from. Hypothesis tests use SciPy underneath, so they match its speed at best; rows
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
[](https://pypi.org/project/evalsuite-python/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
**Unified, reproducible evaluation for machine learning and research.**
|
|
8
|
+
**Unified, reproducible evaluation for machine learning, LLM and research.**
|
|
9
9
|
|
|
10
10
|
EvalSuite brings classification, regression, clinical, statistical, segmentation and object-detection
|
|
11
11
|
evaluation into one consistent, validated, documented framework.
|
|
12
12
|
|
|
13
|
-
> **Status: stable (0.
|
|
13
|
+
> **Status: stable (0.4.0).** Every item on the 0.1.0, 0.2.0, 0.3.0 and 0.4.0 roadmaps is implemented and verified
|
|
14
|
+
> (except SPICE, which needs a Java scene-graph parser).
|
|
15
|
+
> **LLM evaluation is available from 0.4.0**; LLM systems, safety and operations are planned for 0.5.0.
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -166,6 +168,22 @@ Models can be compared over the same images with intervals and paired tests, as
|
|
|
166
168
|
`es.compare(y_true, {"unet": masks_a, "deeplab": masks_b})` resamples images; for detection it compares
|
|
167
169
|
mAP.
|
|
168
170
|
|
|
171
|
+
## LLM evaluation
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
es.bleu(references, predictions) # sacreBLEU-identical; also chrf, ter, rouge_l, meteor, cider
|
|
175
|
+
es.text_report(references, predictions) # BLEU, chrF(++), TER, ROUGE, METEOR, EM, token F1 at once
|
|
176
|
+
es.bertscore(ref_token_embs, pred_token_embs) # from your encoder's embeddings; also mauve, moverscore
|
|
177
|
+
es.model_score(refs, preds, scorer=comet_fn) # any learned metric or judge, with intervals and compare
|
|
178
|
+
es.faithfulness(claim_verdicts) # also hallucination_rate, citation_recall, answer_correctness
|
|
179
|
+
es.bradley_terry(comparisons, scale="elo") # also win_rate, elo_ratings, krippendorff_alpha
|
|
180
|
+
es.ndcg_at_k(relevant, retrieved, k=10) # also mrr, context_precision, context_recall
|
|
181
|
+
es.json_schema_compliance(outputs, schema) # also tool_call_f1, instruction_compliance_rate
|
|
182
|
+
es.pass_at_k(n_samples, n_correct, k=10) # also benchmark_accuracy(..., style="gsm8k")
|
|
183
|
+
es.compare(references, {"a": preds_a, "b": preds_b}, metrics=["bleu", "rouge_l"]) # paired over examples
|
|
184
|
+
es.plot.ratings(comparisons) # Bradley–Terry leaderboard with intervals; also win_matrix, text_scores
|
|
185
|
+
```
|
|
186
|
+
|
|
169
187
|
## Classification report
|
|
170
188
|
|
|
171
189
|
```python
|
|
@@ -223,28 +241,35 @@ Input files can be CSV, TSV, Parquet or JSON. Output format follows `--format` o
|
|
|
223
241
|
## Performance
|
|
224
242
|
|
|
225
243
|
Benchmarked against reference implementations on the same data (fastest of 5 runs; Python 3.12, NumPy 2.5,
|
|
226
|
-
Linux x86_64). **Overall:
|
|
227
|
-
difference
|
|
228
|
-
|
|
229
|
-
segmentation; 1,000 images for detection and 50 for Hausdorff), in
|
|
244
|
+
Linux x86_64). **Overall: 63 of 63 rows agree with the reference** (largest
|
|
245
|
+
difference 1.8e-14), 42 are faster, and the geometric-mean speed-up across all
|
|
246
|
+
21 cases and three sizes is **2.12×**. At 1,000,000 samples (pixels for
|
|
247
|
+
segmentation; 1,000 images for detection and 50 for Hausdorff; 10,000 examples for the LLM cases), in
|
|
248
|
+
alphabetical order:
|
|
230
249
|
|
|
231
250
|
| Case | Reference | EvalSuite (ms) | Reference (ms) | Speed-up |
|
|
232
251
|
| --- | --- | ---: | ---: | ---: |
|
|
233
|
-
| 10 classes: macro F1 | scikit-learn |
|
|
234
|
-
|
|
|
235
|
-
| binary:
|
|
236
|
-
|
|
|
237
|
-
|
|
|
238
|
-
| clinical:
|
|
239
|
-
|
|
|
240
|
-
|
|
|
241
|
-
|
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
|
|
|
245
|
-
|
|
|
246
|
-
|
|
|
247
|
-
| statistics:
|
|
252
|
+
| 10 classes: macro F1 | scikit-learn | 23.8 | 135.5 | **5.7×** |
|
|
253
|
+
| agreement: Krippendorff's alpha, interval (4 raters × 10000 items) | krippendorff | 3.5 | 3.4 | 0.96× |
|
|
254
|
+
| binary: 8 label metrics via evaluate() | scikit-learn | 28.5 | 1023.8 | **36.0×** |
|
|
255
|
+
| binary: ROC AUC | scikit-learn | 187.4 | 327.3 | **1.7×** |
|
|
256
|
+
| calibration: slope and intercept | statsmodels | 205.7 | 1173.7 | **5.7×** |
|
|
257
|
+
| clinical: diagnostic report (7 CIs) | statsmodels | 19.6 | 4.9 | 0.25× |
|
|
258
|
+
| clinical: sensitivity, specificity, LR+, LR− | scikit-learn | 75.2 | 411.5 | **5.5×** |
|
|
259
|
+
| decision curve: 99 thresholds | NumPy loop | 165.0 | 201.9 | **1.2×** |
|
|
260
|
+
| detection: COCO evaluation (1000 images) | pycocotools | 838.7 | 848.6 | **1.0×** |
|
|
261
|
+
| multiple testing: Hochberg (n p-values) | statsmodels | 72.1 | 79.0 | **1.1×** |
|
|
262
|
+
| regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn | 22.9 | 10.6 | 0.47× |
|
|
263
|
+
| retrieval: MRR, MAP@20, NDCG@10 (10000 queries) | ranx | 170.4 | 861.0 | **5.1×** |
|
|
264
|
+
| segmentation: Dice and IoU per class (n = pixels) | scikit-learn | 34.3 | 256.0 | **7.5×** |
|
|
265
|
+
| segmentation: Hausdorff distance (50 images) | SciPy | 24.2 | 19.8 | 0.82× |
|
|
266
|
+
| statistics: Cramér's V (5×5 table) | SciPy | 0.2 | 0.2 | **1.0×** |
|
|
267
|
+
| statistics: Mann–Whitney U | SciPy | 353.5 | 340.6 | 0.96× |
|
|
268
|
+
| statistics: Welch t-test | SciPy | 16.1 | 8.3 | 0.52× |
|
|
269
|
+
| structured: JSON Schema compliance (10000 documents) | jsonschema | 144.6 | 330.5 | **2.3×** |
|
|
270
|
+
| text: corpus BLEU and chrF (10000 sentences) | sacreBLEU | 2789.8 | 3051.8 | **1.1×** |
|
|
271
|
+
| text: METEOR, exact and stem matches (10000 sentences) | NLTK | 488.1 | 626.7 | **1.3×** |
|
|
272
|
+
| text: ROUGE-1, ROUGE-2, ROUGE-L (10000 sentences) | rouge-score | 1290.1 | 1095.6 | 0.85× |
|
|
248
273
|
|
|
249
274
|
`evaluate()` validates inputs once and builds the confusion matrix once for all metrics, which is where most
|
|
250
275
|
of the speed-up comes from. Hypothesis tests use SciPy underneath, so they match its speed at best; rows
|
|
@@ -38,7 +38,8 @@ dependencies = ["numpy>=1.22", "scipy>=1.8", "pandas>=1.4"]
|
|
|
38
38
|
[project.optional-dependencies]
|
|
39
39
|
plot = ["matplotlib>=3.5"]
|
|
40
40
|
vision = ["pillow>=9"]
|
|
41
|
-
|
|
41
|
+
llm = ["nltk>=3.8"]
|
|
42
|
+
all = ["matplotlib>=3.5", "pillow>=9", "nltk>=3.8"]
|
|
42
43
|
dev = [
|
|
43
44
|
"pytest>=7",
|
|
44
45
|
"pytest-cov>=4",
|
|
@@ -48,6 +49,15 @@ dev = [
|
|
|
48
49
|
"matplotlib>=3.5",
|
|
49
50
|
"pillow>=9",
|
|
50
51
|
"pycocotools>=2.0.7",
|
|
52
|
+
"nltk>=3.8",
|
|
53
|
+
"sacrebleu>=2.4",
|
|
54
|
+
"rouge-score>=0.1.2",
|
|
55
|
+
"pycocoevalcap>=1.2",
|
|
56
|
+
"jsonschema>=4.18",
|
|
57
|
+
"choix>=0.3",
|
|
58
|
+
"krippendorff>=0.6",
|
|
59
|
+
"pot>=0.9; python_version < '3.14'",
|
|
60
|
+
"ranx>=0.3.19; python_version < '3.14'",
|
|
51
61
|
"ruff>=0.6",
|
|
52
62
|
"mypy>=1.10",
|
|
53
63
|
"pandas-stubs",
|
|
@@ -99,6 +109,12 @@ files = ["src/evalsuite"]
|
|
|
99
109
|
module = ["scipy.*", "matplotlib.*"]
|
|
100
110
|
ignore_missing_imports = true
|
|
101
111
|
|
|
112
|
+
[[tool.mypy.overrides]]
|
|
113
|
+
# optional reference libraries used only by the benchmarks and METEOR's default stemmer
|
|
114
|
+
module = ["sacrebleu.*", "rouge_score.*", "nltk.*", "ranx.*", "krippendorff.*", "jsonschema.*", "pycocotools.*"]
|
|
115
|
+
ignore_missing_imports = true
|
|
116
|
+
follow_untyped_imports = false
|
|
117
|
+
|
|
102
118
|
[tool.pytest.ini_options]
|
|
103
119
|
testpaths = ["tests"]
|
|
104
120
|
addopts = "-q --strict-markers"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
>>> print(result.summary()) # doctest: +SKIP
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from . import calibration, classification, clinical, plot, regression, stats, vision
|
|
8
|
+
from . import calibration, classification, clinical, plot, regression, stats, text, vision
|
|
9
9
|
from .api import evaluate
|
|
10
10
|
from .calibration import (
|
|
11
11
|
CalibrationReport,
|
|
@@ -110,6 +110,8 @@ from .stats import (
|
|
|
110
110
|
t_test,
|
|
111
111
|
wilcoxon_test,
|
|
112
112
|
)
|
|
113
|
+
from .text import * # noqa: F403 (every LLM-evaluation function, listed in text.__all__)
|
|
114
|
+
from .text import __all__ as _text_all
|
|
113
115
|
from .version import __version__
|
|
114
116
|
from .vision import (
|
|
115
117
|
DetectionReport,
|
|
@@ -134,6 +136,7 @@ from .vision import (
|
|
|
134
136
|
)
|
|
135
137
|
|
|
136
138
|
__all__ = [
|
|
139
|
+
"text",
|
|
137
140
|
"SegmentationReport",
|
|
138
141
|
"segmentation_report",
|
|
139
142
|
"detection_pr_curve",
|
|
@@ -258,3 +261,4 @@ __all__ = [
|
|
|
258
261
|
"specificity",
|
|
259
262
|
"top_k_accuracy",
|
|
260
263
|
]
|
|
264
|
+
__all__ += list(_text_all)
|
|
@@ -213,8 +213,8 @@ def _vision_cases(n: int, rng: np.random.Generator) -> list[Case]:
|
|
|
213
213
|
import contextlib as _ctx
|
|
214
214
|
import io
|
|
215
215
|
|
|
216
|
-
from pycocotools.coco import COCO
|
|
217
|
-
from pycocotools.cocoeval import COCOeval
|
|
216
|
+
from pycocotools.coco import COCO
|
|
217
|
+
from pycocotools.cocoeval import COCOeval
|
|
218
218
|
|
|
219
219
|
def coco_map() -> list[float]:
|
|
220
220
|
images, anns, dets, aid = [], [], [], 1
|
|
@@ -262,6 +262,155 @@ def _vision_cases(n: int, rng: np.random.Generator) -> list[Case]:
|
|
|
262
262
|
return [(name, ref, es_fn, refs.get(name)) for name, ref, es_fn, _ in cases]
|
|
263
263
|
|
|
264
264
|
|
|
265
|
+
_LLM_VOCAB = ( # noqa: SIM905
|
|
266
|
+
"the a cat dog sat on mat quickly model models data run running ran 2.5 3,000 well-known state-of-the-art "
|
|
267
|
+
"( ) , . ! ? don't U.S. e-mail Hello WORLD naive cafe results show improves baseline evaluation"
|
|
268
|
+
).split()
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _llm_cases(n: int, rng: np.random.Generator) -> list[Case]:
|
|
272
|
+
"""v0.4.0: text generation, retrieval, rater agreement and structured output (n / 100 examples)."""
|
|
273
|
+
import evalsuite as es
|
|
274
|
+
|
|
275
|
+
m = max(10, n // 100)
|
|
276
|
+
preds, refs_ = [], []
|
|
277
|
+
for _ in range(m):
|
|
278
|
+
base = list(rng.choice(_LLM_VOCAB, int(rng.integers(5, 25))))
|
|
279
|
+
keep = [w for w in base if rng.random() > 0.2] + list(rng.choice(_LLM_VOCAB, int(rng.integers(0, 4))))
|
|
280
|
+
preds.append(" ".join(keep))
|
|
281
|
+
refs_.append(" ".join(base))
|
|
282
|
+
queries = [
|
|
283
|
+
(set(rng.choice(200, int(rng.integers(1, 6)), replace=False).tolist()), rng.permutation(200)[:20].tolist())
|
|
284
|
+
for _ in range(m)
|
|
285
|
+
]
|
|
286
|
+
rel, ret = [q[0] for q in queries], [q[1] for q in queries]
|
|
287
|
+
ratings = rng.integers(1, 6, (4, m)).astype(float)
|
|
288
|
+
ratings[rng.random(ratings.shape) < 0.1] = np.nan
|
|
289
|
+
schema = {
|
|
290
|
+
"type": "object",
|
|
291
|
+
"properties": {
|
|
292
|
+
"name": {"type": "string", "minLength": 1},
|
|
293
|
+
"age": {"type": "integer", "minimum": 0},
|
|
294
|
+
"tags": {"type": "array", "items": {"type": "string"}, "uniqueItems": True},
|
|
295
|
+
},
|
|
296
|
+
"required": ["name", "age"],
|
|
297
|
+
"additionalProperties": False,
|
|
298
|
+
}
|
|
299
|
+
docs = []
|
|
300
|
+
for i in range(m):
|
|
301
|
+
d: dict[str, Any] = {"name": f"n{i}", "age": int(rng.integers(-2, 90)), "tags": ["a", "b"]}
|
|
302
|
+
if rng.random() < 0.1:
|
|
303
|
+
d["extra"] = 1
|
|
304
|
+
if rng.random() < 0.1:
|
|
305
|
+
del d["name"]
|
|
306
|
+
docs.append(d)
|
|
307
|
+
import json as _json
|
|
308
|
+
|
|
309
|
+
docs_text = [_json.dumps(d) for d in docs]
|
|
310
|
+
|
|
311
|
+
cases: list[Case] = [
|
|
312
|
+
(
|
|
313
|
+
f"text: corpus BLEU and chrF ({m} sentences)",
|
|
314
|
+
"sacreBLEU",
|
|
315
|
+
lambda: [float(es.bleu(refs_, preds)), float(es.chrf(refs_, preds))],
|
|
316
|
+
None,
|
|
317
|
+
),
|
|
318
|
+
(
|
|
319
|
+
f"text: ROUGE-1, ROUGE-2, ROUGE-L ({m} sentences)",
|
|
320
|
+
"rouge-score",
|
|
321
|
+
lambda: [
|
|
322
|
+
float(es.rouge_1(refs_, preds)),
|
|
323
|
+
float(es.rouge_2(refs_, preds)),
|
|
324
|
+
float(es.rouge_l(refs_, preds)),
|
|
325
|
+
],
|
|
326
|
+
None,
|
|
327
|
+
),
|
|
328
|
+
(
|
|
329
|
+
f"text: METEOR, exact and stem matches ({m} sentences)",
|
|
330
|
+
"NLTK",
|
|
331
|
+
lambda: [float(es.meteor(refs_, preds))],
|
|
332
|
+
None,
|
|
333
|
+
),
|
|
334
|
+
(
|
|
335
|
+
f"retrieval: MRR, MAP@20, NDCG@10 ({m} queries)",
|
|
336
|
+
"ranx",
|
|
337
|
+
lambda: [
|
|
338
|
+
float(es.mrr(rel, ret)),
|
|
339
|
+
float(es.mean_average_precision_at_k(rel, ret, k=20)),
|
|
340
|
+
float(es.ndcg_at_k(rel, ret, k=10)),
|
|
341
|
+
],
|
|
342
|
+
None,
|
|
343
|
+
),
|
|
344
|
+
(
|
|
345
|
+
f"agreement: Krippendorff's alpha, interval (4 raters × {m} items)",
|
|
346
|
+
"krippendorff",
|
|
347
|
+
lambda: [float(es.krippendorff_alpha(ratings, level="interval"))],
|
|
348
|
+
None,
|
|
349
|
+
),
|
|
350
|
+
(
|
|
351
|
+
f"structured: JSON Schema compliance ({m} documents)",
|
|
352
|
+
"jsonschema",
|
|
353
|
+
lambda: [float(es.json_schema_compliance(docs_text, schema))],
|
|
354
|
+
None,
|
|
355
|
+
),
|
|
356
|
+
]
|
|
357
|
+
refs: dict[str, Callable[[], Any]] = {}
|
|
358
|
+
with contextlib.suppress(ImportError):
|
|
359
|
+
import sacrebleu
|
|
360
|
+
|
|
361
|
+
refs[cases[0][0]] = lambda: [
|
|
362
|
+
sacrebleu.corpus_bleu(preds, [refs_], force=True).score,
|
|
363
|
+
sacrebleu.corpus_chrf(preds, [refs_]).score,
|
|
364
|
+
]
|
|
365
|
+
with contextlib.suppress(ImportError):
|
|
366
|
+
from rouge_score import rouge_scorer
|
|
367
|
+
|
|
368
|
+
def rouge_ref() -> list[float]:
|
|
369
|
+
sc = rouge_scorer.RougeScorer(["rouge1", "rouge2", "rougeL"])
|
|
370
|
+
out = [sc.score(r, p) for r, p in zip(refs_, preds)]
|
|
371
|
+
return [float(np.mean([o[k].fmeasure for o in out])) for k in ("rouge1", "rouge2", "rougeL")]
|
|
372
|
+
|
|
373
|
+
refs[cases[1][0]] = rouge_ref
|
|
374
|
+
with contextlib.suppress(ImportError):
|
|
375
|
+
from nltk.translate.meteor_score import meteor_score
|
|
376
|
+
|
|
377
|
+
class _NoWordNet:
|
|
378
|
+
def synsets(self, _w: str) -> list[Any]:
|
|
379
|
+
return []
|
|
380
|
+
|
|
381
|
+
refs[cases[2][0]] = lambda: [
|
|
382
|
+
float(
|
|
383
|
+
np.mean([meteor_score([r.split()], p.split(), wordnet=_NoWordNet()) for r, p in zip(refs_, preds)])
|
|
384
|
+
)
|
|
385
|
+
]
|
|
386
|
+
with contextlib.suppress(ImportError):
|
|
387
|
+
from ranx import Qrels, Run
|
|
388
|
+
from ranx import evaluate as ranx_evaluate
|
|
389
|
+
|
|
390
|
+
def ranx_ref() -> list[float]:
|
|
391
|
+
qrels = Qrels({f"q{i}": {f"d{d}": 1 for d in r} for i, r in enumerate(rel)})
|
|
392
|
+
run = Run({f"q{i}": {f"d{d}": float(len(r) - j) for j, d in enumerate(r)} for i, r in enumerate(ret)})
|
|
393
|
+
res = ranx_evaluate(qrels, run, ["mrr", "map@20", "ndcg@10"])
|
|
394
|
+
return [float(res["mrr"]), float(res["map@20"]), float(res["ndcg@10"])]
|
|
395
|
+
|
|
396
|
+
refs[cases[3][0]] = ranx_ref
|
|
397
|
+
with contextlib.suppress(ImportError):
|
|
398
|
+
import krippendorff
|
|
399
|
+
|
|
400
|
+
refs[cases[4][0]] = lambda: [
|
|
401
|
+
float(krippendorff.alpha(reliability_data=ratings, level_of_measurement="interval"))
|
|
402
|
+
]
|
|
403
|
+
with contextlib.suppress(ImportError):
|
|
404
|
+
import jsonschema
|
|
405
|
+
|
|
406
|
+
def jsonschema_ref() -> list[float]:
|
|
407
|
+
v = jsonschema.Draft202012Validator(schema)
|
|
408
|
+
return [float(np.mean([v.is_valid(_json.loads(t)) for t in docs_text]))]
|
|
409
|
+
|
|
410
|
+
refs[cases[5][0]] = jsonschema_ref
|
|
411
|
+
return [(name, ref, es_fn, refs.get(name)) for name, ref, es_fn, _ in cases]
|
|
412
|
+
|
|
413
|
+
|
|
265
414
|
def _clinical_cases(n: int, rng: np.random.Generator) -> list[Case]:
|
|
266
415
|
"""v0.2.0: diagnostic accuracy, calibration, decision curves and statistical tests."""
|
|
267
416
|
import evalsuite as es
|
|
@@ -377,6 +526,7 @@ _GROUPS = (
|
|
|
377
526
|
("Classification and regression", ("binary", "10 classes", "regression")),
|
|
378
527
|
("Clinical, calibration and statistics", ("clinical", "calibration", "decision", "statistics", "multiple")),
|
|
379
528
|
("Segmentation and object detection", ("segmentation", "detection")),
|
|
529
|
+
("LLM evaluation", ("text", "retrieval", "agreement", "structured")),
|
|
380
530
|
)
|
|
381
531
|
MATCH_TOLERANCE = 1e-9
|
|
382
532
|
|
|
@@ -419,7 +569,7 @@ class BenchmarkResult:
|
|
|
419
569
|
speedups = [r["speedup"] for r in compared]
|
|
420
570
|
return {
|
|
421
571
|
"group": name,
|
|
422
|
-
"cases": len({re.sub(r"\s*\(
|
|
572
|
+
"cases": len({re.sub(r"\s*\((?=[^)]*\d)[^)]*\)$", "", r["case"]) for r in rows}),
|
|
423
573
|
"rows": len(rows),
|
|
424
574
|
"compared": len(compared),
|
|
425
575
|
"matching": sum(
|
|
@@ -597,8 +747,8 @@ def run_benchmarks(
|
|
|
597
747
|
|
|
598
748
|
if repeat < 1:
|
|
599
749
|
raise ValueError("repeat must be at least 1.")
|
|
600
|
-
if suite not in ("all", "core", "clinical", "vision"):
|
|
601
|
-
raise ValueError("suite must be 'all', 'core', 'clinical' or '
|
|
750
|
+
if suite not in ("all", "core", "clinical", "vision", "llm"):
|
|
751
|
+
raise ValueError("suite must be 'all', 'core', 'clinical', 'vision' or 'llm'.")
|
|
602
752
|
rng = np.random.default_rng(random_state)
|
|
603
753
|
rows: list[dict[str, Any]] = []
|
|
604
754
|
versions: dict[str, Optional[str]] = {"sklearn": None, "statsmodels": None, "pycocotools": None}
|
|
@@ -613,7 +763,8 @@ def run_benchmarks(
|
|
|
613
763
|
"core": [_core_cases],
|
|
614
764
|
"clinical": [_clinical_cases],
|
|
615
765
|
"vision": [_vision_cases],
|
|
616
|
-
"
|
|
766
|
+
"llm": [_llm_cases],
|
|
767
|
+
"all": [_core_cases, _clinical_cases, _vision_cases, _llm_cases],
|
|
617
768
|
}[suite]
|
|
618
769
|
for n in sizes:
|
|
619
770
|
for build in builders:
|
|
@@ -297,6 +297,24 @@ def cmd_detection(args: argparse.Namespace) -> int:
|
|
|
297
297
|
return 0
|
|
298
298
|
|
|
299
299
|
|
|
300
|
+
def cmd_text(args: argparse.Namespace) -> int:
|
|
301
|
+
import evalsuite as es
|
|
302
|
+
|
|
303
|
+
df = read_table(args.file)
|
|
304
|
+
preds = [str(v) for v in column(df, args.prediction, "--prediction")]
|
|
305
|
+
ref_cols = [column(df, c, "--reference") for c in args.reference]
|
|
306
|
+
refs = [[str(col[i]) for col in ref_cols if not _missing(col[i])] for i in range(len(preds))]
|
|
307
|
+
if any(not r for r in refs):
|
|
308
|
+
raise CLIError("Every row needs at least one non-empty reference.")
|
|
309
|
+
report = es.text_report(refs, preds, metrics=metric_list(args.metrics))
|
|
310
|
+
emit(report, args)
|
|
311
|
+
return 0
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _missing(v: Any) -> bool:
|
|
315
|
+
return v is None or (isinstance(v, float) and v != v) or (isinstance(v, str) and not v.strip())
|
|
316
|
+
|
|
317
|
+
|
|
300
318
|
def cmd_compare(args: argparse.Namespace) -> int:
|
|
301
319
|
import evalsuite as es
|
|
302
320
|
|
|
@@ -506,6 +524,18 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
506
524
|
add_output(p, digits=3)
|
|
507
525
|
p.set_defaults(func=cmd_detection)
|
|
508
526
|
|
|
527
|
+
p = sub.add_parser(
|
|
528
|
+
"text", help="text generation: BLEU, chrF, chrF++, TER, ROUGE, METEOR, exact match, token F1"
|
|
529
|
+
)
|
|
530
|
+
p.add_argument("file", help="CSV, TSV, Parquet or JSON-lines file with one example per row ('-' for stdin)")
|
|
531
|
+
p.add_argument("--prediction", required=True, help="column with the model output")
|
|
532
|
+
p.add_argument(
|
|
533
|
+
"--reference", required=True, action="append", help="column with a reference (repeat for several)"
|
|
534
|
+
)
|
|
535
|
+
p.add_argument("--metrics", help="comma-separated subset, e.g. bleu,chrf,rouge_l")
|
|
536
|
+
add_output(p)
|
|
537
|
+
p.set_defaults(func=cmd_text)
|
|
538
|
+
|
|
509
539
|
p = sub.add_parser(
|
|
510
540
|
"plot", help="save a ROC, PR, calibration, decision-curve, confusion-matrix or residual plot"
|
|
511
541
|
)
|
|
@@ -529,7 +559,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
529
559
|
p.add_argument("--repeat", type=int, default=5, help="timed repetitions per case; the fastest is reported")
|
|
530
560
|
p.add_argument(
|
|
531
561
|
"--suite",
|
|
532
|
-
choices=("all", "core", "clinical", "vision"),
|
|
562
|
+
choices=("all", "core", "clinical", "vision", "llm"),
|
|
533
563
|
default="all",
|
|
534
564
|
help="core: classification/regression; clinical: clinical, calibration, tests; vision: segmentation, "
|
|
535
565
|
"detection (default all)",
|