Download tests/test_evaluation.py from INLEVEL9/Vons: direct link, hf CLI and curl.
- Browser
- Download file 9.94 kB
-
https://huggingface.co/INLEVEL9/Vons/resolve/main/tests/test_evaluation.py
- Command line
-
hf download hf://INLEVEL9/Vons/tests/test_evaluation.py
-
curl -L -o test_evaluation.py https://huggingface.co/INLEVEL9/Vons/resolve/main/tests/test_evaluation.py
9.94 kB
| import json | |
| import math | |
| import tempfile | |
| import unittest | |
| from pathlib import Path | |
| from vons.data import Example, smoke_examples | |
| from vons.evaluation import ( | |
| METRIC_VERSION, | |
| Prediction, | |
| accuracy, | |
| brier_score, | |
| coverage, | |
| ece, | |
| macro_f1, | |
| nll, | |
| selective_risk, | |
| summarize, | |
| validate_distribution, | |
| write_report, | |
| ) | |
| def _row(identifier: str, label: str | None, *, answerable: bool = True, options: tuple[str, ...] = ("a", "b")) -> Example: | |
| return Example( | |
| id=identifier, | |
| task_group="evaluation", | |
| state="state", | |
| question="choose", | |
| options=options, | |
| label=label, | |
| answerable=answerable, | |
| split="test", | |
| provenance={}, | |
| metadata={}, | |
| ) | |
| def _reject_constant(value: str) -> float: | |
| raise ValueError(f"non-strict JSON constant {value}") | |
| class EvaluationTests(unittest.TestCase): | |
| def test_metrics_include_abstention_and_probability_checks(self) -> None: | |
| rows = smoke_examples()[:3] | |
| predictions = [ | |
| Prediction(row.id, row.label, {option: 1.0 if option == row.label else 0.0 for option in row.options}, 1.0, False) | |
| for row in rows | |
| ] | |
| self.assertEqual(macro_f1(rows, predictions), 1.0) | |
| self.assertEqual(nll(rows, predictions), 0.0) | |
| self.assertEqual(brier_score(rows, predictions), 0.0) | |
| self.assertEqual(selective_risk(rows, predictions), 0.0) | |
| summary = summarize(rows, predictions) | |
| self.assertIn("macro_f1_answerable", summary) | |
| self.assertEqual(summary["metric_version"], METRIC_VERSION) | |
| self.assertIn("brier_answerable", summary["metric_definitions"]) | |
| self.assertEqual(summary["probability_population"], "complete") | |
| def test_missing_predictions_stay_in_accuracy_and_coverage_denominators(self) -> None: | |
| rows = [_row(f"r{index}", "a") for index in range(10)] | |
| predictions = [Prediction("r0", "a", {"a": 0.99, "b": 0.01}, 0.99, False)] | |
| summary = summarize(rows, predictions) | |
| self.assertEqual(summary["accuracy_answerable"], 0.1) | |
| self.assertEqual(summary["coverage"], 0.1) | |
| self.assertEqual(summary["selective_risk"], 0.0) | |
| self.assertEqual(summary["counts"]["missing_prediction_rows"], 9) | |
| self.assertEqual(summary["counts"]["probability_rows"], 1) | |
| self.assertEqual(summary["counts"]["probability_unavailable_answerable_rows"], 9) | |
| self.assertEqual(summary["probability_population"], "incomplete") | |
| self.assertAlmostEqual(summary["nll_answerable"], -math.log(0.99)) | |
| def test_abstained_and_error_predictions_are_never_correct_or_covered(self) -> None: | |
| rows = [_row("abstained", "a"), _row("failed", "a")] | |
| predictions = [ | |
| Prediction("abstained", "a", {"a": 0.9, "b": 0.1}, 0.9, True), | |
| Prediction("failed", "a", {}, 0.0, False, error="timeout"), | |
| ] | |
| summary = summarize(rows, predictions) | |
| self.assertEqual(accuracy(rows, predictions), 0.0) | |
| self.assertEqual(macro_f1(rows, predictions), 0.0) | |
| self.assertEqual(coverage(rows, predictions), 0.0) | |
| self.assertIsNone(selective_risk(rows, predictions)) | |
| self.assertEqual(summary["counts"]["abstained_prediction_rows"], 1) | |
| self.assertEqual(summary["counts"]["error_prediction_rows"], 1) | |
| self.assertEqual(summary["counts"]["covered_rows"], 0) | |
| self.assertEqual(summary["counts"]["probability_rows"], 1) | |
| def test_covered_unanswerable_prediction_is_a_selective_risk_error(self) -> None: | |
| rows = [_row("answerable", "a"), _row("unanswerable", None, answerable=False)] | |
| predictions = [ | |
| Prediction("answerable", "a", {"a": 0.9, "b": 0.1}, 0.9, False), | |
| Prediction("unanswerable", "a", {"a": 0.9, "b": 0.1}, 0.9, False), | |
| ] | |
| self.assertEqual(selective_risk(rows, predictions), 0.5) | |
| self.assertEqual(coverage(rows, predictions), 1.0) | |
| self.assertAlmostEqual(ece(rows, predictions), 0.4) | |
| self.assertEqual(summarize(rows, predictions)["counts"]["covered_unanswerable_rows"], 1) | |
| def test_undefined_metrics_are_none(self) -> None: | |
| rows = [_row("a", "a"), _row("b", "b")] | |
| abstained = [ | |
| Prediction("a", None, {"a": 0.2, "b": 0.8}, 0.2, True), | |
| Prediction("b", None, {"a": 0.9, "b": 0.1}, 0.1, True), | |
| ] | |
| self.assertIsNone(selective_risk(rows, abstained)) | |
| self.assertIsNone(ece(rows, abstained)) | |
| self.assertEqual(coverage(rows, abstained), 0.0) | |
| unanswerable = [_row("u", None, answerable=False)] | |
| answered = [Prediction("u", "a", {"a": 0.5, "b": 0.5}, 0.5, False)] | |
| for metric in (accuracy, macro_f1, nll, brier_score): | |
| self.assertIsNone(metric(unanswerable, answered)) | |
| self.assertIsNone(coverage([], [])) | |
| def test_macro_f1_ignores_choices_outside_answerable_rows(self) -> None: | |
| rows = [_row("a", "a", options=("a", "b", "zzz")), _row("b", "b", options=("a", "b", "zzz")), _row("u", None, answerable=False, options=("a", "b", "zzz"))] | |
| predictions = [ | |
| Prediction("a", "a", {}, 0.9, False), | |
| Prediction("b", "b", {}, 0.9, False), | |
| Prediction("u", "zzz", {}, 0.9, False), | |
| ] | |
| self.assertEqual(macro_f1(rows, predictions), 1.0) | |
| def test_unknown_and_duplicate_ids_fail_closed(self) -> None: | |
| rows = [_row("a", "a")] | |
| with self.assertRaisesRegex(ValueError, "unknown example id"): | |
| summarize(rows, [Prediction("ghost", "a", {}, 0.9, False)]) | |
| with self.assertRaisesRegex(ValueError, "duplicate prediction"): | |
| summarize(rows, [Prediction("a", "a", {}, 0.9, False), Prediction("a", "b", {}, 0.9, False)]) | |
| with self.assertRaisesRegex(ValueError, "duplicate example id"): | |
| summarize([_row("a", "a"), _row("a", "a")], []) | |
| def test_invalid_confidence_and_probability_vectors_fail_closed(self) -> None: | |
| rows = [_row("a", "a")] | |
| for confidence in (math.nan, -0.1, 1.8): | |
| with self.subTest(confidence=confidence), self.assertRaisesRegex(ValueError, "confidence"): | |
| ece(rows, [Prediction("a", "a", {"a": 1.0}, confidence, False)]) | |
| for probabilities in ({"a": math.nan, "b": 0.5}, {"a": -0.1, "b": 1.1}, {"a": 0.5, "b": 0.2}, {"a": 0.5, "c": 0.5}): | |
| with self.subTest(probabilities=probabilities), self.assertRaises(ValueError): | |
| nll(rows, [Prediction("a", "a", probabilities, 0.5, False)]) | |
| with self.assertRaisesRegex(ValueError, "no choice"): | |
| coverage(rows, [Prediction("a", None, {"a": 1.0}, 1.0, False)]) | |
| def test_answerable_row_without_label_fails_closed(self) -> None: | |
| rows = [_row("a", None)] | |
| with self.assertRaisesRegex(ValueError, "needs a label"): | |
| accuracy(rows, [Prediction("a", None, {}, 0.1, True)]) | |
| def test_brier_score_is_the_standard_multiclass_sum(self) -> None: | |
| for options, expected in ((("a", "b"), 0.5), (("a", "b", "c", "d"), 0.75)): | |
| rows = [_row("r", "a", options=options)] | |
| uniform = {option: 1.0 / len(options) for option in options} | |
| self.assertAlmostEqual(brier_score(rows, [Prediction("r", "a", uniform, 0.5, False)]), expected) | |
| def test_probability_metrics_are_conditional_on_valid_distributions(self) -> None: | |
| rows = [_row("valid", "a"), _row("failed", "a"), _row("absent", "a"), _row("zero", "a")] | |
| predictions = [ | |
| Prediction("valid", "a", {"a": 0.8, "b": 0.2}, 0.8, False), | |
| Prediction("failed", None, {}, 0.0, False, error="crash"), | |
| Prediction("absent", "a", {}, 0.7, False), | |
| Prediction("zero", "b", {"b": 1.0}, 1.0, False), | |
| ] | |
| summary = summarize(rows, predictions) | |
| self.assertEqual(summary["counts"]["probability_rows"], 2) | |
| self.assertEqual(summary["counts"]["probability_unavailable_answerable_rows"], 2) | |
| self.assertEqual(summary["counts"]["nll_floor_clipped_rows"], 1) | |
| self.assertEqual(summary["probability_population"], "incomplete") | |
| self.assertAlmostEqual(summary["nll_answerable"], (-math.log(0.8) - math.log(1e-12)) / 2) | |
| self.assertEqual(validate_distribution({"b": 1.0}, ("a", "b")), {"b": 1.0}) | |
| def test_ece_uses_last_bin_for_confidence_one(self) -> None: | |
| rows = [_row("a", "a"), _row("b", "b"), _row("c", "a"), _row("u", None, answerable=False)] | |
| predictions = [ | |
| Prediction("a", "a", {"a": 0.9, "b": 0.1}, 0.9, False), | |
| Prediction("b", "a", {"a": 0.9, "b": 0.1}, 0.9, False), | |
| Prediction("c", "a", {"a": 1.0}, 1.0, False), | |
| Prediction("u", "b", {"a": 0.3, "b": 0.7}, 0.3, False), | |
| ] | |
| self.assertAlmostEqual(ece(rows, predictions), 0.275) | |
| with self.assertRaisesRegex(ValueError, "bins"): | |
| ece(rows, predictions, bins=0) | |
| def test_write_report_is_strict_json(self) -> None: | |
| rows = [_row("a", "a")] | |
| predictions = [Prediction("a", "a", {"a": 1.0}, 1.0, False, latency_ms=1.5)] | |
| with tempfile.TemporaryDirectory() as directory: | |
| invalid = Path(directory) / "invalid.json" | |
| with self.assertRaises(ValueError): | |
| write_report(invalid, config={}, summary={"nll_answerable": math.nan}, predictions=predictions) | |
| self.assertFalse(invalid.exists()) | |
| valid = Path(directory) / "valid.json" | |
| write_report(valid, config={"model_id": "m"}, summary=summarize(rows, predictions), predictions=predictions) | |
| payload = json.loads(valid.read_text(encoding="utf-8"), parse_constant=_reject_constant) | |
| self.assertEqual(payload["summary"]["metric_version"], METRIC_VERSION) | |
| self.assertEqual(payload["summary"]["latency_p50_ms"], 1.5) | |
| if __name__ == "__main__": | |
| unittest.main() | |