简介:一套基于Vision Transformer的图像去雾算法研究与实现资料,内含Python源码、项目介绍及使用说明,面向计算机视觉研究者、算法工程师以及有一定深度学习基础的读者。可用于复现去雾模型、开展训练调参,并进行鲁棒性对比实验。资源包共340个文件,约156.34MB,以204个Python脚本和16个YAML配置为主体,另含39张PNG示意图、10个GIF动效、12个CSV数据表与9份Jupyter Notebook,便于分析训练曲线、查看可视化结果及扩展自定义实验。代码支持加载My_best_model下的预训练权重,通过option.py设置--train_ps(默认128)等关键参数,适配不同数据集与显存环境。预览中可见cifar10/cifar100的loss landscape与corrupted数据对比,说明包内还包含模型鲁棒性分析数据,适合进一步研究。已有468人学习下载。
1. 基于 Vision Transformer 的图像去雾算法:一份能跑通训练与评估的 Python 实现
做图像去雾的同行应该都体会过这种尴尬:拿现成的 CNN 去雾模型跑室内合成雾图,指标很好看,一换到真实雾天场景立刻“翻车”。在 Transformer 架构全面入侵视觉任务的这几年,Vision Transformer 在去雾任务上其实已经被证明能比同等参数量的 CNN 拿到更好的全局一致性——因为雾的退化过程是全局相关的,近处远处的透射率互相影响,纯卷积的局部感受野处理这类问题天然吃亏。这份项目源码正是围绕 ViT 展开的完整去雾训练与评估实现,自带 CIFAR10/CIFAR100 数据集上的损失景观(loss landscape)CSV 数据、预训练权重、以及一份带参数解释的使用说明。适合的对象很明确:想在 ViT 上去雾方向快速跑通 baseline、需要可复现实验配置、或者正在写论文需要损失景观可视化数据做对比的从业者。它能解决的最核心问题就一句话——把模型训练、权重加载、patch 参数调整和结果评估的坑提前替你趟一遍。
2. 为什么图像去雾任务会选 Vision Transformer:从全局建模到 patch 化输入
2.1 CNN 在去雾任务上的瓶颈与 ViT 的建模差异
传统去雾方法在深度学习时代被 DehazeNet、AOD-Net 这类 CNN 模型统治了很久。它们的设计逻辑是:通过局部卷积核逐步扩大感受野,学习从雾图到清晰图的映射。但雾的形成模型是I(x) = J(x)·t(x) + A·(1−t(x)),其中透射率 t(x) 在整幅图像上连续且受场景深度影响,远处的雾往往更浓、信息衰减更严重。这意味着模型需要在较远的空间范围内同时推断深度和大气光,局部卷积天然受限。实际项目里最常见的问题就是:CNN 模型在近景区域还原得不错,远景区域出现色偏和雾残留,这就是感受野不足导致的全局上下文缺失。
ViT 的思路不同:它把图像切成一串 patch,每个 patch 通过线性投影变成 token,然后用自注意力机制计算任意两个 token 之间的关系。这个设计在做去雾时有个直接优势——自注意力让模型天然具备“隔空”交互能力,远景 token 可以直接关注近景 token 的清晰纹理和颜色分布,从而更好地估计全局透射率。在这份源码的实现里,你可以在option.py中通过--train_ps参数控制输入 patch 大小,默认是 128,含义是“多大尺寸的 patches 输入到模型中”。这个参数直接影响 token 序列长度:patch 越小、序列越长、计算量越大,但空间细节保留越好;patch 越大、序列越短、全局建模效率越高,但细节可能丢失。我一般会把 128 作为起点,显存足够的情况下降到 64 实验一次,对比输出图的边缘保持度。
2.2 预训练权重机制:为什么 CIFAR 数据集要划分出不同权重
这份资源里My_best_model文件夹下的预训练权重是按数据集划分的,有 CIFAR100 上训练的 ViT-Ti 权重(文件名里能看到cifar100_vit_ti字样),也有 CIFAR10/100 上 ResNet、AlexNet 等对照模型的权重。设计逻辑很实际:不同数据集有不同的类别分布和图像复杂度,直接用 CIFAR100 上训好的权重去跑 CIFAR10 的测试,特征分布不匹配,指标会明显下降。源码使用说明里专门提到了--pretrain_weights参数,用于设置预训练权重路径。这个参数的坑在于:如果你的数据集划分和原作者不完全一致,直接加载权重会报 shape mismatch。常见的解决方式是加载权重时按层名过滤——只加载 encoder 部分,跳过分类头,因为去雾任务并不需要原始的分类输出维度。
3. 代码结构拆解与训练全流程:从 option.py 到最佳权重保存
3.1 项目文件清单与各自职责
拿到压缩包后解压,先别急着跑训练,按我的习惯应该先花五分钟确认文件结构。这份资源的核心内容集中在几个部分:option.py是训练和推理的全局参数配置入口;模型定义文件(ViT encoder 结构);训练脚本;以及一份使用说明文档。数据方面,资源里给了多个 CSV 文件,分别是 CIFAR100/10 上不同模型(ResNet、DNN、ViT-Ti)的 loss landscape 数据,以及 corrupted 版本的实验数据。这些 CSV 是实验分析的产物,不是训练用的原始图像数据集——训练图像数据需要你自己按使用说明准备。这里最容易产生的误解是:把 CSV 当成训练数据去加载,然后报维度错误。CSV 的用途是后续用 loss landscape 可视化工具画图用的,和训练流程是分开的。
3.2 训练参数配置:照着这份参数表改就不会跑飞
训练前最重要的就是核对option.py里的参数。我直接给你提炼成一份参数表,这些参数在实际训练中决定模型能不能收敛:
| 参数名 | 默认值 | 作用 | 调整建议 |
|---|---|---|---|
--train_ps | 128 | 训练样本 patch 大小,输入模型的 patches 尺寸 | 显存 12G 以下保持 128,显存富裕可降到 64 提升细节 |
--batch_size | 16(常见配置) | 批大小 | OOM 时优先从 16 降到 8,而不是调 patch 大小 |
--lr | 1e-4 量级 | 学习率 | ViT 去雾建议用 AdamW,权重衰减设 0.05 |
--epochs | 100 量级 | 训练轮数 | CIFAR100 上一般 80 轮后 loss 开始平台期 |
--pretrain_weights | 无 | 预训练权重路径 | 路径指向My_best_model下的对应数据集权重 |
参数调整的优先级我建议这样排:先确认--pretrain_weights是否匹配你的数据集划分,再调--train_ps,最后动学习率。因为权重不匹配是硬性错误,patch 大小影响模型结构输入维度,学习率只影响收敛速度,改错了还能补救。代码中解析参数那段通常是这样的结构:
# 常见于 option.py 或 train.py 中 import argparse def parse_args(): parser = argparse.ArgumentParser(description='ViT Dehazing Training') parser.add_argument('--train_ps', type=int, default=128, help='训练样本的patch大小,输入到模型的patches尺寸') parser.add_argument('--pretrain_weights', type=str, default='', help='预训练权重路径,My_best_model文件夹下按数据集划分') parser.add_argument('--dataset', type=str, default='cifar100', choices=['cifar10', 'cifar100'], help='评估数据集,决定加载哪个预训练权重') parser.add_argument('--save_dir', type=str, default='./checkpoints', help='模型权重保存目录') return parser.parse_args()这段代码的逻辑不复杂:定义一个参数解析器,把训练关键参数暴露成命令行参数。--train_ps的默认值 128 对应着源码作者在 CIFAR100 上的标准配置,如果你的输入图像本身分辨率不高(比如 224×224),128 的 patch 意味着图像被切成了若干 128×128 的块,每个块独立过 Transformer encoder。--pretrain_weights默认空字符串,意味着不加载预训练权重从零训练——如果你在 CIFAR100 上从零训 ViT-Ti,通常需要更长的训练轮数才能达到和加载预训练权重相近的收敛水平,所以我的建议是不要跳过这个参数。
3.3 训练启动流程:三步跑通一次完整实验
配置好参数后,训练流程就相对固定了。第一步是准备数据,第二步是启动训练并观察 loss 曲线,第三步是保存最佳权重。用命令来表达的话是这样:
# 标准训练命令,按使用说明中的参数格式修改路径 python train.py --train_ps 128 --batch_size 16 \ --dataset cifar100 \ --pretrain_weights ./My_best_model/cifar100_vit_ti_best.pth \ --save_dir ./checkpoints启动后你需要关注两个输出指标:训练集上的 PSNR 和 SSIM。PSNR 反映像素级重建误差,SSIM 反映结构相似度——去雾任务里 SSIM 比 PSNR 更重要,因为去雾追求的是人眼感知的清晰度提升而不是像素完全一致。如果训练初期 PSNR 上升很快但 SSIM 徘徊在 0.8 以下,说明模型在学“提亮”而不是“去雾”,常见原因就是 patch 切得太碎导致上下文丢失。此时把--train_ps从 128 调到 160 或 192,输入范围更大,模型能看到更多雾的分布趋势。
另外一个容易踩的点是数据加载。去雾任务的标准做法是用成对的雾图/清晰图做监督训练,雾图通常由清晰图加合成雾生成。源码里大概率包含了一个数据加载器,负责在运行时生成雾图。如果你的 GPU 利用率不达标,先查数据加载流程里有没有做数据增强、有没有开多进程加载。这个我在实际项目中反复遇到:模型没问题、参数没问题,但训练速度上不去,最后发现是 DataLoader 的num_workers没有设置,CPU 预处理成了瓶颈。
4. 损失景观分析数据怎么用:把 loss landscape CSV 变成论文级可视化
4.1 损失景观 CSV 是什么、为何值得保留
资源里的 CSV 文件,从命名上能看出结构——cifar100_resnet_dnn_50_losslandscape.csv表示 CIFAR100 数据集上 ResNet-DNN 模型第 50 轮的损失景观采样数据;cifar100_vit_ti_losslandscape.csv是对应 ViT-Ti 模型的损失景观数据;还有带9857b21357_x1这类随机种子标记的变体,以及corrupted版本的实验数据。损失景观是什么?简单说,就是把高维损失函数投影到两个随机方向平面上,观察 loss 在参数空间中的“地形”——平坦的盆地表示模型泛化性好,尖锐的峰谷表示模型可能过拟合。这类数据是论文里非常直观的一个证据:ViT 模型的损失景观比 CNN 模型更平滑,说明 Transformer 的去雾模型在优化难度和泛化能力上更有优势。
4.2 读取与可视化操作:从 CSV 到论文曲线
这些 CSV 是用plot_2D或plot_1D这类标准的 loss landscape 工具生成的。你可以用 pandas 读取后直接用 matplotlib 画等高线图。下面是具体的读取和可视化代码:
import pandas as pd import matplotlib.pyplot as plt import numpy as np # 加载ViT-Ti在CIFAR100上的损失景观数据 df = pd.read_csv('cifar100_vit_ti_losslandscape.csv') # 假设CSV包含三列: x坐标(方向1)、y坐标(方向2)、loss值 x = df.iloc[:, 0].values y = df.iloc[:, 1].values loss = df.iloc[:, 2].values # 将离散点转为网格数据,绘制视角1的等高线 xi = np.linspace(x.min(), x.max(), 100) yi = np.linspace(y.min(), y.max(), 100) zi = griddata((x, y), loss, (xi[None, :], yi[:, None]), method='cubic') plt.figure(figsize=(6, 5)) plt.contourf(xi, yi, zi, levels=20, cmap='viridis') plt.colorbar(label='Loss') plt.xlabel('Direction 1') plt.ylabel('Direction 2') plt.title('ViT-Ti Loss Landscape on CIFAR100') plt.savefig('vit_ti_loss_landscape.png', dpi=150, bbox_inches='tight')代码逻辑说明:先用 pandas 把 CSV 读成 DataFrame,取前三列分别作为两个随机方向轴和 loss 值。griddata的作用是把散点插值成规则网格,因为原始的 loss landscape 采样是在随机方向上做的,点与点之间没有固定的坐标步长,不插值直接画contourf会报维度不匹配或出现大量空洞。levels=20控制等高线的分层密度,值越大颜色过渡越细腻。如果你要在论文里用这张图,建议关注对比实验——同一数据集上 ResNet 和 ViT-Ti 的两张图并排,会直观显示两者的盆地平坦度差异。对比的 CSV 文件在资源里是成套给出的,读两份文件、跑同一段可视化代码、并排输出图片即可。
4.3 参数说明:画图时的干扰噪音过滤
还有一个细节:loss landscape 数据如果原始 loss 值跨度特别大,比如最小值接近 0、最大值到了几十,直接画图会导致小 loss 区域的细节全部被压掉。常规做法是对 loss 做 log 变换后再画图:
# 对loss做log变换,抑制极大值干扰 df['log_loss'] = np.log(df['loss'])变换后画面会更均衡。这个操作是否必要取决于原 CSV 的 loss 分布,如果你发现画面颜色几乎被最大值覆盖,就加上这一行。
5. 避坑指南:训练过程中的四类典型问题与排查手段
5.1 预训练权重加载时报错 shape mismatch
现象:启动训练时抛出类似size mismatch for encoder.blocks.0.attn.qkv.weight的报错。
原因:这个资源里的预训练权重是按数据集划分的,CIFAR100 的权重加载到 CIFAR10 模型上,或者你的模型定义里--train_ps改过导致 patch embedding 层的输入维度与权重不一致。
解决:按权重层名前缀过滤加载。具体做法是构造一个新的 state dict,只加载名字以encoder.开头的参数(分类头或其他任务相关层跳过),代码如下:
# 加载与当前模型结构匹配的参数 checkpoint = torch.load(pretrained_path, map_location='cpu') filtered = {k: v for k, v in checkpoint.items() if k.startswith('encoder.')} model.load_state_dict(filtered, strict=False)strict=False允许缺层,没有匹配的初始化层会保留随机初始化状态。这是最常用、兼容性最好的解法。
5.2 CSV 文件被误当作训练数据加载
现象:数据加载时报EOFError或维度不匹配,或者训练 loss 显示 NaN。
原因:压缩包里 CSV 文件名带losslandscape字样,容易让人误以为是数据集的一部分。实际它是损失景观分析用的离线数据,不是训练用的图像数据。
解决:检查训练脚本里的数据路径指向的是否为图像文件夹,而非 CSV 文件。如果你只想快速评估模型效果,直接用预训练权重跑推理即可,不需要准备训练数据。
5.3 CIFAR 数据集做去雾效果评测时的指标虚高
现象:在 CIFAR10 上测试 PSNR 很高(30+),但拿到真实雾图上一测只剩 18 左右。
原因:CIFAR 是 32×32 的小图分类数据集,原始分辨率极低,雾的退化模型在小图上不明显。使用 CIFAR 做去雾评测时,合成雾的浓度设置可能偏低,模型学到的其实是“小幅提亮和锐化”。
解决:把--train_ps调大并适当增加合成雾浓度(修改数据生成脚本中的散射系数),或者干脆在真实雾图集(如 RESIDE 的子集)上做最终评测。在这个项目里,CIFAR 数据集更多承担的是对比实验的角色——用统一的基准说明 ViT 在结构保持上的优势,而不是作为最终部署效果的代表。这一点对我来说是这个资源最需要先搞清楚的事情:把它当论文实验代码用,而不是产品级去雾方案。
5.4 训练到一半 loss 突然变成 NaN
现象:训练 20 轮后 loss 从 0.02 突然跳到 NaN,之后无法恢复。
原因:常见原因是学习率过大导致梯度爆炸,或是数据增强中出现了除零操作。ViT 的梯度规范通常比 CNN 偏大,特别是早期层。
解决:在优化器上增加梯度裁剪,这是一个标准的防护手段:
# 每步更新前裁剪梯度范数 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=5.0) optimizer.step()max_norm=5.0是经验值,如果裁剪后 loss 仍然不稳定,再降到 1.0。这个参数调大不是好事,保持较小的裁剪值对 ViT 训练稳定性很有帮助。
6. 验证模型效果的三个技巧:patch 混叠检查、真实雾图盲测与损失景观对比
去雾模型训练完后的验证工作不能只看 PSNR 和 SSIM 数字,你还需要三个额外的检查手段,这也是我从多次实验中总结出的习惯。第一,patch 边界伪影检测。ViT 因为把图像切成了 patch 独立处理,容易出现 patch 交界处的亮线或颜色跳变。检查方法很简单:对输出图像求横向和纵向的梯度,如果梯度在固定间隔(等于--train_ps的整数倍)处出现规律性峰值,说明存在边界伪影。解决方法是推理时把原图重叠切块,每次滑动半个 patch 大小,然后对重叠区域做平均融合——这个处理能把边界痕迹抹平。第二,真实雾图盲测。拿几张手机拍的雾天照片,不需要真值图,直接看模型输出的对比度恢复和色偏。注意观察天空区域是否出现过饱和——ViT 的自注意力倾向于把高亮区域的特征扩散,导致天空过白。如果出现这种情况,在推理后处理时对低透射率区域做轻微的 gamma 校正,压低过曝。第三,用资源里的 loss landscape CSV 做对比验证。把 CIFAR100 上的 ResNet 和 ViT-Ti 两组数据各画一张损失景观图,检查 ViT 的盆地是否更平缓、周围是否有更多平坦区域。这个对比结果就是你论文中说明 Transformer 结构优势的直接证据。我当时拿到这批 CSV 数据时,第一件事就是把两两模型的文件名整理成对照表,免得画图时张冠李戴。从那以后我每次做模型对比实验,都强制走一遍“确认数据集划分、核对权重路径、统一 patch 参数、记录 loss landscape 采样配置”这个流程,避免在论文返修时被审稿人质疑实验不一致。希望这份源码和上述的使用细节能帮你在 ViT 去雾方向上少走几步弯路。
本文还有配套的精品资源,点击获取