Shortcuts

EM-R50 (Synaptic Polarity Detection)

A synapse is an essential structure that allows electric or chemical signals to be passed between neurons. Identification of synapses is important for reconstructing the wiring diagram of the brain. Signal flows in one direction at a synapse, so each synapse usually consists of a pre-synaptic and a post-synaptic region.

This section covers two benchmarks:

  • CREMI — synaptic cleft detection on the CREMI Challenge dataset (adult Drosophila melanogaster brain tissue, 2016).

  • EM-R50 — synaptic polarity detection on the dataset released by Lin et al. in 2020 (Layer II/III primary visual cortex of an adult rat). This predicts pre-synaptic and post-synaptic masks separately.

This tutorial covers synaptic polarity detection — predicting separated pre-synaptic and post-synaptic masks so the signal flow between neurons can be traced. The dataset was released by Lin et al. (2020) from Layer II/III of the primary visual cortex of an adult rat.

Unlike the cleft-detection task, polarity detection requires distinguishing individual synapses and assigning a side (pre / post) to each. The model outputs three channels — pre-synaptic, post-synaptic, and synaptic (union) — and a connected-components post-processor groups them into per-synapse instances.

Note

A dedicated EM-R50 tutorial config does not yet ship under tutorials/ in the modern (v2/v3) PyTC layout. The decoder and target-generation building blocks are present (polarity2instance decoder; seg_to_polarity target), but a canonical end-to-end YAML is a follow-up. The recipe below shows how to assemble one from the existing CREMI cleft-detection config (tutorials/syn_cremi.yaml) plus the polarity-specific pieces.

Goal

The intended pipeline:

  • Model output 3 channels — pre-synaptic, post-synaptic, and synaptic-union.

  • Target generation seg_to_polarity over instance labels; set exclusive=true if pre / post should never overlap (uses softmax) or false for the standard non-exclusive BCE setup (channels predicted independently with sigmoid).

  • Loss WeightedBCEWithLogitsLoss with rejection sampling on the foreground (synapses are sparse), or WeightedCE for the exclusive variant.

  • Decoder polarity2instance — connected-components on the synaptic-union channel, then voting on pre / post per instance.

  • Metric an IoU-based F1 score on instance-matched pairs.

1 - Get the data

mkdir -p datasets/jwr15 && cd datasets/jwr15
wget http://rhoana.rc.fas.harvard.edu/dataset/jwr15_synapse.zip
unzip jwr15_synapse.zip

2 - Building a v2 config (template)

Start from tutorials/syn_cremi.yaml and apply three changes:

(a) Switch the model output to 3 channels and use a non-exclusive BCE loss (or WeightedCE if you prefer exclusive masks):

default:
  model:
    out_channels: 3
    loss:
      losses:
      - function: WeightedBCEWithLogitsLoss
        weight: 1.0
        kwargs: {reduction: mean}
        pred_slice: "0:3"
        target_slice: "0:3"

(b) Drive target generation through the polarity helper. In your data.label_transform block, generate the 3-channel polarity target from instance labels by post-processing your label volume through connectomics.data.processing.target.seg_to_polarity (invoke from a small data-preparation script ahead of training, or add it as a custom processing step). Disable the cleft binary path that ships in syn_cremi.yaml.

  1. Add the polarity decoder under decoding.steps:

decoding:
  steps:
  - name: polarity2instance
    kwargs:
      # default: non-exclusive (independent BCE channels);
      # set true if you trained with WeightedCE / softmax.
      exclusive: false

The rest of the CREMI config (RSUNet, anisotropic 18 × 256 × 256 patches, sliding-window inference, EMA, augmentation profile) carries over.

Tip

Synapses are sparse, so add rejection sampling to focus training on patches that contain foreground:

data:
  dataloader:
    reject_sampling:
      size_thres: 1000
      p: 0.95

This is what syn_cremi.yaml already does for cleft detection; keep it for polarity training.

3 - Run training

Once your config is assembled (e.g. saved as tutorials/syn_em_r50.yaml):

conda activate pytc
python scripts/main.py --config tutorials/syn_em_r50.yaml

4 - Inference, decoding, evaluation

python scripts/main.py --config tutorials/syn_em_r50.yaml \
    --mode test \
    --checkpoint outputs/<exp>/<timestamp>/checkpoints/last.ckpt

The decoding stage runs polarity2instance, which converts the 3-channel probability map into per-synapse instance masks with a recorded side (pre vs. post). For programmatic use:

from connectomics.decoding import polarity2instance
# volume: (3, D, H, W) prediction, channels = [pre, post, syn]
instances = polarity2instance(volume)

The exclusive=True variant interprets the channels as a softmax output over {background, pre, post} and uses a different connected- component path; pass exclusive=True to polarity2instance if your model was trained with WeightedCE.

5 - Reference behavior

  • Training loss is dominated by the synaptic-union channel early on; the pre / post channels improve as the model learns spatial directionality, typically after the first cosine decay phase. Rejection sampling plus higher foreground weights is essential — without it, pre and post collapse to all-zeros.

  • Inference is identical to the CREMI flow (same RSUNet, same sliding window). The decoder runs in CPU and is fast.

  • Reporting the published EM-R50 F1 is computed against the Lin et al. 2020 evaluation script. Comparable code lives in the upstream paper repo; contact the maintainers if you intend to benchmark formally.