问题确认 Search before asking
需求描述 Feature Description
Feature Request: RankSEG Post-Processing Integration for Metric-Aware Inference Optimization
📋 Issue Type
🎯 Summary
We propose to integrate RankSEG as an optional post-processing layer in PaddleSeg's inference pipeline. RankSEG is a plug-and-play, training-free module that replaces the standard argmax step with a metric-aware optimization, yielding measurable gains in Dice/IoU without any retraining.
On CamVid with PP-LiteSeg-T, we demonstrate:
| Model |
mIoU ($IoU^D$) |
Dice |
Note |
| PP-LiteSeg-T (baseline) |
75.92% |
81.38% |
argmax |
| PP-LiteSeg-T + RankSEG |
76.13% |
82.48% |
RankSEG |
+0.21 pp mIoU, +1.10 pp Dice — zero retraining.
A fully reproducible environment is available as a pre-built Docker image: ghcr.io/leev1s/rankseg:paddleseg. See the README for exact commands.
🔬 Background: What is RankSEG?
RankSEG is a post-processing optimization technique published in two peer-reviewed venues:
Core Idea
Standard segmentation pipelines end with:
pred = argmax(logit) # maximizes Accuracy, not Dice/IoU
argmax is theoretically optimal for pixel-wise Accuracy but not for non-decomposable metrics like Dice or IoU. RankSEG bridges this gap:
# RankSEG (3 lines in pure PyTorch)
from rankseg import RankSEG
rankseg = RankSEG(metric='dice') # or 'iou', 'acc'
pred = rankseg.predict(softmax(logit, dim=1))
Available solvers: RMA (fast, recommended), BA, TRNA, BA+TRNA.
⚠️ Current Limitation: The Dual-Framework Challenge
RankSEG is implemented in PyTorch. PaddleSeg is built on PaddlePaddle. Running both frameworks simultaneously in a single Python process comes with real costs.
Our current implementation explicitly acknowledges this by performing a logit.numpy() round-trip:
# rankseg/paddleseg/infer.py (our prototype)
import torch
from rankseg import RankSEG
logit_np = logit.numpy() # Paddle → NumPy
logit_torch = torch.from_numpy(logit_np) # NumPy → PyTorch
probs = torch.softmax(logit_torch, dim=1)
pred_np = RankSEG(metric='dice').predict(probs).numpy() # PyTorch → NumPy
pred = paddle.to_tensor(pred_np, dtype='int32') # NumPy → Paddle
Our Migration Intent
We fully intend to port RankSEG's core algorithms to PaddlePaddle so that the logit never leaves the Paddle ecosystem. The RMA solver, for example, requires only basic tensor operations (sum, cumsum, sort, element-wise multiply) that map directly to paddle equivalents. We would like to coordinate with the PaddleSeg team on this transition.
The current dual-framework implementation should be viewed as a functional prototype for demonstration and benchmarking purposes, not a production design.
🛠️ Proposed Integration Architecture
Minimal-Invasive Approach: _generate_pred_from_logit
Rather than modifying the public API of inference() / aug_inference(), we propose inserting a small internal helper into paddleseg/core/infer.py:
def _generate_pred_from_logit(logit,
use_multilabel=False,
use_rankseg=False,
rankseg_metric='dice',
rankseg_solver='RMA',
rankseg_output_mode='multiclass'):
"""
Replace argmax with a metric-aware predictor (RankSEG) when requested.
Falls back to original PaddleSeg logic when use_rankseg=False.
"""
if not use_rankseg:
# ---- ORIGINAL PADDLESEG LOGIC (unchanged) ----
if not use_multilabel:
return paddle.argmax(logit, axis=1, keepdim=True, dtype='int32')
else:
return (F.sigmoid(logit) > 0.5).astype('int32')
else:
# ---- RANKSEG PATH ----
# Phase 1 (current): Paddle → NumPy → PyTorch bridge
# Phase 2 (planned): native Paddle RankSEG implementation
import torch
from rankseg import RankSEG
probs = torch.softmax(torch.from_numpy(logit.numpy()), dim=1)
rs = RankSEG(metric=rankseg_metric, solver=rankseg_solver,
output_mode=rankseg_output_mode)
pred_np = rs.predict(probs).cpu().numpy()
if rankseg_output_mode == 'multiclass' and pred_np.ndim == 3:
pred_np = pred_np[:, None, :, :]
return paddle.to_tensor(pred_np, dtype='int32')
Both inference() and aug_inference() call _generate_pred_from_logit(logit, ...) at their tail, passing RankSEG flags through **kwargs. The existing pred, _ = infer.inference(...) call signature is fully preserved for all callers that do not opt in.
Config & CLI Support
# paddleseg config YAML (optional new section)
post_processing:
use_rankseg: True
rankseg_metric: dice # dice | iou | acc
rankseg_solver: RMA # RMA | BA | TRNA | BA+TRNA
rankseg_output_mode: multiclass # multiclass | multilabel
# tools/val.py
python tools/val.py \
--config configs/pp_liteseg/pp_liteseg_stdc1_camvid_960x720_10k.yml \
--model_path model.pdparams \
--use_rankseg --rankseg_metric="iou"
📦 Reproducible Demo via Docker
README We provide a fully containerized environment that demonstrates the integration end-to-end.
Image Architecture
ghcr.io/leev1s/rankseg:paddleseg
├── Stage 1 (builder) — uv:python3.10-bookworm-slim
│ ├── paddlepaddle==3.3.0 (CPU build, ~700 MB)
│ └── torch (CPU build via pytorch.org/whl/cpu, ~500 MB)
├── Stage 2 (pioneer) — uv:python3.10-bookworm
│ ├── PaddleSeg release/2.10 (cloned & installed from source)
│ └── rankseg (installed from source)
└── Stage 3 (runtime) — python:3.10-slim-bookworm
├── /opt/venv ← copied virtual environment
├── /workspace/configs ← PaddleSeg upstream configs (release/2.10)
└── /workspace/{val,predict,analyse}.py ← RankSEG-aware tools
Both paddlepaddle and torch are installed in the same virtual environment. GPU version is also tested but not published.
Quick Reproduction
# Pull image
docker pull ghcr.io/leev1s/rankseg:paddleseg
# Prepare host directories
mkdir -p ./data ./models ./output
curl -L https://paddleseg.bj.bcebos.com/dataset/camvid.tar | tar -xC ./data
mkdir -p ./models/pp_liteseg_camvid
curl -L https://paddleseg.bj.bcebos.com/dygraph/camvid/pp_liteseg_stdc1_camvid_960x720_10k/model.pdparams \
-o ./models/pp_liteseg_camvid/model.pdparams
# Run container
docker run --rm -it \
-v ./data:/workspace/data \
-v ./models:/workspace/pretrained_models \
-v ./output:/workspace/output \
ghcr.io/leev1s/rankseg:paddleseg
# Inside container:
# Baseline
python val.py \
--config configs/pp_liteseg/pp_liteseg_stdc1_camvid_960x720_10k.yml \
--model_path pretrained_models/pp_liteseg_camvid/model.pdparams
# With RankSEG
python val.py \
--config configs/pp_liteseg/pp_liteseg_stdc1_camvid_960x720_10k.yml \
--model_path pretrained_models/pp_liteseg_camvid/model.pdparams \
--use_rankseg --rankseg_metric='iou'
📈 Benchmark Results
All results use $IoU^D$ (dataset-level accumulation) — the metric PaddleSeg reports by default:
$$IoU^D = \frac{\sum_{i=1}^{N} TP_i}{\sum_{i=1}^{N} (TP_i + FP_i + FN_i)}$$
$IoU^I$ (image-level average) is computed differently — it first calculates IoU for each image independently, then takes the mean:
$$IoU^I = \frac{1}{N} \sum_{i=1}^{N} \frac{TP_i}{TP_i + FP_i + FN_i}$$
Key difference: $IoU^D$ accumulates TP/FP/FN across all images before computing the ratio, while $IoU^I$ computes the ratio per-image first, then averages. $IoU^D$ is more sensitive to image size distribution (larger images contribute more to the accumulation), while $IoU^I$ treats all images equally regardless of size.
Note: For detailed discussions on the theoretical and practical differences between $IoU^D$ and $IoU^I$, please refer to the NeurIPS 2025 paper.
CamVid — PP-LiteSeg-T (STDC1)
# Baseline (argmax)
[EVAL] #Images: 101 mIoU: 0.7592 Acc: 0.9652 Kappa: 0.9564 Dice: 0.8138
[EVAL] Image-level average mIoU (IoU^I): 0.7361
# + RankSEG (metric=iou, solver=RMA)
[EVAL] #Images: 101 mIoU: 0.7622 Acc: 0.9604 Kappa: 0.9506 Dice: 0.8255
[EVAL] Image-level average mIoU (IoU^I): 0.7377
| Metric |
Baseline |
+ RankSEG |
Δ |
| mIoU ($IoU^D$) |
75.92% |
76.13% |
+0.21 pp |
| Dice |
81.38% |
82.48% |
+1.10 pp |
| mIoU ($IoU^I$) |
73.61% |
73.77% |
+0.16 pp |
🖼️ Visualization Results
Below are qualitative comparisons between baseline (argmax) and RankSEG predictions on CamVid test images:
Example 1: 0016E5_08051
| Baseline (argmax) |
RankSEG (iou, RMA) |
 |
 |
