diff --git a/PLAN.md b/PLAN.md index ae70bd8..bb3bbcc 100644 --- a/PLAN.md +++ b/PLAN.md @@ -448,8 +448,11 @@ data budget asks. | V35 | **ML Lab: the number stops flattering itself.** Two of these are not missing features but **method defects in shipped code**, which is why they come first in a lab whose selling point is honest evaluation. **(1) The winner is picked on the test set.** `LeaderboardTable.tsx:42` sorts nine models by `primary` — the metric computed on test — and crowns `sorted[0]`. Taking the maximum of nine draws on ~180 test rows biases the headline figure upward; V20's paired bootstrap softens the comparison but the crowned number stays optimistic. Fix: a third split — train / validation / test — selecting on validation, reporting on test, and **showing the gap between the two**, which is itself the most useful lesson the lab can teach. **(2) The split is always random, even on dated data.** `splitIndices()` stratifies at random, full stop — while V10 already derives year/month/day from a date column and V8 does time series, so dated files arrive routinely. A random split puts the future in training and the past in test: the model looks excellent and collapses in production. Fix: detect a date column and offer a **chronological split** (oldest 80% trains), announced; same logic for a repeated identifier — the same customer on both sides is the same leak. Then two additions in the same spirit: a **target-leakage detector** — a lone column predicting at 99% is almost always a leak (« amount_refunded » predicting « fraud ») and must show as a warning, not a victory: small to write, striking to demonstrate, and nobody does it — and **repeated cross-validation for the leaderboard**, because ~180 test rows carry roughly ±3 points of standard deviation and ranking two models one point apart is meaningless; 5×2 CV with intervals makes the ranking defensible. | Owner request (22/08/2026): what to improve in /ml. The audit found two defects rather than gaps — a lab that sells honest evaluation cannot ship a headline figure it knows to be optimistic, nor a split that leaks on dated data. | | V36 | **ML Lab: the gaps that were deliberately left open.** **Class imbalance** — weighting was explicitly descoped in V16; class weights in logistic regression, tree, forest and gbdt, announced like every other choice, finally complete what the threshold panel started. **Multiclass thresholds** — set aside since V16 and still open. **Choosing the ranking metric** — accuracy and RMSE are imposed today, while on an imbalanced problem F1 or recall is the right criterion and the ranking changes with it; small to build, and it makes the leaderboard answer the user's question rather than ours. **An ensemble of the best** — average or vote over the top three: typically 1–3 points, free in compute since the models are already trained, and it teaches why ensembling works. **What this wave deliberately does not do**: add a tenth model family (nine is plenty; a tenth improves neither honesty nor understanding), build an AutoML « we handle everything » mode (the opposite of a lab that shows its decisions), or bring in tabular deep learning (high cost, no gain at this scale, and no longer hand-written). | Each item was consciously deferred in an earlier wave rather than forgotten; grouping them keeps the descopes visible instead of letting them quietly become permanent. | | V37 | **ML Lab: speed and the comfort of long sessions.** **Parallel training** — the zoo trains sequentially in a single worker; N workers means N cores, and at a million rows that is a different experience entirely. The V25 benches already exist to measure it before and after, so the gain is published rather than claimed. **Comparing more than two runs** — V21 compares two; three or four changes what the tool is for, and the diff machinery is already written. **Resuming an interrupted run** — closing the tab loses everything today, while V13 (artifacts) and V19 (persistence) already provide the storage; what is missing is a checkpoint between model families and the offer to resume. | Comes last on purpose: speed and comfort matter, but a faster wrong number is still a wrong number. V35 first, then V36, then this. | +| V38 | **Data Studio: reading the file exactly as it was written.** The headline item is a **defect in shipped code, not a missing feature**. `Papa.parse` is called with `skipEmptyLines: true` and nothing else — no encoding, no decimal separator — and `parseNumber` ends in `Number(cleaned)`. A French Excel export therefore breaks silently: `12,5` becomes `NaN`, the column is classified **text** rather than numeric, and every downstream stage one-hot encodes what should have been a number; a windows-1252 file displays `Québec`. Nothing warns, nothing refuses — the pipeline simply produces a worse model. Fix: **detect encoding and decimal separator and announce both** (« séparateur décimal : virgule, détecté sur 412 valeurs »), expose explicit **delimiter / encoding / decimal** selectors for the cases detection cannot settle, and show a **5-row preview before committing to the load** so a wrong guess is caught in two seconds rather than three panels later. The same pass covers thousands separators and dates written `31/12/2025` instead of ISO. | Owner request (22/08/2026): what to improve in /data. The audit found a defect first: a French-locale CSV — the single most likely file this owner's users will open — loses its numeric columns silently, and silence is the part that violates the project's rules. | +| V39 | **Data Studio: a recipe that works column by column.** `RecipeOptions` today applies `missing` and `clipOutliers` to the **whole file**: one strategy for every column, however different they are. A median makes sense for an age and none at all for a postcode. Make the recipe an ordered **list of per-column steps** — the current global settings becoming the defaults a column may override — and add the strategies that are missing: **median / mean / constant / a « MANQUANT » category** for categorical columns. With them comes a rule the tool should never break: **imputing without marking destroys information**, so every imputed column gains an optional **missing indicator** (`col_absent`), which is frequently predictive in its own right (a blank field is rarely blank at random). The recipe stays what it already is — a replayable, inspectable object — so the per-column version remains exportable, re-appliable to a new file, and legible as a list of named decisions. | A single global strategy is the kind of default that looks tidy and quietly makes the data worse; per-column steps cost little to build because the recipe is already an object, not a pile of checkboxes. | +| V40 | **Data Studio: validity, drift, and an auditable diff.** Quality is measured today as completeness and consistency of type; what is missing is **validity** — a value can be present, well-typed and still impossible. Named rules, each stated in plain language: an age of 200, a date in the future, a percentage at 130, a malformed postcode. Then **cross-column consistency** (`date_fin < date_debut`, `total ≠ quantité × prix`), for which **V29's DuckDB is already the engine** — the rules are SQL, and they run on the file that is already registered. Then three things that make the studio auditable rather than merely helpful: a **replayable reference profile** so a second file can be checked for drift against the first (the same idea as the V22 model manifest), a **before/after diff of the rows a recipe modified** — which rows, which columns, which values, not just a count — and a **breakdown of the quality score** so the number is explained by its parts instead of being asserted. Ends with **Parquet export**, nearly free now that DuckDB is loaded (`COPY … TO 'x.parquet'`). **What this wave deliberately does not do**: a spreadsheet-style cell editor (hand edits break reproducibility — the recipe is the record), fuzzy deduplication (guaranteed false positives on names and addresses, silently merging two real people), or model-based imputation (opaque, and it fabricates values that look plausible). | Comes last because it builds on V38's faithful read and V39's per-column recipe: validity rules on mis-parsed numbers would flag the parser, not the data. | -**Ordering**: V35 comes before V36 and V37 because two of its items are defects in shipped code, not features — a lab that sells honest evaluation fixes those first. V32 ships one finished tutorial before any reference page — the tutorial is the template the rest copies, and settling it late means rewriting everything. V30 and V31 both start with a bench, because neither « a bigger model » nor « it still makes mistakes » is a measurable statement today; no wave starts without an explicit launch command. V23 first (owner request); V24 keeps its vocabulary capped — +**Ordering**: V38 comes before V39 and V40, and for the same reason V35 comes first in its own group: its headline item is a defect in shipped code, not a feature — a studio that promises honest data cannot silently turn `12,5` into `NaN`. V35 comes before V36 and V37 because two of its items are defects in shipped code, not features — a lab that sells honest evaluation fixes those first. V32 ships one finished tutorial before any reference page — the tutorial is the template the rest copies, and settling it late means rewriting everything. V30 and V31 both start with a bench, because neither « a bigger model » nor « it still makes mistakes » is a measurable statement today; no wave starts without an explicit launch command. V23 first (owner request); V24 keeps its vocabulary capped — V25 (delivered) chose announced sampling and a named memory guard over the typed-array rewrite, which measurement showed unnecessary; widening the vocabulary stays possible later. Set aside for now: multiclass thresholds. V12