Skip to content

Cleanup - #5

Merged
ziw-liu merged 2 commits into
microdlfrom
cleanup
May 30, 2023
Merged

ziw-liu merged 2 commits into
microdlfrom
cleanup

Conversation

@ziw-liu

@ziw-liu ziw-liu commented May 30, 2023

Copy link
Copy Markdown
Contributor

Clean up unused code from microDL 1.0.

@ziw-liu
ziw-liu marked this pull request as ready for review May 30, 2023 23:12
@ziw-liu
ziw-liu merged commit 530af3b into microdl May 30, 2023
@ziw-liu
ziw-liu deleted the cleanup branch May 30, 2023 23:12
@ziw-liu ziw-liu mentioned this pull request May 30, 2023
3 of 4 tasks
alxndrkalinin added a commit that referenced this pull request Oct 2, 2026
…mito

Stage 2 #5 targets thin structures, so the SAUNA-weighted Dice and the
soft-clDice arms now cover ER and mito on the fnet3d_vscyto3daug recipe,
next to a plain segaux arm on the same cell. Their training mask is the
eval's classical segmenter (fg_mask in SEC61B.zarr / TOMM20.zarr).

Each weight is the median ||grad base|| / ||grad L|| over 20 seeded
val batches at the v2 baseline's best checkpoint, taken mid-training
(ER epoch 28, mito epoch 18): ER 0.22 / 0.21 / 0.24 and mito
0.77 / 0.66 / 0.71 for Dice / SAUNA / clDice. Every new train leaf
composes, and its data and trainer blocks equal the v2 baseline's except
fg_mask_key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J
alxndrkalinin added a commit that referenced this pull request Oct 5, 2026
Scores the FNet-3D vscyto3daug nucleus SAUNA-map-weighted Dice arm
(Stage 2 #5; fit 36906516 reached max_steps=200000; --ckpt best epoch
64) on all four legs. It is read against the wave2q 2x2. All four
predicts completed with complete markers on every FOV, and the
generator's store gate passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J
alxndrkalinin added a commit that referenced this pull request Oct 7, 2026
…Diff-2D (#524)

* feat(viscy-utils): add SAUNA weighting and soft-clDice to SegAuxDice

Spotlight v2 Stage 2 #5 compares two topology-aware variants of the
seg-aux Dice on thin structures (ER, mito, nucleus necks):

- weighting="sauna" weights both Dice sums by |y~|, SAUNA's combined
  boundary/thickness "h" map of the patch mask (Oulu-IMEDS/SAUNA),
  ported to anisotropic 3D. The training venv has no cupy, so the
  distance transform is an exact separable torch EDT, capped at the
  distance the map uses; it matches scipy to 1e-4.
- topology="cldice" mixes in 1 - soft-clDice (jocpae/clDice skeleton).
  Each skeleton iteration is activation-checkpointed: at 10 iterations
  a (8, 1, 32, 384, 384) batch otherwise exceeded 44 GiB on an A40.

Both default off, and the defaults stay bit-identical to the plain
soft Dice. per_channel keeps its contract, so CellDiff's gated path
gets the options too. Tests: test_seg_aux_loss.py 63 passed;
test_training_integration.py 66 passed (fast_dev_run of both options
in 3D and Z=1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* feat(dynacell): add Spotlight v2 topology arms and ER/mito baselines

Stage 2 #5 compares SAUNA-weighted Dice against soft-clDice as one-term
changes to the segaux arm, on the FNet-3D recipe that works out of
domain (fnet3d_vscyto3daug):

- nucleus: segaux_sauna (w 5.3) and segaux_cldice (w 0.88, 5 skeleton
  iterations), both calibrated on the Phase 15 Arm B ckpt like segaux;
  the plain-Dice row of that calibration reproduces segaux's 4.5. They
  are scored against the in-flight v2/segaux 2x2.
- ER and mito: never-run template leaves of the same recipe (target
  channel Structure, so the overlay's lists apply unchanged) and the
  v2/v2_seed1 baseline draws. The topology terms are aimed at these
  thin organelles; their arms follow once the baselines can be
  calibrated on.

The A549 predict leaf names become per-organelle (nucleus keeps the
Phase 15 _h2b_ names). The SAUNA spacing is read from the aics-hipsc
manifest. Tests: 2630 passed, 176 skipped (leaf generator, paths,
config composition, grouped eval generator).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* feat(dynacell): add Spotlight v2 segaux and topology arms for ER and mito

Stage 2 #5 targets thin structures, so the SAUNA-weighted Dice and the
soft-clDice arms now cover ER and mito on the fnet3d_vscyto3daug recipe,
next to a plain segaux arm on the same cell. Their training mask is the
eval's classical segmenter (fg_mask in SEC61B.zarr / TOMM20.zarr).

Each weight is the median ||grad base|| / ||grad L|| over 20 seeded
val batches at the v2 baseline's best checkpoint, taken mid-training
(ER epoch 28, mito epoch 18): ER 0.22 / 0.21 / 0.24 and mito
0.77 / 0.66 / 0.71 for Dice / SAUNA / clDice. Every new train leaf
composes, and its data and trainer blocks equal the v2 baseline's except
fg_mask_key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* feat(viscy-utils): add BackgroundLowPass training-target transform

x' = w * x + (1 - w) * G_lp(x), w = clamp(G_f(dilate(fg_mask, r)), 0, 1):
the target is unchanged inside the dilated, feathered foreground and
a Gaussian low-pass of itself in the background. It changes the
target rather than masking the loss, so a flow-matching model is still
supervised in the background and learns to remove the initial noise
there (masking it out was the Spotlight v1 collapse).

The Gaussians are normalized and edge-replicated (scipy mode="nearest",
truncate=4.0), so a constant passes through unchanged and the low-pass
keeps the local mean; no normalization change is needed. They run as
float32 shifted multiply-adds, out of reach of autocast and cuDNN TF32.
XY per Z plane by default; sigma_lp_z / sigma_feather_z /
dilate_radius_z extend each step to Z.

Tests check the op against a scipy.ndimage reference (D=1, per-plane
D>1, with Z), identity where w = 1, exact low-pass where w = 0, the
local mean, empty/full masks, plane independence, dtype/autocast and
CPU vs CUDA.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* feat(dynacell): train CELL-Diff on a background-low-passed target

DynacellFlowMatching gains target_bg_lowpass (default None). When set,
the training step replaces the target by
target_bg_lowpass(target, fg_mask) after normalization and the GPU
augmentations, so the flow learns full-resolution texture inside cells
and a smooth, still-supervised field in the background, instead of
spending capacity on background noise that phase cannot predict
(Spotlight v2 Track H). seg_aux, C-joint and C-cond see the same
transformed target; validation, its samples and prediction see the raw
target, so loss/validate and --ckpt best keep the baseline's criterion.

The op is applied in the engine and fg_mask reaches the wrapper only
through seg_aux, so it composes with seg_aux and never trips "fg_mask
was passed but seg_aux is not configured". A missing fg_mask raises
the shared KeyError, now naming target_bg_lowpass. With None the loss
is bit-identical to the baseline's, even on a batch that carries
fg_mask. The op holds no parameters, so checkpoints are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* feat(dynacell): add Spotlight v2 bglp arm for CellDiff-2D

Track H H1: celldiff_2d_bglp, nucleus and membrane. The fit leaf is the
celldiff_2d baseline plus fg_mask_key: fg_mask and target_bg_lowpass
(BackgroundLowPass); normalization, sampling, budget and the monitored
metric are the baseline's. Predict leaves for iPSC and the three A549
legs equal the baseline's but for checkpoint, store and names, since
the op is training-only.

BG_LOWPASS_ARGS (sigma_lp 2, sigma_feather 2, dilate_radius 4 px) is
PROVISIONAL: the final values come from the H0 band-power spectra.

The new token is registered wherever an arm token must be (PAPER_KEY,
the CellDiff arm segments, the grouped generator's code map and
CellDiff list) so it never collapses onto celldiff_2d. Regenerating
writes the 10 new leaves and leaves the other 372 byte-identical. A
test composes both fit leaves, checks their delta against the baseline
and builds the model through jsonargparse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* fix(viscy-utils): estimate the background from background pixels only

G_lp(x) of the whole target bleeds a bright foreground into the
background as a halo, so BackgroundLowPass taught the background to
carry a ghost of every cell. The estimate is now a normalized
convolution over background pixels:

  m = dilate(fg_mask, r),  B = G_lp(x * (1 - m)) / G_lp(1 - m),
  x' = w * x + (1 - w) * B,  w = clamp(G_f(m), 0, 1).

Where under 1e-6 of the kernel mass falls on background (e.g. a patch
the dilated mask covers entirely) B is undefined and the target is
kept, so x' stays finite; w is ~1 there anyway. dilated_mask(),
blend_weight(m) and background(x, m) replace lowpass() and
blend_weight(fg_mask).

A new test shows a blob's halo in a plain blur (> 2 just outside the
dilated mask) and none in B (|B| < 0.15 on noise of std 0.1); it fails
when B is swapped back to the plain blur.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* feat(viscy-utils): add a flat background mode to BackgroundLowPass

sigma_lp=None (or inf) makes the background estimate flat: per sample
and channel, the mean of the target over every background voxel of the
patch, the sigma_lp -> inf limit of the normalized convolution. Beyond
background high frequencies it also removes autofluorescence and
uneven illumination, which phase cannot predict either (Track H
revision). A patch with no background keeps its target, as in the
smooth mode. sigma_lp_z is rejected with the flat mode, which already
spans Z.

The mean, not the median, keeps the flat mode the exact limit of the
smooth one. Tests: x' is the masked background mean wherever w = 0,
across planes and through an illumination gradient (failing for an
unmasked or a per-plane mean), plus the scipy reference, the
no-background patch and CPU vs CUDA in flat mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* test(dynacell): train CELL-Diff with a flat background target

The fast_dev_run of the background target op now also runs the flat
mode (sigma_lp=None), 2D and 3D, with and without seg_aux, on real
generate_fg_masks output. The engine docstring describes the
background-pixel estimate instead of a low-pass of the whole target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* feat(dynacell): add Spotlight v2 bgflat arm for CellDiff-2D

Track H revision: celldiff_2d_bgflat, nucleus and membrane, next to
celldiff_2d_bglp. Its target_bg_lowpass has sigma_lp: null, so the
background becomes the patch's background mean, which removes
autofluorescence and uneven illumination as well as background high
frequencies. Its risk is foreground the mask misses (dim nuclei)
becoming an "erase" example, where bglp only blurs it.

BG_LOWPASS_ARGS becomes BG_TARGET_ARGS, one entry per arm sharing the
feather and dilation (sigma_feather 2, dilate_radius 4 px; bglp
sigma_lp 2 px); all values stay PROVISIONAL until the H0 spectra.
celldiff_2d_bgflat is registered like bglp. Regenerating writes the 10
new leaves; the other 382, including bglp's, are byte-identical. The
composition test now covers both arms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* test(dynacell): build the Track H arms' model from a LightningModule base

test_lazy_init evicts every dynacell.* module from sys.modules, so when
it shares an xdist worker with the composition test, jsonargparse
imports a fresh dynacell.engine whose DynacellFlowMatching is not the
class the test bound at import ("does not correspond to a subclass").
Run after test_lazy_init, all 4 cases failed. LightningModule as the
base, as LightningCLI uses, is never evicted; the test now checks the
built class by its import path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* fix(viscy-utils): make the BackgroundLowPass blend weight exactly 1 inside the mask

The normalized Gaussian sums to 1 only up to float32 rounding, so under
an all-foreground window w was 0.99999982 (sigma_feather 0.5),
0.99999988 (1.0, 3.0) and exactly 1 only at 2.0 and 4.0 (measured on an
all-ones mask). x' = w * x + (1 - w) * B then differs from x by
rounding wherever B != x, and "an all-foreground plane comes back
unchanged" held by luck. w is now divided by the blur of ones: edge
replication makes that one value at every voxel, computed by the same
operations as any all-ones window, so w is exactly 1 there and still
exactly 0 where the kernel misses the mask.

The new test requires w == 1 and a bit-identical plane for sigma_feather
0.5, 1, 2 and 3; without the division it fails at 0.5, 1 and 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* fix(viscy-utils): flatten the background per plane and fall back to that mean

Two refinements to BackgroundLowPass from the Track H revision:

- The flat background (sigma_lp=None) is now the mean over background
  pixels per sample, channel and Z plane, consistent with the op's
  no-Z-mixing default, instead of one mean over the whole patch.
- Where the normalized convolution has at most 1e-6 of its kernel mass
  on background (a background ring thinner than its support), B is now
  that plane's flat mean, not the target itself. Only a plane with no
  background pixel at all keeps B = x; there w == 1 (exact since the
  previous commit), so it is returned bit-identical.

Tests, each failing under the matching mutation:
- a bright blob on a constant background gives B == the constant
  everywhere, including the ring just outside the dilated mask where a
  plain blur of the target reaches > 2.7 (fails with G_lp(x));
- the flat output is the masked mean per (b, c, plane) through
  per-plane offsets and a ramp (fails with a whole-patch mean);
- the pixels a thin ring cannot reach take the ring's mean, all finite
  (fails with the x fallback and with an eps-divide to 0);
- a linear backlight ramp is flattened by bgflat and kept by bglp at
  sigma 8, fitted slope within 20% (fails if either mode is swapped).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* docs(dynacell): describe bgflat as a per-plane background mean

BackgroundLowPass now flattens each Z plane to its own background mean;
the generator's docstring and BG_TARGET_ARGS comment said "the patch's".
The 2D leaves have one plane, so no leaf changes (--check: 0 stale).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* fix(dynacell): give the Track H arms their measured mask geometry

The bglp and bgflat arms shared a provisional r=4 / sigma_f=2 ring.
The H0 step measured each arm's geometry on the GT with the training
op (trackh/target_op_check2.csv, mask_recall.csv):

- bglp moves to r=2 / sigma_f=1. At r4/f2 the unsmoothed ring keeps
  12-15% of the background's 1-px power, vs 3% at r2/f1. A nucleus
  the mask misses is only blurred, so the tighter ring is safe.
- bgflat keeps r=4 / sigma_f=2, for mask recall. Flattening erases
  any nucleus the mask misses: 1.2% of iPSC cpdino GT nuclei are
  <50% covered at r4, vs 2.9% at r2.

Only the two bglp train leaves change. The bgflat leaves stay
byte-identical, and the generator's --check reports 0 stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* fix(dynacell): resolve the Track H arms ahead of the celldiff_2d fallback

The merge of dynacell-models took that side's paths.py for the
PAPER_KEY conflict and with it dropped this branch's non-conflicting
hunk listing celldiff_2d_bglp/bgflat in _CELLDIFF_ARM_MODELS. Without
it, a leaf that keeps model_name celldiff_2d resolves a bglp/bgflat
checkpoint to the baseline; test_resolve_model_spotlight_v2_arm_not_collapsed
caught both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* docs(dynacell): describe bgflat as a per-plane background mean in PAPER_KEY

BackgroundLowPass with sigma_lp=None takes the mean over each sample,
channel and Z plane's background pixels (background_lowpass.py), as
161e098 already says in the leaf generator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* docs(viscy-utils): say where BackgroundLowPass leaves the target unchanged

The module docstring said the whole dilated foreground is unchanged.
w is exactly 1 only where the feather kernel lies inside the dilated
mask, as the class docstring states; with a feather radius above the
dilation radius the blend reaches the foreground rim. Both Track H arms
are in that regime: on a 24x24 square, min w on the original foreground
is 0.9909 (bglp) and 0.9772 (bgflat), and a 1-px object gets 0.9818 and
0.9546. The code is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

* test(dynacell): build the Track H model with ArgumentParser.instantiate

jsonargparse 4.49 deprecates instantiate_classes in favour of
instantiate, and the Track H compose test raised that warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gwn9hwgT8uJSoWaCxCAm8J

---------

Co-authored-by: Alexandr Kalinin <alxndrkalinin@users.noreply.github.com>
Co-authored-by: Claude Opus 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.

1 participant