Radiance SEN1Floods11 — v2 (2026-09-06)
Binary flood-water segmentation on Sentinel-1 SAR imagery. Fine-tuned from the Radiance MAE Base v1 ViT-B backbone on the SEN1Floods11 v1.1 hand-labeled split.
v2 replaces a defective v1. The v1 weights published here on 2026-09-02 were trained with a bug that scrambled patch order before the segmentation head (MAE's
random_maskingreturns tokens shuffled even atmask_ratio = 0, and the head was never un-shuffled). v1 could only learn a per-crop flood fraction; its prediction maps were spatially flat and its inference was non-deterministic. The v1 numbers (val 0.414 / test 0.462 / bolivia 0.587) were the scores of that per-crop classifier, not of a segmentation model. v2 fixes the head, is deterministic, and is evaluated on the held-out test and bolivia splits with per-chip distributions and a qualitative figure. The old weights are kept atlegacy/v1_degenerate_best.ptfor reproducibility only — do not use them.
- Task: per-pixel binary flood segmentation from 2-channel Sentinel-1 GRD (VV + VH), dB-normalised, 224 × 224 crops
- Base:
stratus-labs/radiance-mae-base-v1(ViT-B/16 MAE, 100 epochs SSL4EO-S12 pretrain) - Head: 4-stage ConvTranspose upsampler (768 → 256 → 128 → 64 → 32 → 1, ~3.55 M params) on raster-ordered patch tokens
- License: CC-BY-4.0
Which protocol you use changes the number — read this first
Two evaluation protocols appear below. They measure different things and must never be compared to each other.
| protocol | what it sees | test flood IoU @0.5 |
|---|---|---|
| Full-tile sliding window (recommended, operational) | the whole 512×512 tile, 224 windows at stride 112, probabilities averaged | 0.629 |
| 224 centre-crop (historical, what the numbers below use) | the centre 224×224 only, 19 % of each tile | 0.598 |
The centre-crop protocol is what this model was originally scored under and every number in the next section uses it. It understates the model by roughly three points, because most of each tile is never looked at. New work should use full-tile.
Recommended operating point: threshold 0.25, not 0.5
The default 0.5 was never optimal for anything — validation IoU peaks at 0.40. More importantly, lowering the threshold recovers small floods that 0.5 discards, at negligible cost. Measured full-tile, threshold selected on validation under a false-positive budget declared before the run (≤2 % of valid pixels on water-free tiles), then the test set scored once:
| test, full-tile | flood IoU | recall | precision | small-water recall | false-positive area on water-free tiles |
|---|---|---|---|---|---|
| threshold 0.50 | 0.629 | 0.736 | 0.812 | 0.505 | 0.10 % |
| threshold 0.25 | 0.622 | 0.785 | 0.750 | 0.584 | 0.18 % |
+7.9 points of small-flood recall for 0.7 points of IoU, using under a tenth of the false-positive budget. If your cost of missing a flood exceeds your cost of a small false positive — which is usual in flood mapping — use 0.25. Recall on small water keeps climbing to 0.68 at threshold 0.05, at 2.9 % false-positive area; pick your own point on that curve.
This also corrects an earlier characterisation on this card. We previously reported near-zero per-chip IoU on the smallest floods and inferred the model could not see them. It can: it recovers half to three-fifths of small-water pixels. What collapses on tiny targets is precision, which is what drives per-chip IoU to zero. The signal was there; the operating point was throwing it away.
Results, 224 centre-crop protocol (held-out, threshold 0.5, nodata masked)
Pixel-pooled flood-class IoU on the three SEN1Floods11 hand-labeled evaluation splits, center 224 × 224 crop of each 512 × 512 tile:
| split | chips | flood IoU | precision | recall | F1 |
|---|---|---|---|---|---|
| val (selection only) | 89 | 0.615 | 0.79 | 0.73 | 0.76 |
| test | 90 | 0.598 | 0.80 | 0.71 | 0.75 |
| bolivia (out-of-region) | 15 | 0.651 | 0.82 | 0.76 | 0.79 |
A second seed of the same recipe gives val 0.603 / test 0.594 / bolivia 0.618 — test is stable to ±0.005; bolivia is 15 chips, treat its second decimal as noise.
Per-chip IoU by how much of the crop is flooded (the number that tells you what the model can and cannot do):
| test chips by flood fraction of crop | n | median IoU | mean IoU |
|---|---|---|---|
| no flood | 17 | 0.00 | 0.00 |
| < 1 % | 23 | 0.00 | 0.03 |
| 1 – 10 % | 30 | 0.35 | 0.28 |
| 10 – 50 % | 9 | 0.66 | 0.59 |
| ≥ 50 % | 11 | 0.75 | 0.62 |
Median IoU over the 51 test chips with ≥ 1 % flood: 0.39. The bolivia split behaves the same way (1–10 %: 0.41 · 10–50 %: 0.49 · ≥ 50 %: 0.78). The model segments floods that cover more than a few percent of a crop well and misses most floods under 1 % of a crop — that is the v3 target, not "+IoU".
Inference is deterministic (same input twice: max |Δ| 8e-5). test_chips.png shows VV / label / prediction for the four test chips with the most flood.

