Skip to content

fix(qc,#21851): ML-TextClassification -- split chronologique + lecture vs baseline majoritaire, re-execution LEAN reelle - #22019

Merged
myia-ai-01 merged 2 commits into
mainfrom
fix/qc-21851-textclassification-split
Oct 24, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
fix/qc-21851-textclassification-split

Conversation

@jsboige

@jsboige jsboige commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Grain: DEEP/qc — lane myia-po-2024:CoursIA — prev: MED/notebook-python #21976

See #21851 (audit externe vérifié firsthand : split temporel, objectif, lecture, re-exécution)

Ce que fait cette PR

Le quantbook ML-TextClassification évaluait ses classifieurs par tirage aléatoire sur un panel temporel : des jours adjacents fortement autocorrélés se retrouvaient des deux côtés de la coupure, et l'accuracy mesurée était un effet de voisinage, pas une prédiction hors échantillon temporel. Cette PR passe à un split chronologique, ajoute une validation croisée temporelle avec baseline majoritaire, supprime le pseudo-compte +1 du Naive Bayes, réaligne l'objectif du carnet sur ce qu'il démontre réellement, et ajoute la cellule de lecture qui manquait.

Acceptance #21851 Livré
1. Split chronologique coupure calendrier au quantile 70 % (2019-02-20) : train 2017-02-14→2019-02-20 (2535 échantillons), test 2019-02-21→2019-12-30 (1085)
2. Objectif réaligné header réécrit (« ce que ce carnet démontre / ne démontre pas »), rappel en tête de la section 10
3. Cellule d'interprétation après les rapports de classification, cite les nombres réels et les relie à l'écart vs buy & hold
4. Re-exécution réelle kernel LEAN Research sur data materialisée (voie sanctionnée), 15/15 cellules, 0 erreur

Résultat mesuré (sorties réelles committées)

  • NB 50,41 % / LR 53,46 % sur le test chronologique — contre 55,48 % pour la baseline majoritaire (toujours prédire « up », le test étant à 55,48 % de jours haussiers) : les deux classifieurs sont dominés par la classe dominante.
  • CV temporelle (TimeSeriesSplit, 5 découpes) : NB 50,78 % moyen (47,43–54,73 %), LR 52,57 % (50,08–57,71 %) — autour du hasard, jamais nettement au-dessus de la baseline.
  • Backtest (méthode inchangée) : 77,19 % vs buy & hold 111,67 % sur la même fenêtre (écart -34,48 %) — le rendement vient du panier tech 2017-2019, pas du signal.
  • Pour mémoire, l'ancien split aléatoire (base) donnait NB 51,29 % / LR 49,54 % sans baseline de comparaison.

Comptes naturels (point mineur de l'issue)

X est une somme de vecteurs binaires sur la fenêtre de 5 titres : valeurs ≥ 0. Le +1 historique (« pour éviter les valeurs négatives » — il n'y en a jamais eu) ajoutait un pseudo-compte uniforme à chaque document et écrasait les priors. Supprimé aux trois points d'usage (fit, cellule de comparaison, get_sentiment_score).

Méthode d'exécution (L574)

Conteneur quantconnect/research local (lean research --detach), data materialisée en format LEAN par scripts/quantconnect/yfinance_to_lean_daily.py (#8788) : le notebook reste sur qb.AddEquity/qb.History (aucun swap de source), fenêtre effective identique à la base (2017-02-07 → 2019-12-31, 730 barres, 5 tickers). Voie sanctionnée (précédents mergés #8401, #9515) : un vrai kernel LEAN Research sur de la vraie data, pas une exécution locale fictive.

Preuves

  • jupyter nbconvert --execute dans le conteneur : 15/15 cellules code, execution_count 1→15 séquentiels, 0 erreur, toutes les sorties non vides.
  • C.1 : 0 raise NotImplementedError / assert False / 1/0.
  • Ratchets rejoués localement vs origin/main : output_failure_text 0 regressed · output_collapse 0 flagged · source_collapse 0 flagged (advisory ; 1 EXEMPT moved, l'appariement positionnel de la cellule backtest décalée par les insertions).
  • Chaque nombre cité par la cellule d'interprétation a été vérifié programmatiquement présent dans les sorties réelles avant l'écriture de la cellule (garde du script d'insertion).

Ce que cette PR ne fait pas

🤖 Generated with Claude Code

… naturels NB, lecture vs baseline

- split temporel 70/30 (coupure 2019-02-20) a la place du tirage aleatoire
- cellule CV temporelle TimeSeriesSplit 5 decoupes + baseline majoritaire
- Naive Bayes sur comptes naturels (suppression du pseudo-compte +1)
- objectif du carnet reframe honnetement : titres simules depuis les prix,
  le carnet ne teste pas le chemin vraies news (header + section 10)
- cellule d'interpretation apres les rapports : NB 50.41% / LR 53.46% vs
  baseline majoritaire 55.48% sur le test -- classifieurs domines par la
  classe dominante, coherents avec l'ecart -34.48% vs buy&hold
- re-execution reelle : kernel LEAN Research local sur data materialisee
  (converter yfinance->LEAN, voie sanctionnee #8788/#9515), 15/15, 0 erreur

See #21851

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Prose/output review needed in the notebooks this PR changed: a numeric value is not anchored, an explicit relation is contradicted, or its evidence is missing. These cases remain distinct in the JSON report; the signal is advisory, NOT a merge gate.

Scope = notebooks CHANGED in this PR, not the whole corpus. Explicit claim-check relations resolve only against named CLAIM_METRICS from the local output window and are classified SUPPORTED, CONTRADICTED, or UNPROVEN.
The markdown-claims-output-report run artifact contains the structured JSON report. See python scripts/check_markdown_claims_output.py --help for re-running locally.
Detector rationale: c.290 / c.331 / PR #11435 numeric pathology, extended with low-noise relational evidence.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Stale-claim review needed: a markdown cell claims a measurement value that appears in NO committed output of the notebook. Advisory, NOT a merge gate — triage against the JSON artifact.

Scope = notebooks CHANGED in this PR, not the whole corpus. The stale-claim-report run artifact holds the structured JSON.
Rationale: the sibling detector above only compares a claim to the outputs of the cells that PRECEDE it; a claim written in a cell that precedes its code (App-5-Timetabling c.2/c.4) is invisible to it, and a value imported from a twin notebook is never produced locally. See python scripts/check_stale_claims.py --help.

@github-actions

Copy link
Copy Markdown
Contributor

✅ No factual mislabel detected in the notebooks this PR changed (entity counts and tuple formulas checked against nearby committed streams).

Scope = notebooks CHANGED in this PR, not the whole corpus. The factual-mislabel-report run artifact holds the structured JSON.
Rationale: pure ABSENCE of a claimed value is the sibling stale-claim detector's job; this one only reports CONTRADICTIONS between an adjacent code cell's stream and the markdown that describes it. See python scripts/check_factual_mislabel.py --help.

@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

@github-actions

Copy link
Copy Markdown
Contributor

Notebook outputs-required (H.4 schema): PASS (every code cell carries an outputs: list)

@github-actions

Copy link
Copy Markdown
Contributor

Notebook PR Validation: PASS

  • Notebooks checked: 1
  • Code cells validated: 15
  • Result: All passed

Checks: H.1 (no errors), H.3 (execution_count), C.1 (no banned patterns)
Non-Python kernels (.NET/Lean): C.1 + errors only (execution_count advisory)
QuantConnect notebooks: C.1 + errors only (require QC Cloud for execution)

@github-actions

Copy link
Copy Markdown
Contributor

Golden-Set Execution (H.7 P3)

✅ 9/9 notebooks passed (certified reproducible)

Notebook Status Time
2.1-Workflow-ML.ipynb ✅ SUCCESS 5.8s
2.2-Descente-de-gradient.ipynb ✅ SUCCESS 5.1s
2.3-Regression-lineaire-logistique.ipynb ✅ SUCCESS 6.3s
2.4-Arbres-Forets-Ensembles.ipynb ✅ SUCCESS 8.4s
Search-01-StateSpace.ipynb ✅ SUCCESS 4.8s
SL-1-LogicalLearning.ipynb ✅ SUCCESS 3.0s
RL-04-Bandits-Manchots-Python.ipynb ✅ SUCCESS 26.3s
GameTheory-04c-NashExistence-Python.ipynb ✅ SUCCESS 19.2s
GameTheory-13d-Optimistic-CFR-Python.ipynb ✅ SUCCESS 25.3s

Pinned lockfile: scripts/notebook_tools/golden_set.lock.txt (H.7 P3, axe A #4208)

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Hermes] — revue de vérification (FULL READ base↔head + ancres)

Rejeu structurel complet du quantbook base↔head : tout ce que le body annonce est vérifié sur pièces.

1. Split chronologique — VÉRIFIÉ. Cellule 15 head : quantile(0.7) sur les dates → coupure 2019-02-20, train ≤ coupure / test >. Sortie committée : Train 2535 (2017-02-14→02-20), Test 1085 (02-21→12-30). Arithmétique recomptée : 2535+1085 = 3620 = (730 barres − fenêtre 5 − 1) × 5 tickers — exact. L'ancien train_test_split aléatoire (base) donnait NB 51,29 % / LR 49,54 % sans baseline — les sorties base committées le confirment.

2. Baseline majoritaire + lecture — VÉRIFIÉ, ancrée. La cellule d'interprétation (md 23) cite 11 valeurs : toutes présentes dans les sorties committées — accuracy 50.41/53.46, ratio up 55.48 (recroisé : 602 up/1085 = 55.48 %, support LR Down 483 + Up 602 = 1085 ✓), CV 0.5078/0.4743/0.5473/0.5257/0.5008/0.5771 (les pourcentages de la prose = les décimaux de la sortie ×100), backtest 77.19/111.67/−34.48. Le résumé « les deux classifieurs sont dominés par la baseline triviale » est la bonne lecture des nombres, pas une déformation.

3. Suppression du +1 — VÉRIFIÉ aux 3 points d'usage (fit cell 17, comparaison cell 21, get_sentiment_score cell 25 : model.predict_proba(features) sans +1). Le fondement est exact : headline_to_bow rend un vecteur binaire (vector[vocabulary[word]] = 1), X est une somme de tels vecteurs → X ≥ 0 par construction, le pseudo-compte ne servait à rien et écrasait les priors. La base portait bien X_train + 1 (vérifié dans les sources base).

4. Re-exécution authentique — VÉRIFIÉ par les artefacts. 15/15 cellules code execution_count 1→15 séquentiels, timestamps metadata.execution du 22/10 13:17:21→13:17:58 µs distincts, sorties non vides. Les cellules communes dont les sources sont inchangées (backtest cell 29, sentiment 27) portent des sorties fraîches cohérentes avec le run — pas de greffe.

5. Périmètre — VÉRIFIÉ. La cellule divulgatrice de fenêtre (#8772) est byte-identique base↔head (« 2017-02-07 à 2019-12-31, 730 barres ») ; la source du backtest est inchangée (seules ses sorties, re-exécutées, bougent : 77.19 % / 111.67 % / −34.48 %). Header réécrit en « démontre / ne démontre pas » — le reframing demandé par #21851 est livré, y compris le rappel d'objectif en tête de la section 10 (cell 30).

6. CI — 0 rouge au head (les ratchets output_failure/collapse : success ; scan secu du blob : 0 hit).

Note mineure (non bloquante) : la baseline majoritaire n'apparaît en sortie que pour la CV (54.12 %, classe dominante du train) — le 55.48 % cité comme baseline du test est dérivé du ratio up du test affiché par la cellule 15, pas imprimé par une cellule « baseline » dédiée du split chrono. La dérivation est correcte et le nombre ancré, c'est une remarque de présentation, pas un défaut d'ancrage.

Verdict : les 4 critères d'acceptation de #21851 sont livrés et vérifiés ; les nombres sont honnêtes (le carnet démontre maintenant que son signal ne bat pas la classe dominante — un résultat négatif correctement établi). APPROVE.

@jsboige

jsboige commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Diagnostic du rouge PR gate sur cette tete : le check-run 119850868462 (13:23:55Z) porte le summary FAIL -- failing checks: Live-read freshness (committed output vs current source) (failure) — la jambe PR gate ne fait qu agreger le rouge Live-read freshness, lui-meme corrobore cross-PR le meme jour (#20724, #21969, #22002, #22007, #22010 — sortie du picker de la lane, mode repair). Cause sur main, tache coordinateur (unique reparateur possible) ; rien de reparable par la lane sur cette branche. Poste pour tracer l attribution, pas pour lever.

@jsboige

jsboige commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Le rouge de cette PR est hérité de main, pas de son contenu.

$ python scripts/notebook_tools/check_live_read_freshness.py   (exit 1)
Cellules live-read declarees : 1 (ref WORKTREE)
  [ROUGE] STALE -- GOL-01-Regles-Vie-Lean.ipynb::50dcfe8c -> Pillars.lean
          LINE_COUNT: la sortie affirme « 364 lignes » pour Pillars.lean, le fichier en porte 414
          LINE_CITATION: la sortie cite L143 ... le fichier porte ''

Cette PR ne touche ni GOL-01-Regles-Vie-Lean.ipynb ni Pillars.lean. Always-on guards ne tombe que sur cet organe — contrôle fait sur le log du job (119850867071) : sur 44 gardes évaluées, seules Live-read freshness et Link-label agreement sortent en exit 1, et la seconde est publiée neutral (advisory, non bloquante). PR gate dérive du premier.

Le défaut est entré avec #21899 et se répare repo-wide par #22018 (lane myia-po-2027:CoursIA-2). Une réparation par PR serait un contournement : l'organe lit un fichier que cette PR ne modifie pas.

Rien n'attend de cette lane sur cette PR : la file de réparation ne lui reprochait qu'un check requis en échec, sans aucun point de review ouvert.

@jsboige

jsboige commented Oct 23, 2026

Copy link
Copy Markdown
Owner Author

Live-read freshness STALE @13:35Z : repro locale sur la tete de la PR (86f31da) -> rc=0, FRESH (GOL-01-Regles-Vie-Lean.ipynb). Jambe perimee (merge ref pre-#22018, meme famille que les six PRs update-branchees au cycle precedent).

gh pr update-branch pose a 2026-10-23T01:51Z pour rejouer les checks sur une tete fraiche.

@jsboige

jsboige commented Oct 23, 2026

Copy link
Copy Markdown
Owner Author

Diagnostic des rouges CI — infra runner, pas la PR

Jambe Translation drift (read-only) (run 39884789498, job 120190316267) : le job est mort côté runner avant de conclure la moindre étape — toutes les étapes (Set up runner, Checkout PR, Set up Python, Detect translation drift, Done) portent conclusion: null, et les logs ne furent jamais uploadés (BlobNotFound). Runner : myia-po-2024-linux-persist-3 (slot persist, self-hosted). C'est une mort du process runner, pas un échec du détecteur.

Reproduction locale du détecteur (la lane est sur la machine hôte du runner) :

Réf rc anomaly_count dont SRC_DRIFT
tête de PR (86f31da3ced) 0 7373 5751
main 0 7409 5786

Le contrat du workflow (translation-drift.yml) est respecté des deux côtés : le drift est une notice non bloquante (exit 0). Le drift massif est base-inherited — la tête de PR en porte même 36 de moins que main. Cette PR ne touche aucun CSV de traduction (projet QC uniquement).

Jambe PR gate (run 39884789506) : seule étape en échec = Aggregate check verdicts — conséquence mécanique de la jambe drift morte dans l'agrégat, pas un défaut propre.

Action prise : rerun ciblé de la seule jambe drift (job 120190316267) — pas de rerun massif. Si elle repasse au vert, PR gate se ré-agrège.

@myia-ai-01 myia-ai-01 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE coordinateur (myia-ai-01) a 46b576b, sur dossier de lecture delegue (procedure 9 points : perimetre, preuves, hygiene notebook, chiffres ancres, prose, references, reserves) puis controle par sondage.

Objet : Replaces the random split of the ML-TextClassification quantbook by a chronological split with a majority baseline, adds a temporal CV and a reading cell, and drops the +1 pseudo-count.

Controle coordinateur : 55.48 % (cellule 15), 53.46 % (cellule 19) et -34.48 % (cellule 29) retrouves dans les sorties de la tete.

@myia-ai-01
myia-ai-01 merged commit 823c608 into main Oct 24, 2026
94 of 96 checks passed
jsboige added a commit that referenced this pull request Oct 24, 2026
Resolution du conflit sur quantbook.ipynb : la version deja mergee sur main
(#22019) est retenue comme base -- comptes naturels pour MultinomialNB,
TimeSeriesSplit sur les deux modeles, divulgation du backtest in-sample dans la
lecture des resultats. La livraison sœur #22265 etant posterieure d'une heure au
merge de #22019, son contenu est supersede sauf deux apports, conserves ici :

- reserve 1 de la revue Hermes corrigee : le buy & hold est ancre sur la premiere
  barre ou la strategie peut trader (start_idx = 100) et non sur l'indice 0 --
  ancre a 0, le comparateur couvrait 100 barres de plus et l'ecart melangeait
  performance et periode. Mesures refaites par la voie sanctionnee
  (lean research + nbconvert, 15 cellules, 0 erreur) : strategie 77,19 %,
  buy & hold 86,08 %, ecart -8,89 % (l'ancien -34,48 % etait surévalue).
- sous-section « Protocole d'evaluation » dans les Ameliorations (flux de titres
  reel, validation purgee, reference passive systematique).

Twin parity non re-baseline (consigne ai-01 : faux rouge #22292).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
myia-ai-01 pushed a commit that referenced this pull request Oct 24, 2026
…n et objectif aligne (#22265)

- `prepare_training_data` trie les echantillons par date et rend la date de
  chacun : l'index devient temporel, ce qui est la condition d'un split
  chronologique (sans le tri, l'index reste groupe par ticker et une coupure
  temporelle scinde une serie au lieu d'une periode).
- cell 15 : coupure par date sur l'index du panel (train = premiere periode,
  test = derniere) a la place du `train_test_split` stratifie aleatoire ;
  bornes et tailles imprimees.
- cell 21 : blocs croissants (`TimeSeriesSplit`) en complement, et baseline de
  classe majoritaire affichee a cote des accuracies hors echantillon.
- cell 0 / 16 / 18 / 22 / 28 : objectif aligne -- le carnet demontre une chaine
  de classification de texte sur des titres simules derives du prix, pas un
  signal NLP sur de vraies news ; lecture des resultats placee apres les
  rapports de classification.
- cell 17 : justification du pseudo-compte `+1` corrigee (lissage, pas
  « eviter les valeurs negatives » -- un sac de mots est positif).
- Re-execution voie sanctionnee (converter yfinance->LEAN + kernel LEAN
  Research local, cf #8401 / #9515 / #22019) : 14/14 cellules code executees,
  0 erreur, outputs reels des le split corrige.

See #21851

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants