Skip to content

Ais train optim DO NOT MERGE - #1371

Draft
constantinpape wants to merge 61 commits into
devfrom
ais-train-optim
Draft

constantinpape wants to merge 61 commits into
devfrom
ais-train-optim

Conversation

@constantinpape

Copy link
Copy Markdown
Contributor

This contains all the code for AIS optimization.

This is just for reference, we don't want to merge this.

constantinpape and others added 30 commits September 6, 2026 11:34
Snapshot of the APG 2D/3D optimization experiments on this branch:
library changes to the v2 automatic prompt generator and UniSAM2
decoder width, the campaign screening/training/benchmark scripts and
their JSON configs, campaign notes and reviews, development check
scripts, and the accompanying tests.

Committed as-is so the full experimental state stays on this branch;
the clean-up happens on a separate branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every optimization tested in the 2026-09 APG campaigns was refuted under the
generalization rule and is removed from the library, together with the
learned prompt-selection machinery that PR #1340 had merged earlier:

- micro_sam/v2/automatic_prompt_generation.py drops the learned multimask
  selector, filter and refinement gate (multimask_scorer,
  multimask_selection, score_filter, set_multimask_models, gate kwargs),
  the structural hooks (prompt_type, arbitration, fusion, recover_residual
  and their helpers), the label-free refinement gating rules
  (protect_neighbours, negative_scope, gate="isolated", isolated_fallback,
  touch_radius), the 3D learned candidate filter (return_metadata,
  VOLUME_CANDIDATE_FEATURE_NAMES, candidate_* options) and every
  experiment-only hook (prompts=, keep_trace, return_multimask_features).
  The plain second-round refinement (points / boxes / masks, 2D and the 3D
  anchor-slice variant) stays, as does the tiled generator.
- micro_sam/v2/multimask_selection.py is deleted; the annotator no longer
  passes score_filter.
- finetuning/v2/evaluation keeps the generic harness core (2D/3D manifests
  and runners, comparator, SLURM submitter, task registry, 3D case tools,
  pre-existing library benchmarks) stripped of the learned/structural
  plumbing; the 17 feature-specific screens/trainers/readers and 39 of 44
  configs are removed. The 3D runner loses its trace-based recall
  attribution and anchor export. The stray test_apg_3d_tiling.py becomes
  check_apg_3d_tiling.py so bare pytest no longer collects it.
- Tests for the removed features are deleted or trimmed; the two
  comparator test files are merged into test_apg_optimization.py.

The full campaign state stays preserved on branch apg-optim-fable (356b76d).
This commit starts a new implementation checksum epoch
(f76ee7170ca77da882c0078dfaa5b301, seven files).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every campaign note gets a banner stating that the mechanisms it describes
were tested, refuted and removed on this branch, that the full state is
preserved on apg-optim-fable (356b76d), and which sections describe kept
functionality (the second-round refinement). A "Status on this branch"
section at the end of each note maps the removed scripts, configs and
library hooks.

EXPERIMENTAL_SETUP.md records the reproducible set-up of the campaigns for
future instance-segmentation (AIS) optimization: environment and SLURM
presets, datasets and splits, checkpoints and the v4 staging recipe, the 2D
subset manifests and the leak-free 3D crop manifests with their checksums,
metrics and aggregation, parameter defaults and controls, acceptance and
generalization gates, the timing-trial protocol, implementation checksum
epochs (now f76ee7170ca77da882c0078dfaa5b301), the output-root layout and
the steps to run an AIS campaign on the same set-up.

The two tiled-3D-APG design notes move from the repository root into the
notes directory unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…al_features

torch_em 0.10.4 takes 'initial_features' as an argument of UNETR3D and sizes
the decoder from it, so the joint/v4 checkpoints (32-wide decoders) load
strictly through the plain kwargs forwarding UniSAM2 already had on dev.
Verified against joint/v4 hvit_t geodesic best.pt: all 325 decoder keys match
with no rebuild. micro_sam/v2/models/util.py returns to its dev version; the
UniSAM2 width test stays. torch_em 0.10.1 built a 64-wide decoder regardless,
so v4 checkpoints now require the newer torch_em (dependency floor to follow).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
submit_all_evaluations.py defaulted to the 'super' environment, which does
not exist on grete, and to 3g.40gb slices for 3D jobs, which are scarce; it
now activates 'new-stack' and puts 3D jobs on 1g.20gb (the (8, 512, 512)
production crops peak around 6 GiB). The parameter_search.py array template
activated 'super' as well and now uses the same environment; as a checksum
file this starts implementation epoch e1903b1b3c1e4e3610c71e1d0bd81f1d
(harness-only, results unaffected).

EXPERIMENTAL_SETUP.md gains section 14 with the 2026-09-06 baseline reruns of
the cleaned harness on joint/v4 geodesic defaults: the 2D subset benchmark
(bit-identical to the recorded v4 controls), the 3D deep crops (holdout
identical, primary identical on 56/57 crops) and the AIS/APG production
defaults on nine test splits, each with its run directories, result files
and reference numbers. The campaign notes point to it from their status
sections.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Predict every manifest sample once, cache the (4, *spatial) decoder prediction with its labels, and run
configurations, parameter sweeps, seed diagnostics and ground-truth oracles on the cache. Run directories
follow the APG layout so the comparator reads them; the report applies the generalization gate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…cessing

The geodesic decoder predicts a distance magnitude that falls to zero along the boundary of every object
it recognised, while a false foreground region carries no such dip. drop_instances_without_boundary_dip
removes instances whose median boundary magnitude exceeds a threshold; flow_instance_segmentation exposes
it as boundary_magnitude_max (off by default). The AIS benchmark mirrors it, the cached sweep scorer applies
it, and the campaign notes record the diagnostics and prototypes that led to it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the A1 screens

report_ais_sweep.py joins the per-dataset sweep tables of a grid over several manifests and scores every
combination as a shared default: balanced mSA, change per dataset against the library defaults, the gate
verdict and the ratio to each dataset's optimum. The notes record the Phase 1 diagnostics, the three
prototypes and the epoch A1 filter screens.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
evaluate_automatic_segmentation.py --ais_params reads a benchmark-style configuration, resolves it against
the library defaults (sparse and dense) and scores the test split with it; submit_all_evaluations.py passes
--ais_params and --result_tag through. The sweep ranking accepts the deep 3d manifests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-aware

The flow post-processing defaults of hvit_t become sigma 1.0, min_size 50 and the boundary-magnitude
filter at 0.4 for images; volumes take min_size 200 and foreground 0.6 through a new 'sparse_volume'
override table that default_postprocessing resolves by dimensionality. Against the registry values this
gains +2.4 % balanced mSA on eleven 2d development datasets (9 up), +4.3 % on the 2d holdout and +22 %
on the 3d LM crops with the joint/v4 geodesic checkpoint. The filter now derives every instance's boundary
median from one sort over the boundary pixels, 18x faster with identical output.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the filter test

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…boundary filter

The volume overrides (foreground 0.6, size floor 200 voxels) that won on the 3d tuning crops lost on the
seven test-only 3d datasets (two up, two below the loss limit); the registry values plus the filter gain
+3.4 % there with every dataset up, and +4.7 % / +9.7 % on the tuning crops.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…results

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lower_height_under_seeds lowers the height map under the seeds ('zero', or the minimum of a ring around the
seed) so that the monotone flooding of the watershed does not hold a seed's front at the seed's own height.
Off by default; the harness and the sweep scorer mirror it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ampaign

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…automatic branch (AIS epoch A5)

The decoder can now predict a fifth channel, the touching boundary between objects
(touching_boundaries / contact=True in the label transforms, Dice + BCE in
DirectedDistanceLoss, sigmoid in CustomActivation), and the foreground can be
trained with a per-pixel BCE weighted in a band around the object boundary
(boundary_weight). Inference infers the channel count from the checkpoint and
flow_instance_segmentation takes the contact map as an opt-in ridge
(contact_weight) or an excluded-then-reassigned mask (contact_mask_threshold);
the default path is unchanged. The AIS harness mirrors the keywords, gains the
fg_area_ratio diagnostic and two contact configs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, submission, readers

