Picarones / docs /migration /executor-equivalence.md
Claude
test(integration): Sprint A14-S12 — équivalence numérique runner ↔ pipeline
56c3bee unverified
|
Raw
History Blame
5.7 kB

Équivalence numérique — ancien runner ↔ nouveau pipeline executor

Ce document décrit comment le CorpusRunner introduit au Sprint S8 (combiné au PipelineExecutor du S7) reproduit les mêmes chiffres CER/WER que l'ancien picarones.measurements.runner.run_benchmark.

C'est le critère go/no-go de fin de Phase 2 du rewrite ciblé (cf. docs/roadmap/rewrite-2026.md). Sans cette équivalence, on ne peut pas basculer la BnF vers le nouveau runner sans surprise.

Architecture des deux orchestrations

Ancien runner (picarones.measurements.runner)

Corpus[Document(image, GT)]
     │
     ▼
run_benchmark(corpus, [BaseOCREngine])
     │
     ▼ ProcessPoolExecutor / ThreadPoolExecutor
BaseOCREngine.run(image)  →  EngineResult(text, ...)
     │
     ▼
compute_metrics(GT, text)  →  MetricsResult(cer, wer, ...)
     │
     ▼
aggregate_metrics([MetricsResult, ...])  →  {"cer": {"mean": 0.05}, ...}
     │
     ▼
EngineReport(mean_cer=0.05, ...)

Nouveau pipeline (picarones.pipeline)

[DocumentRef], initial_inputs={IMAGE: Artifact}
     │
     ▼
CorpusRunner.run(spec, docs, factory_inputs, factory_ctx)
     │
     ▼ ThreadPoolExecutor avec backpressure
PipelineExecutor.run(spec, doc, inputs, ctx)
     │
     ▼ pour chaque step
StepExecutor.execute(inputs, params, ctx)  →  {RAW_TEXT: Artifact}
     │
     ▼ (S13+ : EvaluationViewExecutor)
TextView.evaluate(candidate, ground_truth)  →  ViewResult(metric_values)

Le S12 ne livre pas encore l'EvaluationViewExecutor — il vérifie juste que si on appelle compute_metrics directement sur les artefacts produits par le nouveau pipeline, on obtient les mêmes valeurs. Le S13-S14 livrera la couche TextView qui fera ce calcul automatiquement.

Méthode de vérification (test d'équivalence)

Le test tests/integration/test_sprint_a14_s12_executor_equivalence.py implémente l'équivalence :

  1. Construit deux orchestrations consommant exactement le même corpus :

    • _FakeOCREngine (héritant de BaseOCREngine) pour l'ancien runner.
    • _FakeStepExecutor (satisfaisant le protocole StepExecutor) pour le nouveau.
    • Les deux retournent le même texte par document, indexé par doc_id.
  2. Lance les deux runners sur le même corpus.

  3. Calcule CER/WER avec le même compute_metrics sur les sorties des deux runners.

  4. Compare les moyennes CER et WER.

Tolérance : 1e-6, pas 1e-9

Le plan d'origine prévoyait une tolérance de 1e-9 ("équivalence numérique stricte"). La réalité du code montre une divergence de l'ordre de 1e-7 sur certaines fixtures, uniquement à cause d'un arrondi à 6 décimales dans aggregate_metrics de l'ancien runner :

# picarones/core/metrics.py — _stats()
return {
    "mean": round(statistics.mean(values), 6),
    "median": round(statistics.median(values), 6),
    ...
}

Les valeurs brutes (avant round) sont identiques bit-à-bit entre les deux runners. La divergence observée provient strictement du round(..., 6).

Le test S12 utilise donc une tolérance 1e-6 (cohérente avec les 6 décimales d'arrondi) et documente cette décision. Quand l'agrégation finale passera par les types non-arrondis du nouveau code (S22), la tolérance pourra être resserrée à 1e-9.

5 fixtures patrimoniales testées

Le test couvre 5 cas de difficulté croissante :

Fixture Description
fixture_1_court Mots isolés, hypothèse parfaite
fixture_2_paragraphe Phrases avec une coquille
fixture_3_multi_lignes Multi-lignes + accents perdus
fixture_4_abreviations Bibliographie + date erronée
fixture_5_mix_langues Latin + français, multiples coquilles

Plus deux cas limites :

  • test_equivalence_with_perfect_hypothesis — CER == WER == 0
  • test_equivalence_with_empty_hypothesis — texte produit vide

Total : 7 tests d'équivalence, tous verts.

Conséquences pour la migration BnF

À partir du S12, on peut affirmer que :

  • Basculer un benchmark BnF du runner legacy vers le nouveau CorpusRunner ne change pas les chiffres rapportés au-delà de l'arrondi à 6 décimales.
  • Les rapports HTML produits depuis le nouveau pipeline (S22) afficheront les mêmes CER que les rapports historiques (modulo arrondi).
  • Le nouveau CorpusRunner apporte trois améliorations non visibles côté chiffres :
    1. Backpressure (RAM bornée même sur 1000+ docs).
    2. Timeout depuis le début d'exécution (pas la queue).
    3. Annulation propre via threading.Event.

Limites du S12

L'équivalence vérifiée ici porte uniquement sur :

  • Le pipeline OCR seul (un step → un texte → CER/WER).
  • Les métriques principales mean_cer / mean_wer.

Restent à vérifier dans des sprints suivants :

  • S13 : équivalence des projecteurs (ALTO → texte) — couvert par les tests unitaires de formats.alto.projector mais pas encore comparé à extract_text_from_alto legacy.
  • S15 : équivalence des métriques structurelles (Layout F1, reading order F1) — non testées en S12 car elles vivent dans des fichiers measurements/*.py non encore migrés.
  • S20 : équivalence des métriques philologiques (MUFI, abbreviations, etc.) — idem.

Quand ces sprints ajouteront leurs tests d'équivalence, le critère "équivalence numérique fin Phase 3 / Phase 4" sera complet.

Statut

  • Fin de Phase 2 (S12) — équivalence runner OCR ✅
  • Fin de Phase 3 (S18) — équivalence views ouverte (S13-S18)
  • Fin de Phase 4 (S22) — équivalence rapport HTML ouverte