Example 2: 0016E5_08159
| Baseline (argmax) |
RankSEG (iou, RMA) |
 |
 |
Observations: RankSEG improves boundary delineation and reduces small misclassified regions, particularly in challenging areas like thin structures and class boundaries.
🔗 References
📝 Checklist
Special thanks to @statmlben @ZixunWang
是否愿意提交PR Are you willing to submit a PR?
问题确认 Search before asking
需求描述 Feature Description
Feature Request: RankSEG Post-Processing Integration for Metric-Aware Inference Optimization
📋 Issue Type
🎯 Summary
We propose to integrate RankSEG as an optional post-processing layer in PaddleSeg's inference pipeline. RankSEG is a plug-and-play, training-free module that replaces the standard
argmaxstep with a metric-aware optimization, yielding measurable gains in Dice/IoU without any retraining.On CamVid with PP-LiteSeg-T, we demonstrate:
argmaxRankSEG+0.21 pp mIoU, +1.10 pp Dice — zero retraining.
A fully reproducible environment is available as a pre-built Docker image:
ghcr.io/leev1s/rankseg:paddleseg. See the README for exact commands.🔬 Background: What is RankSEG?
RankSEG is a post-processing optimization technique published in two peer-reviewed venues:
Core Idea
Standard segmentation pipelines end with:
argmaxis theoretically optimal for pixel-wise Accuracy but not for non-decomposable metrics like Dice or IoU. RankSEG bridges this gap:Available solvers:
RMA(fast, recommended),BA,TRNA,BA+TRNA.RankSEG is implemented in PyTorch. PaddleSeg is built on PaddlePaddle. Running both frameworks simultaneously in a single Python process comes with real costs.
Our current implementation explicitly acknowledges this by performing a
logit.numpy()round-trip:Our Migration Intent
We fully intend to port RankSEG's core algorithms to PaddlePaddle so that the logit never leaves the Paddle ecosystem. The RMA solver, for example, requires only basic tensor operations (
sum,cumsum,sort, element-wise multiply) that map directly topaddleequivalents. We would like to coordinate with the PaddleSeg team on this transition.The current dual-framework implementation should be viewed as a functional prototype for demonstration and benchmarking purposes, not a production design.
🛠️ Proposed Integration Architecture
Minimal-Invasive Approach:
_generate_pred_from_logitRather than modifying the public API of
inference()/aug_inference(), we propose inserting a small internal helper intopaddleseg/core/infer.py:Both
inference()andaug_inference()call_generate_pred_from_logit(logit, ...)at their tail, passing RankSEG flags through**kwargs. The existingpred, _ = infer.inference(...)call signature is fully preserved for all callers that do not opt in.Config & CLI Support
📦 Reproducible Demo via Docker
README We provide a fully containerized environment that demonstrates the integration end-to-end.
Image Architecture
Both
paddlepaddleandtorchare installed in the same virtual environment. GPU version is also tested but not published.Quick Reproduction
📈 Benchmark Results
All results use$IoU^D$ (dataset-level accumulation) — the metric PaddleSeg reports by default:
Key difference:$IoU^D$ accumulates TP/FP/FN across all images before computing the ratio, while $IoU^I$ computes the ratio per-image first, then averages. $IoU^D$ is more sensitive to image size distribution (larger images contribute more to the accumulation), while $IoU^I$ treats all images equally regardless of size.
Note: For detailed discussions on the theoretical and practical differences between$IoU^D$ and $IoU^I$ , please refer to the NeurIPS 2025 paper.
CamVid — PP-LiteSeg-T (STDC1)
🖼️ Visualization Results
Below are qualitative comparisons between baseline (argmax) and RankSEG predictions on CamVid test images:
Example 1: 0016E5_08051
Example 2: 0016E5_08159
Observations: RankSEG improves boundary delineation and reduces small misclassified regions, particularly in challenging areas like thin structures and class boundaries.
🔗 References
ghcr.io/leev1s/rankseg:paddleseg📝 Checklist
Special thanks to @statmlben @ZixunWang
是否愿意提交PR Are you willing to submit a PR?