train_ais_decoder.py trains the UniSAM2 decoder with the encoder frozen at the v4
joint weights on the train splits of the AIS tuning datasets (four variants:
baseline, contact channel, boundary-weighted foreground, both), warm-started from
the v4 decoder; stage_ais_decoder_checkpoint.py writes a lean joint-format file the
evaluation harness reads through MICRO_SAM2_JOINT_CHECKPOINT_ROOT; the submitter
writes single-GPU SLURM jobs. report_ais_decoders.py compares the staged
checkpoints on the cached benchmark runs (gate, merge/absorb shares, extent
figures) and diagnose_decoder_fields.py measures the contact geometry and the
foreground extent of cached predictions. Notes and the proposal move into
optimization/notes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ecoder training

Python 3.14's fork-server start method made every worker re-import the
environment (minutes per job, and every epoch for validation); a 512^2 file
with fewer than three objects made torch_em's sampler give up and end the
run. The subset wrappers now redraw or step to the next file instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…coder campaign notes

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the chained evaluation

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…aign

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… filter

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…her of the decoder campaign

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
constantinpape and others added 29 commits September 7, 2026 12:33
…reens

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mpaign

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…er campaign

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s and the round-2 hand-over

object_boundaries / contact_mode="all" make the auxiliary channel the inner
boundary of every object instead of the touching boundaries only; the
decoder campaign gains the variants boundary and boundary_fgcal, the
unattended scripts take their variant lists from the environment, and
notes/AIS_DECODER_HANDOVER.md describes how to launch, chain, monitor
and read out round 2.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The two boundary-channel trainings run (48000 iterations each, 3g.40gb
slices); finalize_round2_reports.sh submits their dec-top1 screens and
writes the six-decoder overview unattended, so no step depends on the
session. Also records why the jobs would not start (our own sweep array
at the head of grete:preemptible) and why the round-1 finalisation died
(the driver script was edited while a job slept in its wait loop).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
diagnose_decoder_fields.py assumed the touching-boundary target, which
calls the round-2 boundary head's correct pixels negative (on tnbc the
contact decoder's precision reads 0.31 against the touching lines and
0.82 against every object boundary). --contact-mode selects the target;
recall_touching and recall_bg_boundary are reported in both modes, so a
touching-target and a full-boundary head can be read side by side.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Section 4.5: the 3D table of all four decoders (the plain fine-tune
already loses 25-60 % per LM family, the five-channel decoders are 0
because the magnitude filter removes every instance), the field
diagnostics of all four, and the fifth channel rescored with the
mode-independent recalls - both round-1 heads learned the touching
target but stay silent exactly where the campaign lost mSA, and the
boundary-weighted foreground loss makes the head less confident.

The hand-over now covers reading the chained round-2 results rather
than launching them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With the touching target the mask mode kept a contact line free; with
the full boundary it is the classical erode-flood-dilate scheme, so a
confident head makes it active for the first time - and both modes can
shave objects only a few pixels wide, which the mechanism columns
separate from merges.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
baseline's own optimum wants foreground_threshold 0.4; fgcal and both
want 0.5. The shared dec-top1 configuration is therefore fgcal's
optimum and 1 % below baseline's, so the boundary-weighted foreground
loss is worth +1.3 % rather than the +2.4 % of section 4.3 - and its
mechanism is the threshold calibration, which is what point 4.1 claims.
The move to density 50 / sigma 0.5 also belongs to the loss-changed
decoders only; baseline keeps the production density and sigma.