Where we sit vs published SOTA: 0.60–0.75 flood IoU is reported for larger backbones with Sentinel-1 + Sentinel-2 + DEM fusion and full-tile inference. v2 is S1-only, ViT-B, a light head, 252 training chips and a center-crop protocol; the gap is real and the roadmap below is ordered by what closes it.
Training recipe (v2)
- Data: SEN1Floods11 v1.1 hand-labeled split — 252 train / 89 val chips; test (90) and bolivia (15) never touched until the final eval
- Input: VV + VH dB,
nan_to_num(many tiles have > 80 % NaN nodata), clipped to [−60, +20] dB, z-scored with SSL4EO-S12 statsmean = [−12.577, −20.265],std = [5.176, 5.870] - Train: random 224 × 224 crop + hflip + vflip; eval: center crop
- Input: VV + VH dB,
- Encoder → head:
latent, _, ids_restore = encoder.forward_encoder(x); patch tokens gathered back to raster order withids_restorebefore the 14 × 14 reshape (the v1 bug was omitting this) - Optim: AdamW, layer-wise LR decay 0.8 across the 12 encoder blocks, encoder LR 3e-5, head LR 1e-3, weight decay 0.05, 3-epoch warmup + cosine over 60 epochs, early stopping (patience 12) on val IoU — best at epoch 8, stopped at 21
- Loss: BCE + Dice,
label == −1nodata masked out of loss and metrics - Precision: bf16 autocast, grad-norm clip 1.0, batch 8, seed 0
- Hardware: 1× NVIDIA GB10 (DGX Spark), ~4 minutes wall-clock
Ablation on the fixed head (single seed each): no LLRD / encoder LR 1e-5 → test 0.588, bolivia 0.656; LLRD only → 0.589 / 0.594; encoder LR 3e-5 only → 0.596 / 0.555; both (v2) → 0.598 / 0.652. A higher encoder LR buys small-flood recall but costs out-of-region generalisation; LLRD recovers it.
Files
| File | Purpose |
|---|---|
best.pt | v2 fine-tuned state dict (encoder + head), torch.save format, key "model" |
model.py | RadianceMAE (with encode()), RadianceSegHead, RadianceSegModel — the forward pass contains the un-shuffle |
eval_report.json | val / test / bolivia metrics incl. per-chip median and p10 |
test_chips.png | qualitative figure (VV / label / prediction) |
legacy/v1_degenerate_best.pt | the withdrawn v1 weights, reproducibility only |
Usage
from huggingface_hub import snapshot_download
import torch, sys
path = snapshot_download("stratus-labs/radiance-sen1floods11-v1")
sys.path.insert(0, path)
from model import RadianceMAE, RadianceSegModel
encoder = RadianceMAE(img_size=224, patch_size=16, in_chans=2)
model = RadianceSegModel(encoder)
ckpt = torch.load(f"{path}/best.pt", map_location="cpu", weights_only=False)
model.load_state_dict(ckpt["model"]) # strict — every key must match
model.eval()
# sar: [B, 2, 224, 224] — VV + VH in dB, clipped to [-60, 20], z-scored with the SSL4EO-S12 stats above
with torch.no_grad():
flood_prob = torch.sigmoid(model(sar)).squeeze(1) # [B, 224, 224] in [0, 1]; threshold 0.5
For tiles larger than 224 × 224, run a sliding window with overlap and average the probabilities — a full-tile protocol is on the roadmap and will change the published numbers.
Limitations
- Small floods are missed. Per-chip p10 IoU is 0.0 on every split; floods under ~1 % of a crop are usually not detected.
- Small train pool (252 chips). Best epoch is 8; the encoder overfits after that even with LLRD.
- S1-only, fixed 224 × 224 crop. Optical + DEM fusion and full-tile inference are known to help and are not in v2.
- No radiometric calibration correction beyond the SSL4EO-S12 dB normalisation.
- Decision support, not automated alerts. Review before acting on a map.
Roadmap (v3)
Reordered 2026-09-09 after the threshold study above. A decoder change was previously top of this list on the assumption the model could not resolve small floods; that assumption was wrong, so it has moved down.
- Publish full-tile as the primary protocol and retire centre-crop to a reproducibility footnote.
- Out-of-region check under full-tile. The Bolivia split has only been scored under centre-crop; there is no full-tile Bolivia number yet, so do not pair 0.629 with 0.652.
- Weak/auto-labelled SEN1Floods11 chips for train-set expansion.
- S1GFloods pre/post change-detection head on the same backbone.
- Skip-connection decoder for small-target precision — still worth doing, but for precision, not for recall.
- Sentinel-2 + DEM fusion (a Radiance-DualMod project, not a head change).
Citation
@misc{stratus-labs-radiance-sen1floods11-v2,
title = {Radiance SEN1Floods11 v2: Sentinel-1 flood segmentation on the Radiance MAE Base v1 backbone},
author = {Stratus Labs},
year = {2026},
publisher = {HuggingFace},
howpublished = {\url{https://huggingface.co/stratus-labs/radiance-sen1floods11-v1}},
}
Related
- Backbone: Radiance MAE Base v1
- Dataset: Sen1Floods11 on HuggingFace (redistribution of Bonafilia et al. 2020)
- Sibling: Nocturne v1 (bioacoustics)
- All Stratus Labs releases: huggingface.co/stratus-labs
Research page
The write-up and every Radiance release, in one place: https://runstratus.com/research/radiance-mae-base-v1-sentinel1-sar