Skip to content

Repository files navigation

Vesuvius — Surface Detection

Kaggle Silver Medal solution for the Vesuvius Challenge - Surface Detection competition.

License: MIT Python 3.10+ PyTorch MONAI nnUNetv2 CI Stars Issues

🥈 Silver Medalist · Final LB 0.59757 · Top 5% · 2026 (1st place: 0.62702)

Vesuvius Challenge Silver Medal

📖 Table of Contents

背景

Vesuvius Challenge - Surface Detection 要求从卷积 CT 体数据中分割出可展开的 papyrus 表面层 — 一个 3D 二值分割问题,体数据各向异性、表面薄且拓扑敏感,后处理(3D hysteresis + 各向异性 closing + 去小连通域)对 LB 影响显著。

本仓库把两条常见 baseline 流程脚本化,便于在本地(含 Windows)训练与生成 submission.zip:

  • nnUNetv2 路线(nnunet/):自动规划 / 预处理 / 训练 / 推理,调用 nnUNetv2_* CLI
  • 3D-SegM 路线(segm3d/):MONAI + PyTorch Lightning 的 3D 分割 baseline(SegResNet / SwinUNETR),含 v1 的 patch 训练 + 原尺寸滑窗推理

最终成绩

🥈 Silver Medalist — Kaggle Vesuvius Challenge - Surface Detection 银牌。

指标 数值
最终 Public/Private LB 0.59757
排名 Top 5%
第一名分数 0.62702
比赛年份 2026

仓库结构

Vesuvius/
├── data/                          # Kaggle 数据 / 模型下载工具
│   ├── download.py                # 通用 Kaggle CLI 包装,带重试/续传
│   ├── download_vesuvius.py       # Vesuvius 比赛数据下载 + 解压
│   └── download_model.py          # kagglehub 预训练模型下载
├── nnunet/                        # Pipeline A:nnUNetv2 CLI 包装
│   ├── data_process.py            # 准备 nnUNet_raw + plan_and_preprocess
│   ├── train.py                   # 调用 nnUNetv2_train(支持多 fold 并行)
│   └── infer.py                   # 调用 nnUNetv2_predict + 打包 submission.zip
├── segm3d/                        # Pipeline B:MONAI + PyTorch Lightning
│   ├── train.py                   # 训练(resize 到立方体,base 版)
│   ├── train_v1.py                # 训练(随机 patch 采样 + EMA + pos/neg 引导)
│   ├── infer.py                   # 推理(resize 回原尺寸,base 版)
│   └── infer_v1.py                # 推理(原尺寸 sliding_window_inference)
├── notebooks/                     # 三个参考 notebook
│   ├── surface-nnunet-training-inference-with-2xt4.ipynb
│   ├── surface-train-inference-3d-segm-gpu-augment.ipynb
│   └── vesuvius-predictions-1feb.ipynb
├── docs/
│   └── kaggle-silver-medal.png     # 银牌证书
├── .github/                        # CI + issue/PR 模板 + dependabot
│   ├── workflows/ci.yml            # py_compile + markdown-link-check
│   ├── ISSUE_TEMPLATE/             # bug_report.md, enhancement.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   ├── FUNDING.yml
│   └── markdown-link-check.json
├── ARCHITECTURE.md                 # 双管线架构图 + 数据流
├── CHANGELOG.md                    # 版本里程碑
├── CITATION.cff                    # 学术引用
├── CONTRIBUTING.md                  # 贡献指南
├── CODE_OF_CONDUCT.md              # Contributor Covenant 2.1
├── LICENSE                         # MIT
├── README.md                       # 本文件
└── requirements.txt                # Python 依赖

架构