The cached sweep scorer reproduces the screened runs to 0.05 %.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
contact's own optimum is 0.4083 at foreground_threshold 0.6, i.e. -3.8 %
against baseline's own optimum - within a point of the -4.7 % measured
at the shared defaults, so "it was only mis-tuned" is not the
explanation. The optimal threshold runs baseline 0.4 -> contact 0.6 ->
fgcal/both 0.5: the contact task pushes foreground mass outward and the
boundary-weighted BCE pulls it back, which the fg_area_ratio column
shows at a fixed threshold on ten of eleven datasets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 16:53 edit to launch_tuning_after_caches.sh took down
ais_decoder_tuning at 19:03, six hours after it took down
ais_decoder_finalize: one edit kills every job sleeping in that file,
hours apart. Nothing was lost - the sweeps were already submitted and
all four rankings are written. The frozen copies are byte-identical to
the on-disk files, so tuning2 and finalize_r2 are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The session is stopped so a later one can watch the trainings land.
Records where they stood at 21:29, and makes the first command on
resume checking how they ended - the evaluation is afterany, so a
timed-out or preempted run is evaluated silently rather than failing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both round-2 trainings finished the full budget, but the tuning
launcher re-ran the four round-1 sweeps and never submitted the
round-2 ones: SBATCH_EXPORT=none is set on this cluster, so sbatch
does not propagate the submitting environment and WAIT_VARIANTS /
VARIANTS fell back to the round-1 defaults. The failure is silent and
produces plausible work.

The launcher now takes --wait / --rank arguments; the redundant 46
tasks are cancelled, their job directories moved to jobs/_superseded/
so tasks_done stops shadowing the finished round-1 ones, and the
round-2 sweeps, contact screens and a ranking job are submitted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The head is confident where the contact head was silent (deepbacs
0.015 -> 0.505 recall, tnbc 0.032 -> 0.587, puma 0.123 -> 0.566,
neurips 0.128 -> 0.460), so the class-imbalance diagnosis was right.
At dec-top1 + ridge 1 the boundary decoder reaches +3.3 % balanced on
dev with 10 of 11 datasets up - the best of the campaign - and repairs
covid_if (-19 % -> +0.2 %) and deepseas (-27 % -> +20 %). It fails the
gate on deepbacs alone (-14.1 %), whose rods are shaved and split
rather than merged, and which loses for every five-channel decoder.
Stacking fgcal on top still hurts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correction: the boundary head has no pixel above 0.5 on 33 of 50
dic_hepg2 crops, but its soft probability is 0.133 on the true boundary
against 0.0020 elsewhere. The ridge reads the soft map and swings that
dataset by 30 points; the mask thresholds and cannot. Read the soft
contrast, not only Dice and recall at 0.5.

The 24-configuration screen also separates the two modes: the mask is
the uniform lever (7/11 up, worst -0.5 %) and does not shave thin
objects, while seeded splits rise monotonically with the ridge weight.
And no post-processing rescues deepbacs - it is -11.7 % with neither
mode, so the gate failure is a recipe question.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
boundary's sweep optimum sits at foreground_threshold 0.5, between
baseline's 0.4 and contact's 0.6, and keeps the production density 10 /
sigma 1.0 - confirming that the regime shift to density 50 / sigma 0.5
belongs to the foreground loss change, not the fifth channel.

Also restates the headline: +3.3 % is against baseline at dec-top1,
which is 1 % below baseline's own optimum, so the honest figure is
+2.3 %. Screens of each decoder at its own optimum with and without
the ridge close the missing cell the sweep cannot evaluate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
baseline at its own optimum scores 0.4244 dev / 0.4052 holdout, not
0.4200 / 0.4025. Against that reference the boundary channel is +2.3 %
on dev with 7 of 11 up (deepbacs -10.2 %, neurips -6.1 %, tissuenet
-5.0 %, puma -0.3 %) and +0.8 % on the holdout, where round-1 `both`
still reaches +1.5 %. tissuenet alone swings 14 points from the
reference change. So the target change is worth +4.4 points over the
contact channel, but the fifth channel is still not a win over the
plain fine-tune, and nothing passes the gate.

