ICME23 知识蒸馏复现 · SLDNet阶段性沉淀

ICME23 知识蒸馏复现 · 阶段性沉淀

项目:ICME 2023《Collaborative Spatial-Temporal Distillation for Efficient Video Deraining》

数据:NTURain(合成测试集 8 序列 + 真实雨 7 序列)

阶段:链路打通并验证,训练按成本考量主动中止于 epoch 1

日期:2026-10-12 ~ 2026-10-13

环境:AutoDL RTX 3090(14 vCPU / 24 GB 显存)+ Windows 本地


一、一句话结论

这份官方代码不是"下载权重即可推理"的成品 :SLDNet 去雨权重从未公开,必须自训;但训练是自监督的、只需单序列 ,链路已在 3090 上验证跑通(1 epoch / 21 分钟),完整复现成本约 35 小时 / ¥45--70,本轮按性价比主动停止。


二、两条关键事实纠正

2.1 SLDNet 去雨权重不存在(推翻早先判断)

原判断 实际情况 证据
VDMamba README 的 Google Drive 1D3OpTigv... 是 SLDNet 预训练权重 是可视化对比结果图,不是模型 该链接位于 VDMamba README 的 # Visual Results 表格中,与 MSCSC / S2VD / MFGAN / MPEVNet 并列;同页第 22--24 行才写明"预训练权重在 checkpoints 文件夹"(指 VDMamba 自身)
SLDNet 官方仓库提供去雨权重 只提供 FlowNet2 权重 + 数据,去雨网络要自己训 官方 README 的 Installation 仅列 FlowNet2 与 NTURain,紧接着即 Train: sh train_video_rain.sh;全仓库无 checkpoints/、无模型发布

补充:ICME23 论文说明.md 提到的权重路径 /mnt/hdd/iku/SLDNet_code 是作者内网机器,非公开分发渠道。

结论:所谓"教师模型"必须自行训练得到。

2.2 训练不需要 GT(自监督)

SLDNet 全称 Self-Learned Deraining Network(CVPR 2020)------训练过程完全不需要干净图像:

  • 官方训练列表 lists/video_rain_removal_train.txt 仅一行 b1_Rain(8 字节)→ 单序列训练,这正是官方只要求下载 b1_Rain 的原因;
  • 数据加载只读 data_NTU/train/b1_Rain/*.jpg;
  • 损失全部为 warp 一致性与掩膜约束:overall_loss = loss_fn(frame_pred, frame_target),其中 frame_target 就是当前带雨帧本身。

三、为什么必须先训练才能推理

3.1 客观限制

"推理"需要被去雨网络的权重,而该权重不存在(见 2.1)→ 物理上不存在"直接推理"这条路径。

3.2 为什么先跑 1 个 epoch 探针

在投入 35 小时前,用 21 分钟验证四件"二值"事项(不跑无法知道):

验证项 结果
三个 CUDA 扩展的纯 PyTorch 替换数值是否正确 ✅ FlowNet2 端到端前向通过,162.52 M 参数与论文一致
FlowNet2 权重能否加载 ✅ missing keys = 0,unexpected keys = 0
数据管线是否通 ✅ 256 帧正确接线(并查出训练集被截断到 20 帧的隐患)
存盘 / 续训机制是否工作 ✅ model_epoch_1.pth(666 MB)+ opts.pth 自动落盘

探针的实际价值:避免盲目投入 35 小时。事后看它确实拦下了"训练集只有 20 帧"这一静默错误(若未发现,等于用 8% 的数据训完全程)。

流程教训:给分阶段方案时应提前量化每一步的时间成本(例:"1 epoch = 21 分钟"),让对方尽早介入决策。


四、技术核心:三个 CUDA 扩展的纯 PyTorch 替换

4.1 为什么必须替换

三个自定义算子(channelnorm / correlation / resample2d)在现代 PyTorch 上编译必然失败,三重叠加:

硬伤 后果
build.py 用 torch.utils.ffi.create_extension 该 API 在 PyTorch 1.0 即被移除
make.sh 写死 -arch=sm_35 CUDA 11 起已移除该架构,nvcc 报 Unsupported gpu architecture
.c 绑定层 #include <THC/THC.h> THC 头文件在 PyTorch 1.11 被移除

→ 不要走 nvcc 编译路线(会耗费整天且失败)。

4.2 替换实现(逐行对照 .cu 内核语义)

算子 原始内核语义 纯 PyTorch 等价
ChannelNorm output[b,0,y,x] = sqrt(Σ_c x²)。注意:内核忽略 norm_deg 参数,恒为 2 次范数 sqrt((x*x).sum(dim=1, keepdim=True))
Resample2d xf = x + flow[0],yf = y + flow[1],双线性插值 + 下标 clamp 到 [0, W-1] grid_sample(mode='bilinear', align_corners=True, padding_mode='border'),其中 g = 2*pixel/(size-1) - 1
Correlation 零填充 pad_size 后,out[b,tc,oy,ox] = (1/(k²·C))·Σ_c P1[..., oy·s1+md+kr, ox·s1+md+kr]·P2[...+tj·s2, ...+ti·s2];drad=md//s2,dsize=2·drad+1,out=ceil((H+2·pad−2·(kr+md))/s1);corr_type_multiply 传入内核但从未使用 双层循环切片 + (p1*sub).sum(dim=1),无需 unfold

FlowNetC 实际配置 pad=20, k=1, md=20, s1=1, s2=2 → 输出 441 通道,空间尺寸与输入相同。

4.3 验证方式(两层)

  1. 本地数值对拍 :managed venv 装 CPU 版 torch,用 numpy 朴素实现复刻内核,5 项对拍(含 FlowNetC 真实配置)maxdiff ≤ 4.4e-16(机器精度);
  2. AutoDL 端到端:FlowNet2 构建 162.52 M 参数(与论文一致)、加载权重零缺失零多余、前向 64×64 输出 shape 正确。

这三个算子无任何可学习参数,替换后 state_dict 完全兼容,不影响权重加载。


五、工程坑位清单(全部已解决)

# 坑 现象 处理
1 networks/__init__.py 第 2 行 from .vgg import * 仓库无 vgg.py → ModuleNotFoundError 注释该行
2 train_video_rain.py 第 30 行硬编码 os.environ["CUDA_VISIBLE_DEVICES"] = "1" 单卡实例报 device ordinal 错误 注释,改用 export CUDA_VISIBLE_DEVICES=0
3 第 195 / 202--204 行初始化 VGG networks.Vgg16 在缺失的 vgg.py 里;opt.vgg_path 是作者本机路径 注释这 4 行------经核实训练循环内从不调用 VGG,无需下载 vgg16 权重
4 option.py 覆盖命令行参数 第 101--110 行 opts.data_dir = opt.data_dir 等硬编码,命令行传 -data_dir / -model_name / -checkpoint_dir / -crop_size 一律无效 不传这些参数,直接改 option.py(其默认值恰与测试脚本路径对齐)
5 训练脚本硬编码 data_dir = 'data_NTU/',加载 data_NTU/train/<video> SPAC 数据是平铺的 Dataset_Testing_Synthetic/{a1_Rain, a1_GT, ...} 重组为 data_NTU/train/ 、data_NTU/test/、data_NTU/GT/ 三处软链
6 旧残留目录 data_NTU/train/b1_Rain(仅 20 帧) 接线逻辑"目录非空即保留"→ 训练只用 8% 数据 改为帧数与源不一致即 relink ;prep_step6b_fix_split.sh 专门修此问题
7 utils.py 第 15 行反向 import networks utils ↔ networks 循环导入 导入顺序须为先 utils 后 networks(冒烟脚本已对齐官方顺序)
8 训练脚本 import tensorboardX 未安装 pip install tensorboardX
9 裸装 torchvision 会连带升降 torch,破坏 1.10.0+cu113 必须 --no-deps 且按 torch 版本钉(1.10.0 → 0.11.1);numpy 需 <2
10 服务器装 opencv-python libGL.so.1 报错 用 opencv-python-headless

两个"省事"的发现(消除冗余工作):

  • option.py 默认 checkpoint_dir='./checkpoints/P401_video_rain_self/' + model_name='derain_self',与 test_video_rain.sh 要求天然一致;
  • utils.save_model 会自动写出 opts.pth(第 234--237 行),正是测试脚本第 62--65 行要读的文件 → 无需手工造占位。

六、实测性能与成本

指标 实测值
单 epoch(500 iter,batch 8,crop 64) 21 分钟
单 iter 墙钟 约 2.5 s(数据加载与 GPU 计算并行,日志分别计时各约 2.5 s 属正常)
GPU 利用率 / 显存 100 % / 8.7 GB(24 GB 显存仅用三分之一)
CPU 14 核,threads=8
100 epoch 推算 约 35 小时 ≈ ¥45--70
30 epoch 推算 约 10.5 小时 ≈ ¥14--21

诊断修正记录 :曾判断"数据加载占一半、是瓶颈、调 threads 可省三成"------错误 。GPU 100% 说明算力已吃满,日志中 2.5 s + 2.5 s 是并行发生的两条腿,墙钟由较慢者决定。当前配置已接近该卡吞吐上限,调参空间有限。

可选的加速方向 (未采用):batch 8→16(显存有富余);crop 64→128(但论文原始配置就是 64,改动会偏离复现口径)。建议不改为宜------这是复现任务,配置应与论文一致才有对标意义。


七、资产清单

AutoDL 实例(/root/autodl-tmp/)

路径 内容
CVPR-2020-Self-Rain-Removal/SLDNet_code/ 已 clone + 打完补丁 + 三个扩展已替换的代码
├ pretrained_models/FlowNet2_checkpoint.pth.tar 软链指向根目录的 620 MB 权重
├ checkpoints/P401_video_rain_self/derain_self/model_epoch_1.pth 666 MB,已验证的训练产物(续训起点)
├ checkpoints/.../opts.pth 959 字节,测试脚本必读
├ data_NTU/{train,test,GT}/ 三处软链,各 256 帧
└ Dataset_Testing_Synthetic/、Dataset_Testing_RealRain/ NTURain 解压后的原始数据
prep_step2 ~ step7 系列脚本 见下

本地(H:\zj\SLDNet Knowledge-Distillation\)

  • 2023 SLDNet会议论文 Collaborative_Spatial-Temporal_Distillation_for_Efficient_Video_Deraining.pdf
  • Distilling the Knowledge in a Neural Network.pdf(Hinton 2015 经典蒸馏原论文)
  • FlowNet2_checkpoint.pth.tar(650,102,505 字节 / 620 MB)
  • Knowledge-Distillation-main.zip(ICME23 代码)
  • NTURain Partly/(SPAC 版,含 rar + spacCNN,663 MB)
  • synthetic_NTURain/ + synthetic_NTURain.zip(STVDNet 版,248 MB,额外含真实雨 7 序列)
  • extracted/(ICME23 代码解压结果)

脚本(H:\zj\SLDNet Knowledge-Distillation\ 与工作区)

脚本 作用
prep_step2.sh clone 仓库(含 GitHub 镜像兜底)+ 修 vgg bug
prep_step3_data.sh 数据定位 / 帧数校验 / 软链 / 生成列表
prep_step4_fix_ext.sh 三个 CUDA 扩展替换为纯 PyTorch + 冒烟测试
prep_step5_deps.sh 只装缺失依赖(保护现有 torch)+ 重跑冒烟
prep_step6_train_sldnet.sh 训练(probe 测速 / full 全量),自动打三处补丁
prep_step6b_fix_split.sh 修训练集截断 + cv2 解码自检 + 跑探针
prep_step7_eval.sh 推理 8 序列 + PSNR/SSIM 评分(含带雨基线对照)

八、数据说明

NTURain 来源与认证

三种获取渠道(按推荐顺序):

  1. SPAC 官方 :github.com/hotndy/SPAC-SupplementaryMaterials ------ 用 git clone --depth 1 --filter=blob:none --sparse 稀疏检出 Dataset_Testing_Synthetic,再 unrar 解压(Linux 上用 7z 解这套 rar 会误报 "Sub items Errors",须用真 unrar);
  2. RainMamba 打包版 :Google Drive 1Yt_0k_OVrBkC1n_F8SeOz_9V160o9UOc,同表有 OneDrive 版;
  3. STVDNet 百度盘 :https://pan.baidu.com/s/1ltVpPKvdzeh_h-wbubG29A,码 sg4n(国内直连,额外含真实雨测试集)。

认证结论(已闭环验证)

本机 synthetic_NTURain.zip(275,859,537 字节)经三重验证 = 正宗官方测试集:

  1. 大小与本地同名副本一致,且非整数 MiB(无截断特征);
  2. 八序列帧数 168 / 116 / 125 / 298 / 256 / 250 / 219 / 250,与 SPAC 官方逐序列一致,GT 与 Rain 数量相等;
  3. 与 SPAC rar 解出的副本 diff -rq 全量逐字节比对:16 个序列目录所有 .jpg 完全一致 (唯一差异是 SPAC 版多出 Windows Thumbs.db)。

补充:spacCNN 每序列少 4 帧是该基线方法的固有特征,非数据损坏。

目录结构约定

用途 路径 规模
训练(单序列) data_NTU/train/b1_Rain 256 帧
测试(8 序列) data_NTU/test/<seq>_Rain 1634 帧
真值 data_NTU/GT/<seq>_GT 1634 帧
真实雨(备查) Dataset_Testing_RealRain/ ra1--ra4 / rb1--rb3 共 7 序列

训练集(Dataset_Training_Synthetic,t1--t8)尚未下载------L2 蒸馏需要,SLDNet 自监督训练不需要。


九、结果判据与后续路线

9.1 L1 复现的判据

  • 训练日志:loss 持续下降、无 NaN、Batch n/500 能推进到 500/500;
  • 训练后:b1 的 PSNR 从带雨基线(本机实测 27.45 dB / SSIM 0.8995 )提升到 34 dB 上下;
  • 论文参考值(CVPR 2020 SLDNet,NTURain 8 序列平均):PSNR 34.89 / SSIM 0.954。

9.2 若继续训练

bash 复制代码
cd /root/autodl-tmp/CVPR-2020-Self-Rain-Removal/SLDNet_code
sed -i -E "s|^opt\.epoch_max[[:space:]]*=.*|opt.epoch_max = 30|" option.py
nohup python3 -u train_video_rain.py -crop_size 64 > /root/autodl-tmp/train_sldnet_30ep.log 2>&1 &

会自动从 epoch 1 续训(打印 ===> Resuming model from epoch 1)。

训完执行 bash /root/autodl-tmp/prep_step7_eval.sh(自动选最新 epoch 权重)。

9.3 L2 蒸馏(真正的蒸馏训练)

仍需补齐三项,当前不必投入:

  1. 训练集 Dataset_Training_Synthetic(git sparse-checkout add 即可);
  2. 自写 ConvLSTM_sin(约 20 行,照 ConvLSTM 加时间正弦编码);
  3. 教师改造版权重(ICME23 用的 64 通道 / 7 帧版本)。

9.4 概念澄清:L1 ≠ L2

  • L1(本次):教师纯推理 / 自训 ------ 无反向传播、无参数更新、无损失函数、用不到训练集;
  • L2:真正的蒸馏训练 ------ 学生先在 GT 上独立训到收敛,再用教师特征微调;监督信号是特征图对齐(3 个 ConvLSTM 桥),而非分类蒸馏的软标签 + 温度。

十、可复用方法论

  1. 老代码 + 新 PyTorch = 先查扩展编译可行性 。凡是 torch.utils.ffi / THC/THC.h / -arch=sm_35 三者任一出现,直接放弃编译路线,改纯 PyTorch 重写算子------扒 .cu 源码逐行对拍(numpy 朴素实现做基准),比调试 nvcc 报错快一个数量级。
  2. 下载大文件先看大小是否整数 MiB 。恰为整数 MiB(如 340,787,200 = 325 MiB)是传输截断的典型特征;单个大 tar.gz 走网页下载极易中断,应改传原始小文件或分卷(split -b 200M),或走 scp。
  3. 在投入长时任务前先跑最小探针 ,但必须提前量化并告知其时间成本,让决策者早期介入。
  4. 权重文件用 torch.load 先看 keys 再决定。可避免下载数百 MB 冗余权重(如本次 FlowNet2 是否重复下载的判断),也能第一时间发现"链接其实是结果图而非权重"这类误判。
  5. 多行命令在网页终端会被截断 ------ 一律给单行 && 串联的纯 ASCII 命令,或直接给 .sh 脚本让其在 JupyterLab 上传后 bash xxx.sh。

附:常用命令速查

bash 复制代码
# 训练(续训,30 epoch)
cd /root/autodl-tmp/CVPR-2020-Self-Rain-Removal/SLDNet_code
sed -i -E "s|^opt\.epoch_max[[:space:]]*=.*|opt.epoch_max = 30|" option.py
nohup python3 -u train_video_rain.py -crop_size 64 > /root/autodl-tmp/train_sldnet_30ep.log 2>&1 &

# 看进度
tail -n 20 /root/autodl-tmp/train_sldnet_30ep.log
grep -E "Rect Loss" /root/autodl-tmp/train_sldnet_30ep.log | tail -n 30

# 查看已存权重
ls -lh checkpoints/P401_video_rain_self/derain_self/

# 推理 + 对分(8 序列)
bash /root/autodl-tmp/prep_step7_eval.sh
相关推荐
陈嘿萌3 小时前
学术速递|2026年8月 arXiv 图像融合论文汇总(08.01–08.31)29 篇最新成果
图像处理·人工智能·深度学习·计算机视觉·图像融合·学术研究与理论基础
硅谷秋水3 小时前
EmbodiRSI:面向数据高效机器人适配的递归自我改进
人工智能·深度学习·语言模型·机器人
跟我学机器学习4 小时前
LangGraph教程:基于 LangGraph 构建自定义 RAG Agent
人工智能·深度学习·语言模型
Capricorn19884 小时前
知芽 Notebook Skill:用单元记忆校验 AI 摘要引用的真实性
人工智能·深度学习·架构·知识图谱
Yyyyyy~5 小时前
【深度学习】Day1
人工智能·深度学习
硅谷秋水5 小时前
MobiAgent:面向长时程移动操作任务的双-环递归策略自我提升方法
人工智能·深度学习·机器学习·语言模型·机器人
硅谷秋水5 小时前
RegenHarness:一种具备“证据门控”递归自我改进能力的机器人智能体框架
人工智能·深度学习·机器学习·语言模型·机器人
Uncertainty!!6 小时前
大模型基础架构之Transformer
人工智能·深度学习·transformer
通信大模型7 小时前
IEEE JSTSP| FlashSAM:低空无线网络中多无人机Token通信的轻量级视觉模型
人工智能·深度学习·无人机