File size: 15,608 Bytes
0a3b5b9 7513761 0a3b5b9 7513761 0a3b5b9 7513761 0a3b5b9 7513761 0a3b5b9 7513761 0a3b5b9 7513761 0a3b5b9 7513761 0a3b5b9 7513761 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | ---
license: apache-2.0
language:
- en
tags:
- remote-sensing
- semantic-segmentation
- agriculture
- crop-mapping
- sentinel-2
- video-swin-transformer
- pastis
- pytorch
- amd-rocm
- foundation-model
datasets:
- pastis
metrics:
- mean_iou
- f1
- accuracy
model-index:
- name: AgriFM-small-PASTIS
results:
- task:
type: image-segmentation
name: Semantic Segmentation
dataset:
name: PASTIS
type: pastis
metrics:
- type: mean_iou
value: 44.43
name: Mean IoU (5-fold CV)
- type: f1
value: 58.35
name: F1 Score (5-fold CV)
- type: accuracy
value: 68.36
name: Overall Accuracy (5-fold CV)
---
# AgriFM Γ PASTIS
> **Reimplementation of AgriFM** β *A Multi-source Temporal Remote Sensing Foundation Model for Agriculture Mapping* (Li et al., 2025) β adapted for the [PASTIS](https://github.com/VSainteuf/pastis-benchmark) benchmark dataset and trained from scratch on a single AMD MI300X GPU (ROCm 7.0).
---
## π Results β 5-Fold Cross-Validation
### Summary
| Metric | Fold 1 | Fold 2 | Fold 3 | Fold 4 | Fold 5 | **Mean Β± Std** |
|--------|--------|--------|--------|--------|--------|----------------|
| **mFscore** | 58.37% | 56.69% | 59.18% | 61.00% | 56.49% | **58.35% Β± 1.70%** |
| **mIoU** | 44.35% | 42.90% | 45.54% | 46.80% | 42.58% | **44.43% Β± 1.62%** |
| **OA** | 67.10% | 66.85% | 69.96% | 71.48% | 66.41% | **68.36% Β± 2.13%** |
| **Kappa** | 60.02% | 59.71% | 62.79% | 64.33% | 58.99% | **61.17% Β± 2.19%** |
| **mPrecision** | 52.78% | 51.33% | 54.51% | 57.40% | 51.24% | **53.45% Β± 2.47%** |
| **mRecall** | 70.72% | 69.00% | 68.69% | 68.93% | 68.72% | **69.21% Β± 0.82%** |
> **Official benchmark result: mFscore = 58.35% Β± 1.70%** β trained from scratch, no pretrained weights, 32 minutes per fold on AMD MI300X.
---
## π Per-Class IoU β All Folds
| Class | Fold 1 | Fold 2 | Fold 3 | Fold 4 | Fold 5 | **Mean** |
|-------|--------|--------|--------|--------|--------|----------|
| π½ Corn | 76.76% | 75.31% | 75.75% | 77.09% | 74.60% | **75.90%** |
| πΏ Winter rapeseed | 74.17% | 76.05% | 77.14% | 77.49% | 72.90% | **75.55%** |
| πΎ Beet | 73.32% | 72.52% | 78.01% | 71.87% | 73.78% | **73.90%** |
| πΎ Soft winter wheat | 72.22% | 68.92% | 71.64% | 71.18% | 73.37% | **71.47%** |
| πΏ Soybeans | 66.81% | 55.03% | 63.35% | 62.73% | 59.49% | **61.48%** |
| πΎ Winter barley | 56.56% | 59.08% | 57.70% | 62.90% | 56.94% | **58.64%** |
| π± Meadow | 56.56% | 55.96% | 57.47% | 58.18% | 55.29% | **56.69%** |
| π» Sunflower | 53.94% | 48.13% | 64.54% | 57.16% | 50.59% | **54.87%** |
| π« Background | 48.39% | 50.20% | 51.60% | 54.88% | 49.83% | **50.98%** |
| πΎ Winter durum wheat | 43.30% | 43.68% | 49.19% | 47.26% | 39.38% | **44.56%** |
| π₯ Potatoes | 37.33% | 37.75% | 34.93% | 42.60% | 29.07% | **36.34%** |
| πΏ Grapevine | 36.16% | 36.62% | 37.52% | 38.60% | 34.60% | **36.70%** |
| πΎ Spring barley | 30.51% | 40.00% | 32.87% | 38.06% | 33.97% | **35.08%** |
| πΏ Leguminous fodder | 23.17% | 20.88% | 21.02% | 28.88% | 18.12% | **22.41%** |
| π Fruits/veg/flowers | 22.27% | 19.75% | 20.97% | 22.67% | 21.97% | **21.53%** |
| πΎ Winter triticale | 25.01% | 19.33% | 24.30% | 23.72% | 19.32% | **22.34%** |
| πΎ Mixed cereal | 17.63% | 12.64% | 13.20% | 16.81% | 13.02% | **14.66%** |
| π Orchard | 15.25% | 13.11% | 26.05% | 18.81% | 13.14% | **17.27%** |
| πΏ Sorghum | 13.29% | 10.12% | 8.04% | 18.40% | 19.68% | **13.91%** |
---
## β‘ Inference Speed
Measured on AMD MI300X (191.7 GB VRAM), AgriFM-small (39.6M params):
| Batch Size | Time (ms) | Throughput | VRAM Used |
|-----------|-----------|------------|-----------|
| 1 | 7.8 ms | 128.9 patches/sec | 1.12 GB |
| 4 | 19.6 ms | 203.8 patches/sec | 2.08 GB |
| 8 | 37.1 ms | 215.4 patches/sec | 3.40 GB |
| 16 | 73.4 ms | 218.0 patches/sec | 6.03 GB |
| 32 | 143.0 ms | 223.8 patches/sec | 11.31 GB |
| 64 | 281.0 ms | 227.8 patches/sec | 21.85 GB |
> Each patch = 32 temporal frames Γ 10 spectral bands Γ 128Γ128 pixels of Sentinel-2 imagery.
---
## ποΈ Model Architecture
```
Input: (B, 32, 10, 128, 128) β 32 frames, 10 S2 bands, 128Γ128 pixels
β
SwinPatchEmbed3D patch_size=(4,2,2) β (B, 64, 8, 64, 64)
β
SwinTransformer3D β 4 stages with synchronized spatiotemporal downsampling
Stage 1: depth=2, heads=2, dim=64 β (B, 64, 8, 64, 64)
Stage 2: depth=2, heads=4, dim=128 β (B, 128, 4, 32, 32)
Stage 3: depth=6, heads=8, dim=256 β (B, 256, 2, 16, 16)
Stage 4: depth=2, heads=16, dim=512 β (B, 512, 1, 8, 8)
β
feature_fusion='cat' β (B, 512, 8, 8) β temporal dim collapsed into channels
β
MultiFusionNeck β 3-level U-Net decoder with skip connections
Level 1: 8Γ8 β 16Γ16 (skip from Stage 3)
Level 2: 16Γ16 β 32Γ32 (skip from Stage 2)
Level 3: 32Γ32 β 64Γ64 (skip from Stage 1)
Final: 64Γ64 β 128Γ128
β
CropFCNHead Conv(512β256)+ReLU+Conv(256β20)
β
Output: (B, 20, 128, 128) β per-pixel crop type probabilities
```
### Key Innovation: Synchronized Spatiotemporal PatchMerging
Unlike standard Video Swin Transformer which only downsamples spatially, AgriFM's `PatchMerging` with `mean_frame_down=True` simultaneously:
- **Spatially**: 4 strided samples concatenated β 4C channels
- **Temporally**: mean pooling over D_step consecutive frames
This keeps spatial and temporal scales synchronized throughout the hierarchy.
---
## π Repository Structure
```
AgriFM_PASTIS/
βββ models/
β βββ video_swin_transformer.py # VST backbone + synchronized PatchMerging
β βββ neck.py # MultiFusionNeck (U-Net decoder)
β βββ heads.py # CropFCNHead
β βββ agrifm.py # Full model + build_agrifm_pastis_small/tiny/base
βββ datasets/
β βββ pastis_dataset.py # Official 5-fold splits via geopandas
βββ losses/
β βββ loss.py # CropCELoss + hard mining + class weights
βββ evaluation/
β βββ metrics.py # OA, mIoU, mFscore, Precision, Recall, Kappa
βββ train.py # Training loop with colored output + logging
βββ visualize_results.py # 8-plot visualization suite per fold
βββ visualize_all_folds.py # Cross-validation summary plots
βββ requirements.txt
βββ checkpoints/
β βββ fold1_v3_best_model.pth # Best checkpoint: fold1, epoch 92, mFscore=58.37%
βββ results/
βββ fold1/ # Logs, metrics, plots for fold 1
βββ fold2/ ... fold5/ # Same for folds 2β5
βββ all_folds_summary/ # Cross-validation summary plots
```
---
## π Quick Start
### Installation
```bash
git clone https://huggingface.co/Dhruv1000/AgriFM_PASTIS
cd AgriFM_PASTIS
pip install torch torchvision timm einops geopandas matplotlib seaborn scikit-learn
```
### Training (single fold)
```bash
python train.py \
--data_root /path/to/PASTIS \
--fold 1 \
--small_model \
--epochs 100 \
--batch_size 16 \
--lr 5e-5 \
--weight_decay 0.05 \
--warmup_iters 500 \
--num_workers 4 \
--amp \
--work_dir ./work_dirs/fold1
```
### Training (all 5 folds)
```bash
for fold in 1 2 3 4 5; do
python train.py \
--data_root /path/to/PASTIS \
--fold $fold \
--small_model \
--epochs 100 \
--batch_size 16 \
--lr 5e-5 \
--weight_decay 0.05 \
--work_dir ./work_dirs/fold${fold}
done
```
### Inference
```python
import torch
from models.agrifm import build_agrifm_pastis_small
model = build_agrifm_pastis_small(num_classes=20)
ckpt = torch.load('checkpoints/fold1_v3_best_model.pth',
weights_only=False)
model.load_state_dict(ckpt['model'])
model.eval()
# Input: (B, T, C, H, W) = (batch, 32 frames, 10 bands, 128px, 128px)
x = torch.randn(1, 32, 10, 128, 128)
logits = model(x) # (1, 20, 128, 128)
pred = logits.argmax(dim=1) # (1, 128, 128) β class indices
```
### Visualize Results
```bash
# Per-fold visualization (8 plots)
python visualize_results.py \
--work_dir ./work_dirs/fold1 \
--data_root /path/to/PASTIS \
--fold 1 --model_size small
# All-folds cross-validation summary
python visualize_all_folds.py
```
---
## π Training Configuration
| Parameter | Value |
|-----------|-------|
| Model | AgriFM-small (39.6M params) |
| Optimizer | AdamW (Ξ²=(0.9, 0.999)) |
| Learning rate | 5e-5 |
| Weight decay | 0.05 |
| LR schedule | Linear warmup (500 iters) β cosine decay to 1e-6 |
| Batch size | 16 |
| Epochs | 100 |
| Loss | Cross-entropy + hard mining (top 50%) + inverse-freq class weights |
| Augmentation | Random h-flip, v-flip, 90/180/270Β° rotation |
| AMP | Enabled (torch.amp, ROCm 7.0 compatible) |
| Gradient clipping | max_norm = 5.0 |
| Input frames | 32 (uniform sampled from 61) |
| Normalization | Per-fold mean/std from NORM_S2_patch.json |
---
## π¦ Dataset: PASTIS
| Property | Details |
|----------|---------|
| Total patches | 2,433 geo-referenced tiles |
| Patch size | 128 Γ 128 pixels at 10m resolution |
| Satellite | Sentinel-2 (10 spectral bands) |
| Temporal length | 61 observations per patch |
| Label hierarchy | 3 levels β we use channel 0 (20 coarse classes) |
| Folds | 5 official geographic folds (~487β496 patches each) |
| Fold assignment | From `metadata.geojson` via geopandas |
| Ignore index | Class 19 (Void) excluded from all metrics |
### PASTIS Classes (20 coarse classes)
| ID | Class | Avg IoU (5-fold) |
|----|-------|-----------------|
| 0 | Background | 50.98% |
| 1 | Meadow | 56.69% |
| 2 | Soft winter wheat | 71.47% |
| 3 | Corn | 75.90% |
| 4 | Winter barley | 58.64% |
| 5 | Winter rapeseed | 75.55% |
| 6 | Spring barley | 35.08% |
| 7 | Sunflower | 54.87% |
| 8 | Grapevine | 36.70% |
| 9 | Beet | 73.90% |
| 10 | Winter triticale | 22.34% |
| 11 | Winter durum wheat | 44.56% |
| 12 | Fruits/veg/flowers | 21.53% |
| 13 | Potatoes | 36.34% |
| 14 | Leguminous fodder | 22.41% |
| 15 | Soybeans | 61.48% |
| 16 | Orchard | 17.27% |
| 17 | Mixed cereal | 14.66% |
| 18 | Sorghum | 13.91% |
| 19 | Void (ignore) | β |
---
## π§ Key Implementation Decisions
### 1. No MMSeg/MMEngine dependency
Original GitHub uses MMSegmentation. This reimplementation uses **pure PyTorch only** β no registry system, no config framework. Runs anywhere PyTorch runs.
### 2. Official fold splits
Initial fold assignment by patch ID prefix caused catastrophic distribution mismatch (W.Durum ratio: 88Γ between train/val). Fixed by loading official fold assignments from `metadata.geojson` using geopandas. Val loss dropped from 34 β 0.67 at epoch 1.
### 3. Class-weighted loss
Added `compute_class_weights()` with inverse-frequency weighting. Background weight = 0.01, Winter Durum Wheat weight = 7.24 β essential for learning rare crops.
### 4. Model size variants
| Variant | Params | embed_dim | depths | Use case |
|---------|--------|-----------|--------|----------|
| `base` | 196.2M | 128 | [2,2,18,2] | Paper-faithful, needs pretrained weights |
| `small` β
| 39.6M | 64 | [2,2,6,2] | **Used here β recommended for scratch** |
| `tiny` | 9.6M | 32 | [2,2,4,2] | Backup if small overfits |
### 5. AMD ROCm compatibility
- `torch.amp.GradScaler('cuda')` and `torch.amp.autocast('cuda')` (new API)
- `torch.load(..., weights_only=False)` for numpy scalar checkpoints
- `MIOPEN_LOG_LEVEL=0` to suppress kernel selection warnings (not errors)
### 6. Added Cohen's Kappa metric
Not in original codebase. Computed from full confusion matrix. Final 5-fold mean Kappa = **61.17%** (substantial agreement).
---
## π Comparison: Paper vs GitHub vs This Implementation
| Aspect | Paper | GitHub | **Ours** |
|--------|-------|--------|----------|
| Framework | PyTorch | PyTorch + MMSeg + MMEngine | **Pure PyTorch** |
| Dataset | 25.2M global images | Custom HDF5 | **PASTIS .npy** |
| Input size | 224Γ224 | 256Γ256 | **128Γ128** |
| Patch embed | SwinPatchEmbed3D | Identical | **Identical β** |
| Patch size | (4,2,2) | (4,2,2) | **Identical β** |
| PatchMerging | Synchronized | mean_frame_down=True | **Identical β** |
| feature_fusion | Not explicit | 'cat' | **Identical β** |
| Window size | Not explicit | (8,7,7) | **Identical β** |
| Neck | MultiFusionNeck | MultiFusionNeck | **Identical β** |
| Head | CropFCNHead | CropFCNHead | **Identical β** |
| Pre-training | LC fractions + L1 | Same | **Not implemented** |
| Class weights | Not mentioned | Not implemented | **Added β** |
| Kappa metric | Not reported | Not implemented | **Added β** |
| Augmentation | Not detailed | flip/rotate | **flip + rotate β** |
| Grad clipping | Not specified | None | **max_norm=5.0 β** |
| GPU | 10Γ NVIDIA L40 | 10Γ NVIDIA L40 | **1Γ AMD MI300X** |
---
## π Training Dynamics (Fold 1 example)
| Epoch | Train Loss | Val Loss | mFscore | mIoU | Kappa |
|-------|-----------|----------|---------|------|-------|
| 1 | 0.878 | 0.671 | 2.79% | 1.47% | 2.39% |
| 4 | 0.431 | 0.472 | 20.08% | 12.34% | 10.76% |
| 10 | 0.320 | 0.380 | ~33% | ~22% | ~24% |
| 18 | 0.222 | 0.323 | 35.55% | 24.17% | 24.93% |
| 55 | 0.083 | 0.363 | 53.37% | 39.92% | 53.16% |
| **92** | **0.050** | **0.350** | **58.20%** | **44.20%** | **60.0%** |
| 100 | 0.048 | 0.360 | 57.90% | 44.10% | 59.8% |
Total training time per fold: **~32 minutes** on AMD MI300X.
---
## πΌοΈ Visualizations
The repository includes 8 visualization plots per fold plus 6 cross-validation summary plots:
**Per-fold (in `results/fold{N}/plots/`):**
- `0_summary_card.png` β Dark-theme dashboard with all metrics
- `1_training_curves.png` β 6-panel training history
- `2_per_class_iou.png` β Sorted horizontal bar chart
- `3_metrics_radar.png` β Spider/radar chart of all 6 metrics
- `4_confusion_matrix.png` β Raw + normalized confusion matrices
- `5_prediction_maps.png` β 6 sample RGB / GT / Pred / Error maps
- `6_class_iou_scatter.png` β Per-class IoU scatter with mIoU line
- `7_overfitting_analysis.png` β Train vs val loss gap over epochs
**Cross-validation (`results/all_folds_summary/`):**
- `CV1_metrics_all_folds.png` β Grouped bar chart all folds
- `CV2_per_class_heatmap.png` β IoU heatmap: folds Γ classes
- `CV3_training_curves_overlay.png` β All folds on same axes
- `CV4_summary_table.png` β Mean Β± std table
- `CV5_mean_per_class_iou.png` β Mean IoU with std error bars
- `CV6_metric_boxplots.png` β Box plots with individual fold points
---
## π Citation
If you use this code or results, please cite the original AgriFM paper:
```bibtex
@article{li2025agrifm,
title = {AgriFM: A Multi-source Temporal Remote Sensing Foundation Model
for Agriculture Mapping},
author = {Li, Wenyuan and Liang, Shunlin and Chen, Kun and others},
journal = {Remote Sensing of Environment},
year = {2025},
doi = {10.1016/j.rse.2026.115234}
}
```
And the PASTIS dataset:
```bibtex
@inproceedings{garnot2021pastis,
title = {Panoptic Segmentation of Satellite Image Time Series
with Convolutional Temporal Attention Networks},
author = {Garnot, Vivien Sainte Fare and Landrieu, Loic},
booktitle = {ICCV},
year = {2021}
}
```
---
## π License
Apache 2.0 β see LICENSE for details.
---
*Trained on AMD MI300X Β· ROCm 7.0 Β· PyTorch 2.x Β· April 2026*
|