双管线结构,详见 ARCHITECTURE.md:

                  ┌────────────────────────┐
                  │  data/  (download*)    │  →  DATA_ROOT/
                  └──────────┬─────────────┘
                              ▼
        ┌─────────────────────┴─────────────────────┐
        ▼                                           ▼
  ┌────────────────────┐                    ┌─────────────────────┐
  │  nnunet/  (CLI)     │                    │  segm3d/  (MONAI)   │
  │  data_process.py    │                    │  train.py           │
  │  train.py           │                    │  train_v1.py        │
  │  infer.py           │                    │  infer.py           │
  │                     │                    │  infer_v1.py        │
  └──────────┬─────────┘                    └──────────┬──────────┘
             │   --postprocess 1: 3D hysteresis +     │
             │   anisotropic closing + small-CC       │
             └──────────────────┬────────────────────┘
                                ▼
                          submission.zip

快速开始

git clone https://github.com/Calix-L/Vesuvius.git
cd Vesuvius
python -m venv venv
# PowerShell
.\venv\Scripts\Activate.ps1
# bash
source venv/bin/activate

# 1) 安装 PyTorch(按官方向导选 CUDA/CPU 版本)
#    https://pytorch.org/get-started/locally/

# 2) 安装其余依赖
pip install -r requirements.txt

验证安装:

python -c "import torch, monai, pytorch_lightning, nnunetv2, tifffile; \
  print('torch', torch.__version__, 'monai', monai.__version__, \
        'pl', pytorch_lightning.__version__, 'nnunetv2', nnunetv2.__version__)"

数据目录约定

下面把比赛数据根目录记为 DATA_ROOT:

DATA_ROOT/
  train_images/
    <case_id>.tif
    ...
  train_labels/
    <case_id>.tif
    ...
  test_images/
    <case_id>.tif
    ...
  test.csv
  • train_imagestrain_labels 必须文件名一一对应(同名 .tif / .npy / .npz 也可用于 3D-SegM)。
  • test.csvid 列决定最终需要打包进 zip 的 mask 文件名。

下载比赛数据:

python data/download_vesuvius.py --out ./kaggle_data/vesuvius_surface_detection

方案 A:3D-SegM(MONAI + Lightning)

训练

python segm3d/train.py \
  --train-images-dir "DATA_ROOT/train_images" \
  --train-labels-dir "DATA_ROOT/train_labels" \
  --output-dir "./work_3d_segm" \
  --arch segresnet \
  --model-input-size 160 160 160 \
  --max-epochs 20 \
  --batch-size 1 \
  --num-workers 2 \
  --gpu-transforms 1
  • 断点续训(从 output-dir 自动找 val_dice 最好的 ckpt 继续):
python segm3d/train.py ... --resume-best 1

推理 + 打包 submission.zip

segm3d/infer.py 需要 --root-dir 指向 DATA_ROOT(包含 test_images/test.csv)。

python segm3d/infer.py \
  --root-dir "DATA_ROOT" \
  --checkpoint-dir "./work_3d_segm" \
  --work-dir "./work_3d_segm_infer" \
  --postprocess 1