Also: boundary's own sweep optimum is the wrong basin once the ridge
exists (dic_hepg2 -8.6 % against +26.5 %), because the cached scorer
cannot evaluate contact keywords at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
density_threshold and sigma track the foreground loss alone: all three
decoders with the boundary-weighted BCE want density 50 / sigma 0.5,
all three without it keep the production 10 / 1.0, and the fifth
channel has no influence - which settles 4.7 point 4.
foreground_threshold tracks the fifth channel instead: 0.4 with none,
0.6 for touching boundaries, 0.5 for full boundaries, and 0.5 whenever
the calibrated foreground loss is present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
boundary also scores 0 on all six LM families, so the better-posed
channel does not repair the volume path - but the mechanism is not the
magnitude filter recorded in 4.0: its 3d fg_area_ratio reaches 6.6
(celegans) and 8.4 (gonuclear) against baseline's 2.0 / 3.6, with up to
9 background seeds per object and nothing matching at IoU 0.5. Seeds
are not missing; the volume is flooded, and boundary is the worst of
the six.

fgcal is the only variant that improves the 3d foreground and keeps an
LM score at baseline level, and on EM the boundary channel is harmless
(best of six on cremi_seen and snemi).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each at its own optimum against baseline at its own optimum: boundary
+2.3 % dev / +0.8 % holdout, boundary_fgcal +1.6/+0.7, fgcal +1.3/+1.0,
both +0.6/+1.5, contact -2.1/-0.2.

Recommendation: include the boundary-weighted foreground loss - it is
the most consistent change and the only one that generalises to 3d.
Include the full-boundary channel only with joint 2d+3d training, with
contact_weight in the tuning grid, and with a remedy for thin objects.
Never use the touching-boundary target.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dic_hepg2 sits near the mSA floor (0.118-0.190) with a 0.072 spread, so
its relative swings dominated the balanced mean without representing
segmentation quality. Re-ranked all six sweeps over the remaining ten
datasets and read the plateau rather than the top row.

Two retractions follow. The "clean 3-3 separation of the seed regime by
the foreground loss" is noise: the two regimes are identical to four
decimals for fgcal and boundary_fgcal. And "a ridge-blind sweep cannot
tune a five-channel decoder" rested entirely on dic_hepg2 - without it
boundary's own sweep optimum is its best configuration and the ridge is
worth +0.5 %, not +2 %.

What survives is the threshold finding, with effects an order of
magnitude larger: the fifth channel shifts the optimal foreground
threshold in proportion to how ill-posed its target is (none 0.4, full
boundary 0.5, touching 0.6). boundary's dev advantage is +1.2 %, not
+2.3 %, and fgcal holds up best on the holdout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On the nine informative datasets the four improved decoders sit +0.2 to
+0.8 % above baseline - inside the sweep's own resolution - and only
contact is clearly worse (-2.7 %). boundary's worst dataset improves
from -12.3 % to -2.1 %, deepbacs being its only remaining loss.

Adds a mechanical account of each optimum: which parameters build the
seed map (foreground_threshold, n_iter x dt, sigma, density_threshold)
and which build the height map (foreground_weight, plus the non-swept
contact ridge), and what each setting's best configuration therefore
does. The height map splits by whether the loss touches the foreground
twice: the three decoders that predict one clean foreground trust it at
0.75 and are the three best.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The base map is a convex mix of two [0,1] terms, so it is bounded by 1;
the ridge is added outside that normalisation, so contact_weight 1.0 is
as tall as the map's entire dynamic range. Measured on livecell, the
base map's boundary-to-interior contrast is +0.145 while the contact
probability is 0.623 against 0.055, so the ridge multiplies the barrier
by 3.0x at weight 0.5 and 4.9x at weight 1.

That explains both observed behaviours: merges fall from 7.8 % to 4.3 %,
and seeded splits rise monotonically because past weight ~2 the ridge
overrides rather than assists the boundary evidence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@constantinpape constantinpape mentioned this pull request Sep 8, 2026
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.

1 participant