dwei commited on
Commit
e7c9dac
Β·
verified Β·
1 Parent(s): a88ce35

Reorganise into per-dataset folders: move the j0126 affinity model to j0126/

Browse files
README.md CHANGED
@@ -9,113 +9,20 @@ tags:
9
  - mednext
10
  ---
11
 
12
- # j0126 affinity model (supervised, from scratch)
13
 
14
- 3-channel voxel affinity prediction for the **j0126 zebra finch** EM volume, for use with
15
- [PyTorch Connectomics](https://github.com/PytorchConnectomics/pytorch_connectomics)
16
- (`tutorials/neuron_j0126`).
17
 
18
- | | |
19
- |---|---|
20
- | Files | `affinity_scratch_48x96x96.ckpt` (247 MB) Β· `affinity_scratch_48x96x96.onnx` (247 MB, opset 17) |
21
- | Architecture | MedNeXt-L, kernel 3 (61.8 M parameters) |
22
- | Input / output | 1 channel EM β†’ 6 channel affinity (`banis+`; channels 0–2 are the nearest-neighbour affinity used downstream) |
23
- | Training patch | `[48, 96, 96]` (ZYX) β€” anisotropic, β‰ˆ isotropic in nm for the 9Γ—9Γ—20 nm grid |
24
- | Training data | the 33 dense-GT j0126 cubes, heavy `aug_em_neuron` augmentation |
25
- | Schedule | 200 k steps from scratch (no pretrained init), AdamW, lr 1e-3, batch 8 Γ— 4 GPUs |
26
-
27
- > **This model is not ground-truth-free.** It was trained on labelled j0126 tissue and is
28
- > the *supervised upper reference* for the zero-shot pipeline in the tutorial, not a
29
- > drop-in replacement for it. (The dense cubes come from a different alignment than the
30
- > 50 evaluation skeletons, so it is not voxel-level test contamination β€” but it is
31
- > supervision on the target domain.)
32
-
33
- ## Inference window must be `[48, 96, 96]`
34
-
35
- MedNeXt normalizes with `GroupNorm(num_groups=C, num_channels=C)` β€” per-sample,
36
- per-channel instance norm with **no running statistics** β€” so normalization statistics are
37
- computed over the sliding window's spatial extent at every block and **the forward pass is
38
- window-size dependent**. Running this checkpoint at the tutorial's inherited
39
- `[144, 144, 144]` is a 3Γ— Z / 1.5Γ— XY mismatch that inverts the trained Z-thin anisotropy.
40
-
41
- `tutorials/neuron_j0126/1_affinity_supervised.yaml` already sets both
42
- `model.input_size` and `inference.window.window_size` to `[48, 96, 96]`. Both must be set
43
- in YAML, never on the CLI: the stage merge can clobber a pre-resolution CLI override.
44
-
45
- ## Usage
46
 
47
  ```bash
48
- hf download pytc/j0126 affinity_scratch_48x96x96.ckpt --local-dir ckpt/
49
-
50
- python scripts/main.py --config tutorials/neuron_j0126/1_affinity_supervised.yaml \
51
- --mode test --checkpoint ckpt/affinity_scratch_48x96x96.ckpt
52
  ```
53
 
54
- Whole volume (726 chunks of 1008Β³ with a 72-voxel halo), one GPU per shard:
55
-
56
- ```bash
57
- python scripts/main.py --config tutorials/neuron_j0126/1_affinity_supervised.yaml \
58
- --mode test --checkpoint ckpt/affinity_scratch_48x96x96.ckpt \
59
- --shard-id "$SLURM_ARRAY_TASK_ID" --num-shards 726
60
- ```
61
-
62
- Output is one float16 CZYX HDF5 per chunk. Affinities are written with the `scale_sigmoid`
63
- activation, which the tutorial's ABISS watershed thresholds are calibrated against
64
- (`scale_sigmoid` 0.70 == plain sigmoid 0.9857) β€” do not substitute a plain sigmoid without
65
- recalibrating step 2.
66
-
67
- ## Results
68
-
69
- Full-volume j0126, 50 evaluation skeletons, from `tutorials/neuron_j0126/README.md`. This
70
- checkpoint is the "scratch" affinity in that table:
71
-
72
- | Affinity | Decoding | Error correction | NERL mt=0 ↑ | NERL mt=5 ↑ | VOI ↓ |
73
- |---|---|---|---:|---:|---:|
74
- | FFN reference | β€” | β€” | **0.526** | 0.538 | **1.856** |
75
- | scratch | ABISS, exclusion mask | β€” | 0.268 | 0.470 | 2.584 |
76
- | scratch | + nucleus instance certificate | β€” | 0.287 | 0.482 | 2.562 |
77
- | scratch | + nucleus instance certificate | morphology-guided branch linking | 0.301 | **0.539** | 2.374 |
78
- | scratch | + nucleus instance certificate | + 3Γ—3Γ—3 inter-object erosion | **0.441** | 0.528 | 2.440 |
79
-
80
- Affinity quality alone is not what these numbers measure β€” see the tutorial for the
81
- decode and error-correction steps that produce them.
82
-
83
- ## ONNX
84
-
85
- `affinity_scratch_48x96x96.onnx` is the same weights exported for runtimes without PyTorch.
86
-
87
- ```python
88
- import numpy as np, onnxruntime as ort
89
-
90
- session = ort.InferenceSession("affinity_scratch_48x96x96.onnx",
91
- providers=["CPUExecutionProvider"])
92
- image = np.random.randn(1, 1, 48, 96, 96).astype(np.float32) # (B, C, Z, Y, X)
93
- logits = session.run(["logits"], {"image": image})[0] # (B, 6, 48, 96, 96)
94
- affinity = 1.0 / (1.0 + np.exp(-0.2 * logits)) # scale_sigmoid, temperature 0.2
95
- ```
96
-
97
- Three things the graph does **not** do for you:
98
-
99
- - **The activation.** The output is raw logits. The pipeline applies `scale_sigmoid` with
100
- BANIS' temperature 0.2 β€” `sigmoid(0.2 * x)` β€” downstream, and step 2's watershed thresholds
101
- are calibrated against that.
102
- - **Channel selection.** Six channels are emitted; the decode consumes channels 0–2.
103
- - **Sliding-window assembly.** One window in, one window out. Tiling, overlap and blending
104
- are the caller's job.
105
-
106
- The spatial dims are fixed at `[48, 96, 96]` and only the batch axis is dynamic β€” see the
107
- window section above for why a dynamic spatial axis would be a trap here. Deep supervision is
108
- switched off in the export, so the graph has a single output.
109
-
110
- Verified against PyTorch on identical input: max |diff| 2.2e-04 on logits spanning
111
- [1.03, 21.17] (relative 1.0e-05), which is **4.6e-06** after `scale_sigmoid` β€” float32
112
- accumulation noise, not a behavioural difference. Regenerate with
113
- `python scripts/export_onnx.py --config tutorials/neuron_j0126/1_affinity_supervised.yaml
114
- --checkpoint <ckpt> --output model.onnx --check`.
115
-
116
- ## Checkpoint format
117
-
118
- A PyTorch Lightning checkpoint (`state_dict` keys are prefixed `model.model.`). Optimizer
119
- state has been stripped for download size; the weights are bit-identical to the reference
120
- run's `step=00200000.ckpt` (all 517 tensors, float32). It is loadable for inference/finetuning,
121
- not for resuming the original optimizer trajectory.
 
9
  - mednext
10
  ---
11
 
12
+ # pytc/vEM β€” volume EM models
13
 
14
+ Checkpoints for [PyTorch Connectomics](https://github.com/PytorchConnectomics/pytorch_connectomics),
15
+ one folder per dataset. Each folder has its own README with the training recipe, the
16
+ inference constraints that matter, and the measured results.
17
 
18
+ | Folder | Dataset | Task | Files |
19
+ |---|---|---|---|
20
+ | [`j0126/`](j0126) | j0126 zebra finch, 9 Γ— 9 Γ— 20 nm | 3-channel voxel affinity | `affinity_scratch_48x96x96.ckpt` (247 MB) Β· `.onnx` (247 MB, opset 17) |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  ```bash
23
+ hf download pytc/vEM j0126/affinity_scratch_48x96x96.ckpt --local-dir ckpt/
 
 
 
24
  ```
25
 
26
+ > Renamed from `pytc/j0126` on 2026-09-08 and reorganised into per-dataset folders. The old
27
+ > repo URL redirects, but a link that pinned `affinity_scratch_48x96x96.ckpt` at the repo
28
+ > root needs the `j0126/` prefix added.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
j0126/README.md ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # j0126 affinity model (supervised, from scratch)
2
+
3
+ 3-channel voxel affinity prediction for the **j0126 zebra finch** EM volume, for use with
4
+ [PyTorch Connectomics](https://github.com/PytorchConnectomics/pytorch_connectomics)
5
+ (`tutorials/neuron_j0126`).
6
+
7
+ | | |
8
+ |---|---|
9
+ | Files | `affinity_scratch_48x96x96.ckpt` (247 MB) Β· `affinity_scratch_48x96x96.onnx` (247 MB, opset 17) |
10
+ | Architecture | MedNeXt-L, kernel 3 (61.8 M parameters) |
11
+ | Input / output | 1 channel EM β†’ 6 channel affinity (`banis+`; channels 0–2 are the nearest-neighbour affinity used downstream) |
12
+ | Training patch | `[48, 96, 96]` (ZYX) β€” anisotropic, β‰ˆ isotropic in nm for the 9Γ—9Γ—20 nm grid |
13
+ | Training data | the 33 dense-GT j0126 cubes, heavy `aug_em_neuron` augmentation |
14
+ | Schedule | 200 k steps from scratch (no pretrained init), AdamW, lr 1e-3, batch 8 Γ— 4 GPUs |
15
+
16
+ > **This model is not ground-truth-free.** It was trained on labelled j0126 tissue and is
17
+ > the *supervised upper reference* for the zero-shot pipeline in the tutorial, not a
18
+ > drop-in replacement for it. (The dense cubes come from a different alignment than the
19
+ > 50 evaluation skeletons, so it is not voxel-level test contamination β€” but it is
20
+ > supervision on the target domain.)
21
+
22
+ ## Inference window must be `[48, 96, 96]`
23
+
24
+ MedNeXt normalizes with `GroupNorm(num_groups=C, num_channels=C)` β€” per-sample,
25
+ per-channel instance norm with **no running statistics** β€” so normalization statistics are
26
+ computed over the sliding window's spatial extent at every block and **the forward pass is
27
+ window-size dependent**. Running this checkpoint at the tutorial's inherited
28
+ `[144, 144, 144]` is a 3Γ— Z / 1.5Γ— XY mismatch that inverts the trained Z-thin anisotropy.
29
+
30
+ The tutorial sets this in one place: `inference.window_size: [48, 96, 96]` in
31
+ `tutorials/neuron_j0126/params.yaml`, which `2_infer.yaml` reads. Set it in YAML, never on
32
+ the CLI: the stage merge can clobber a pre-resolution CLI override.
33
+
34
+ ## Usage
35
+
36
+ The tutorial downloads this checkpoint for you: leave `train.enabled: false` in
37
+ `tutorials/neuron_j0126/params.yaml` and run the whole pipeline with one command.
38
+
39
+ ```bash
40
+ python scripts/run_j0126.py
41
+ ```
42
+
43
+ To use it directly instead:
44
+
45
+ ```bash
46
+ hf download pytc/vEM j0126/affinity_scratch_48x96x96.ckpt --local-dir ckpt/
47
+
48
+ python scripts/main.py --config tutorials/neuron_j0126/2_infer.yaml \
49
+ --mode test --checkpoint ckpt/j0126/affinity_scratch_48x96x96.ckpt
50
+ ```
51
+
52
+ Whole volume (726 chunks of 1008Β³ with a 72-voxel halo), one GPU per shard:
53
+
54
+ ```bash
55
+ python scripts/main.py --config tutorials/neuron_j0126/2_infer.yaml \
56
+ --mode test --checkpoint ckpt/j0126/affinity_scratch_48x96x96.ckpt \
57
+ --shard-id "$SLURM_ARRAY_TASK_ID" --num-shards 726
58
+ ```
59
+
60
+ Output is one float16 CZYX HDF5 per chunk. Affinities are written with the `scale_sigmoid`
61
+ activation, which the tutorial's ABISS watershed thresholds are calibrated against
62
+ (`scale_sigmoid` 0.70 == plain sigmoid 0.9857) β€” do not substitute a plain sigmoid without
63
+ recalibrating step 3.
64
+
65
+ ## Results
66
+
67
+ Full-volume j0126, 50 evaluation skeletons, from `tutorials/neuron_j0126/README.md`. This
68
+ checkpoint is the "scratch" affinity in that table:
69
+
70
+ | Affinity | Decoding | Error correction | NERL mt=0 ↑ | NERL mt=5 ↑ | VOI ↓ |
71
+ |---|---|---|---:|---:|---:|
72
+ | FFN reference | β€” | β€” | **0.526** | 0.538 | **1.856** |
73
+ | scratch | ABISS, exclusion mask | β€” | 0.268 | 0.470 | 2.584 |
74
+ | scratch | + nucleus instance certificate | β€” | 0.287 | 0.482 | 2.562 |
75
+ | scratch | + nucleus instance certificate | morphology-guided branch linking | 0.301 | **0.539** | 2.374 |
76
+ | scratch | + nucleus instance certificate | + 3Γ—3Γ—3 inter-object erosion | **0.441** | 0.528 | 2.440 |
77
+
78
+ Affinity quality alone is not what these numbers measure β€” see the tutorial for the
79
+ decode and error-correction steps that produce them.
80
+
81
+ ## ONNX
82
+
83
+ `affinity_scratch_48x96x96.onnx` is the same weights exported for runtimes without PyTorch.
84
+
85
+ ```python
86
+ import numpy as np, onnxruntime as ort
87
+
88
+ session = ort.InferenceSession("affinity_scratch_48x96x96.onnx",
89
+ providers=["CPUExecutionProvider"])
90
+ image = np.random.randn(1, 1, 48, 96, 96).astype(np.float32) # (B, C, Z, Y, X)
91
+ logits = session.run(["logits"], {"image": image})[0] # (B, 6, 48, 96, 96)
92
+ affinity = 1.0 / (1.0 + np.exp(-0.2 * logits)) # scale_sigmoid, temperature 0.2
93
+ ```
94
+
95
+ Three things the graph does **not** do for you:
96
+
97
+ - **The activation.** The output is raw logits. The pipeline applies `scale_sigmoid` with
98
+ BANIS' temperature 0.2 β€” `sigmoid(0.2 * x)` β€” downstream, and step 2's watershed thresholds
99
+ are calibrated against that.
100
+ - **Channel selection.** Six channels are emitted; the decode consumes channels 0–2.
101
+ - **Sliding-window assembly.** One window in, one window out. Tiling, overlap and blending
102
+ are the caller's job.
103
+
104
+ The spatial dims are fixed at `[48, 96, 96]` and only the batch axis is dynamic β€” see the
105
+ window section above for why a dynamic spatial axis would be a trap here. Deep supervision is
106
+ switched off in the export, so the graph has a single output.
107
+
108
+ Verified against PyTorch on identical input: max |diff| 2.2e-04 on logits spanning
109
+ [1.03, 21.17] (relative 1.0e-05), which is **4.6e-06** after `scale_sigmoid` β€” float32
110
+ accumulation noise, not a behavioural difference. Regenerate with
111
+ `python scripts/export_onnx.py --config tutorials/neuron_j0126/1_train.yaml
112
+ --checkpoint <ckpt> --output model.onnx --check`.
113
+
114
+ ## Checkpoint format
115
+
116
+ A PyTorch Lightning checkpoint (`state_dict` keys are prefixed `model.model.`). Optimizer
117
+ state has been stripped for download size; the weights are bit-identical to the reference
118
+ run's `step=00200000.ckpt` (all 517 tensors, float32). It is loadable for inference/finetuning,
119
+ not for resuming the original optimizer trajectory.
affinity_scratch_48x96x96.ckpt β†’ j0126/affinity_scratch_48x96x96.ckpt RENAMED
File without changes
affinity_scratch_48x96x96.onnx β†’ j0126/affinity_scratch_48x96x96.onnx RENAMED
File without changes