输出:

  • work-dir/submission_masks/*.tif
  • work-dir/submission.zip

(推荐)V1:patch 训练 + 原尺寸滑窗推理

如果你希望避免 "resize 到固定立方体再插回去" 带来的拓扑硬伤,可以使用 v1 脚本:

V1 训练

python segm3d/train_v1.py \
  --train-images-dir "DATA_ROOT/train_images" \
  --train-labels-dir "DATA_ROOT/train_labels" \
  --output-dir "./work_3d_segm_v1" \
  --arch segresnet \
  --patch-size 192 192 192 \
  --samples-per-volume 16 \
  --pos-fraction 0.7 \
  --batch-size 1 \
  --max-epochs 20 \
  --ema 1 \
  --ema-decay 0.999

多 GPU 训练(Lightning 原生支持):

  • Linux(推荐 DDP):
python segm3d/train_v1.py \
  --train-images-dir "DATA_ROOT/train_images" \
  --train-labels-dir "DATA_ROOT/train_labels" \
  --output-dir "./work_3d_segm_v1" \
  --devices 4 \
  --strategy ddp
  • Windows(通常用 spawn):
python segm3d/train_v1.py \
  --train-images-dir "DATA_ROOT/train_images" \
  --train-labels-dir "DATA_ROOT/train_labels" \
  --output-dir "./work_3d_segm_v1" \
  --devices 2 \
  --strategy ddp_spawn

关键参数:

  • --patch-size 192 192 192:训练 patch 尺寸(起步推荐 192³)。
  • --samples-per-volume:每个 volume 每个 epoch 采多少个 patch(越大越"见得多",但训练越慢)。
  • --pos-fraction:pos/neg 引导采样中"采到前景中心"的概率(尽量多采到标注区域,避免全 ignore patch)。
  • --ema / --ema-decay:启用 EMA;验证与保存 best 会使用 EMA 权重,监控指标为 val_fg_dice(只算 class=1)。
  • --devices:使用哪些 GPU(auto / 1 / 0,1,2,3)。
  • --strategy:分布式策略(auto / ddp / ddp_spawn)。
  • --num-nodes:多机训练节点数(默认 1)。

V1 推理 + 打包 submission.zip(原尺寸滑窗)

python segm3d/infer_v1.py \
  --root-dir "DATA_ROOT" \
  --checkpoint-dir "./work_3d_segm_v1" \
  --work-dir "./work_3d_segm_v1_infer" \
  --roi-size 192 192 192 \
  --overlap 0.6 \
  --sw-batch-size 1 \
  --tta 1 \
  --postprocess 1

关键参数:

  • --roi-size 192 192 192:滑窗推理窗口大小;也可试 224 224 224
  • --overlap 0.5~0.7:越大越平滑、越不容易断裂/块状边界,但更慢更吃显存。
  • --tta 1:轻扰动 TTA(不翻转),做两次推理取均值(可用 --tta-noise-std / --tta-scale-jitter 微调)。

方案 B:nnUNetv2

Step 1/2:数据准备 + 预处理

先把比赛数据转换为 nnUNet 目录结构,并运行 nnUNetv2_plan_and_preprocess:

python nnunet/data_process.py \
  --input-dir "DATA_ROOT" \
  --work-dir "./work" \
  --dataset-id 100 \
  --configuration 3d_fullres \
  --planner nnUNetPlannerResEncM
  • 只做"准备数据"(不跑 preprocess):--do-preprocess 0
  • 只做"preprocess"(不重新准备数据):--do-prepare 0
  • Windows 下如果 symlink 权限有问题:加 --no-symlinks

Step 2/2:训练

python nnunet/train.py \
  --work-dir "./work" \
  --dataset-id 100 \
  --configuration 3d_fullres \
  --plans-name nnUNetResEncUNetMPlans \
  --fold all \
  --epochs 250 \
  --num-gpus 1

多 GPU 注意事项(很重要)

nnUNet 的 DDP 多卡训练有一个硬性约束:global batch_size 必须 >= GPU 数(否则会 assert 报错)。

  • 例如你贴出来的 3d_fullres/3d_lowres 在 plans 里 batch_size=2,所以:
    • 单次 DDP 最多只能用 2 张卡:--num-gpus 2
    • 直接 --num-gpus 8 一定会失败
  • 我已在脚本里加入"提前检查":当 --num-gpus 超过 plans 的 batch_size 时,会直接给出更清晰的报错与建议。

用满 8 卡的推荐方式:并行跑多个 fold

当单次 DDP 只能用 12 卡时,想把 8 卡吃满,推荐改为"并行启动多个 fold",每个 fold 占用 12 卡。

示例:8 张卡并行跑 4 个 fold(每个 fold 2 卡):

python nnunet/train.py \
  --work-dir "./work" \
  --dataset-id 100 \
  --configuration 3d_fullres \
  --plans-name nnUNetResEncUNetMPlans \
  --fold all \
  --epochs 250 \
  --parallel-folds 1 \
  --num-gpus 8 \
  --devices 0,1,2,3,4,5,6,7 \
  --gpus-per-job 2

参数说明:

  • --parallel-folds 1:开启并行 fold 模式(多进程)。
  • --devices:指定 GPU 池(会为每个子进程设置 CUDA_VISIBLE_DEVICES)。
  • --gpus-per-job:每个 fold 占用几张卡(必须 <= plans batch_size;你这个 plans 是 2)。

小技巧:

  • 不写 --devices 时:会优先尝试读取环境变量 CUDA_VISIBLE_DEVICES 作为 GPU 池;如果也没有,会假设 0..num_gpus-1
  • --fold 除了 all,也支持 00,1,2 这种形式(方便你只跑部分 fold)。

推理 + 打包 submission.zip

python nnunet/infer.py \
  --root-dir "DATA_ROOT" \
  --work-dir "./work_infer" \
  --dataset-id 100 \
  --configuration 3d_fullres \
  --plans-name nnUNetResEncUNetMPlans \
  --trainer nnUNetTrainer_250epochs \
  --fold all \
  --checkpoint checkpoint_final.pth \
  --save-probabilities 1 \
  --postprocess 1

输出:

  • work-dir/submission_masks/*.tif
  • work-dir/submission.zip

可选:predictions 风格后处理

两个推理脚本(nnunet/infer.pysegm3d/infer.pysegm3d/infer_v1.py)都支持 --postprocess 1,原型来自 notebooks/vesuvius-predictions-1feb.ipynb

默认参数(可按需调整):

  • --T-low 0.30 / --T-high 0.80:3D hysteresis 阈值
  • --z-radius 3 / --xy-radius 2:各向异性 closing 结构元
  • --dust-min-size 100:去除小连通域

依赖:scipy + scikit-image(已在 requirements.txt 中列出)。

常见问题

  • 找不到数据/文件名不匹配:确保 train_imagestrain_labels 文件名完全一致;并确认 test.csv 存在且 test_images 下有对应 .tif
  • Windows symlink 失败:nnUNet 相关脚本可用 --no-symlinks,或开启 Windows 开发者模式/管理员权限。
  • 显存不够(OOM):优先减小 --model-input-size(3D-SegM)或减小 batch、关闭 --gpu-transforms;nnUNet 则考虑换配置/patch size 或减少并行。
  • nnUNet DDP --num-gpus 8 失败:plans 的 batch_size=2,单次 DDP 最多 2 卡;改用 --parallel-folds 1 --gpus-per-job 2 并行多个 fold。

关键依赖

  • Python 3.10+
  • PyTorch(https://pytorch.org/get-started/locally/)
  • monaipytorch-lightning — 3D-SegM 路线
  • nnunetv2 — nnUNet 路线
  • tifffile — TIFF I/O
  • scipy + scikit-image — predictions 风格后处理
  • pandastest.csv 处理
  • kagglehubdata/download_model.py 的预训练模型下载

完整列表见 requirements.txt

文档

贡献

欢迎提交管线修复、后处理改进或文档补全,请先读 CONTRIBUTING.md。提交 PR 前运行 pre-commit run --all-files

引用

@software{calix-l_vesuvius_2026,
  author       = {Calix-L},
  title        = {vesuvius: Surface Detection baselines (nnUNetv2 + MONAI 3D-SegM)},
  year         = {2026},
  url          = {https://github.com/Calix-L/Vesuvius},
  version      = {1.1.0}
}

License

MIT License — 冲榜代码,仅供学习参考。比赛规则的所有内容归 Kaggle / Vesuvius Challenge 主办方所有。

About

Vesuvius Challenge - Surface Detection (Kaggle) · 🥈 Silver Medal · nnUNetv2 + MONAI 3D-SegM baselines

Topics

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages