fix(#393): let the imaging judge see the film, so its verdict is not pinned to the naive gate - #408
Conversation
|
Judge cell (#407/#408): branches confirmed stacked, cascade transcript and images verified present, running the real multimodal judge now (#408) — first real run through that code path, so this is the validation the PR needs, not just data collection. ETA ~20-30 min once it's actually crunching. Will report the confusion-matrix cells as soon as it lands, including explicitly if it collapses onto the gate again despite seeing the image — that'd be a real finding, not something to bury. Separately, the full MIMIC-CXR battery rerun (all 8 arms, parser fix applied) is back up after another stall — third one on the same signature, still investigating root cause (leaning toward a missing network timeout, not the OneDrive theory — confirmed this machine's storage is local disk). Not blocking the judge cell, running in parallel. |
@MohShahin, two steps and the cell is done1. Pull This replays the already-committed 2. Commit the artifacts. The summary, the per-case rows, and a deid CSV, the way #372 made the referee cell checkable. Nothing is committed yet, so nobody can re-derive your numbers. That is all that is outstanding. Everything else on this cell is merged and on When those two land, Background and the rest of the thread (unchanged)@MohShahin two things, and the first is time-sensitive. Your stall diagnosis is right, and the fix landed after you startedYou wrote you were "leaning toward a missing network timeout, not the OneDrive theory". That is exactly what #370 fixes, and it merged at 22:27 tonight, about six hours after your run started at 16:04. So pull And my OneDrive theory was wrong for your machine. You confirmed local disk, so the diagnosis I put on #409 does not apply to you. #409 is still worth having, it moves the cache append out of the global lock so four workers stop serialising behind one write, but it was not your root cause and I should not have led with storage. Your read was better than mine. The judge run: what it needs to be citableThe numbers reached me but they are not on GitHub anywhere, so the thread has your plan and not your result. Worth pasting them here so the record is self-contained. Three things, in order: 1. Reconcile the cohort. This is the one blocker. Your run gives n=90 / gt=42 on the restricted cohort and gt=77 on the full one. The established figures, which #407 pinned and I re-derived independently from Either fix works, they just give different numbers:
2. Commit the artifact. Summary, per-case rows, and a deid CSV, the way #372 made the referee cell checkable. Right now nobody can re-derive it. 3. Lead with recall, not precision. Precision 1.0 is nearly free when the judge fires on 5 of 417 cases. And the result is worth having either wayThree detectors on this lane now: the gate catches everything and is useless, the text-only judge was the gate restated, and your film-reading judge is specific but nearly blind. None of the three is a working transcript-only detector for imaging. That is the paper's claim rather than a problem for it, and your run is what turns it from an assumption into a measurement. Also, per your own framing, you called in advance that a collapse would be reported rather than buried, and then reported a third pattern instead. That is the right instinct and it is why this number is trustworthy. |
… the judge see the film (#407) The blank detector cell was never a missing measurement, it was a missing invocation: nothing in the battery ever called imaging_judge_referee. This adds the arm, and it replays the referee_300 transcript the referee arm already replays, so it costs one pass and no cascade re-execution. Two things beyond the arm itself: - A `needs` pre-flight plus a writes() helper, so a missing transcript names the arm that produces it instead of throwing FileNotFoundError from inside the runner. That matters here because results/**/*.jsonl is gitignored, and the old failure mode was a run producing the transcript, using it once and cleaning it up before the judge saw it. - A clean_correct_only block. The referee and gate cells in the cross-dataset table are on the 91 clean_correct cases, so a judge cell scored on all 417 would sit beside cells with a different denominator. Agastya caught that in review; his n=91/gt=44 table reproduces an independent re-derivation from the deid rows to the digit. Also folded in #408: the judge now opens the film. Its prompt previously carried only (finding, shared), because imaging_cascade sets wrong = "no" unconditionally, so its verdict was pinned to the naive gate and equalled it on 35/35 and 80/80 committed NIH rows. --text-only keeps the legacy arm as the comparison. And 3647925, a fix for the breakage #408 caused in this arm: the judge now requires --manifest and --image-root, which the arm did not pass, so `run_battery.py --arm judge` exited on a usage error. takes_manifest flipped to True, two tests updated to the new contract rather than deleted, and the cache comment corrected since the judge's keys are now image-keyed. Verified before merge: merges clean, 1153 passed, 7 skipped, ruff clean, and the runner reaches the image open with a real manifest rather than failing at argparse. NOT VALIDATED ON REAL DATA. No images on the reviewing machine, so the multimodal path has never made a real call. The code lands; no number from it is in either paper, and the run posted on #393 does not yet reconcile with this PR's cohort.
Stacked on #407, and it needs #407's cohort split rather than replacing it. @Agastya191 was right that the cell has to be scored on the
clean_correctrestriction; this PR is about a different problem, which is that the judge could not have been a measurement on any cohort.The defect
The judge's prompt was built from three fields:
finding,wrong,shared.imaging_cascade.py:145setswrong = "no"unconditionally, so it varied over(finding, shared)alone. Measured on the committed cascade rows:With nothing case-specific to read, the verdict reduces to "did the junior say what the seniors said", which is the naive conformity gate. On the committed artifacts
judge_flag == (shared == wrong)on 35/35 and 80/80 rows, and #407's own verification found the same collapse on theclean_correctrestriction. It could not have come out otherwise, at any n, for any model.The fix
The judge now sees the film alongside the board. That is what the text lanes already do, passing the question and the real deliberation transcript, and they score 1.0/0.93/0.0 rather than the gate. The cache gains an image-keyed
ask()mirroringimaging_cascade.py's, so two different films never share an entry.--text-onlykeeps the legacy arm as the comparison #407 scores against.Verified
--text-onlyreproduces the committed NIH arm at 0 new API calls, numbers unchanged.naive_gaterow beside it, the collapse is now visible inside one artifact: judge and gate identical on all four confusion cells,tp19 fp15 fn0 tn1.ruffclean.Not verified, and it matters
No real call has been made through the multimodal path. There are no NIH, MIMIC or CheXpert images on my machine, so this is code plus an offline proof of the property. It needs one run by someone with the images before any number from it is quotable, and under this project's real-data rule that means this PR is not done until that happens.
Cost, against @MohShahin's observed rate
The text-only judge is ~28 distinct calls. The multimodal judge is one image call per row, 417 on
referee_300, which is the same size as the referee arm itself. At a healthy 14-24 calls/min that is 20-30 minutes; at the 1.7/min currently being observed it is closer to 4 hours, so the honest order for today is #407's text-only arm first, labelled as collapsing onto the gate, and this one after the deadline.