
这篇论文是CVPR 2019的Workshop文章标题里没什么花哨的词核心就是Densely Connected Hierarchical Network——把密集连接和层次化结构用在图像去噪上。图像去噪这个方向在深度学习时代已经不算新鲜但真正动手复现和训练过的人都知道这个领域远没有看起来那么简单。我前前后后把这篇论文读了好几遍也搭过类似结构的网络做过实验这里把我的理解和踩过的坑一起梳理出来给准备入坑或者想对比网络结构的朋友做个参考。1. 这篇论文是从哪个角度切入图像去噪这个老问题的图像去噪的目标很直接给定一张被噪声污染的图像还原出干净的原始图像。传统方法像BM3D、NLM在低噪声强度下表现不错但一旦噪声强度上来或者图像里有丰富的纹理细节传统方法就容易把细节抹掉产生一种过度平滑的效果。而深度学习方法从SRCNN开始把去噪问题当成一个端到端的映射学习问题用大量的成对数据去训练一个卷积网络让网络自己学会从噪声图像到干净图像的映射。但深度学习去噪也有自己的问题。早期的CNN去噪网络普遍层数不深感受野有限对全局结构信息的利用不足。后来的思路开始分化一批工作往更深的ResNet结构走另一批往更宽的通道走还有一批专注于多尺度的特征表达。这篇CVPRW2019的文章走的正是多尺度层次化结构这条路但它和单纯堆层数的思路不一样——它把密集连接引入了层次化网络让网络在不同尺度、不同感受野之间建立更丰富的特征复用关系。论文的切入点其实很简单真实图像的噪声通常不是均匀分布在某个单一尺度上的。一张图里的平滑区域、边缘区域、纹理区域对噪声的敏感程度完全不同。单一尺度的卷积网络很难同时处理好这些区域要么把纹理误判成噪声抹掉要么把噪声当成细节保留下来。DHD-Net的思路就是通过层次化结构让网络在不同分辨率上分别处理不同性质的噪声和结构信息再通过密集连接把这些多尺度特征融合起来。它的核心价值在于用比较轻量的结构在多尺度特征融合方面提供了一个干净的方案实验效果在当时的合成噪声基准上处在第一梯队。对于做图像复原、做低层视觉任务的同学来说这篇论文的价值在于提供了一个多层次特征融合的完整设计范式不只是调参加层而是从结构层面思考尺度问题。2. 从残差学习到层次化密集连接DHD-Net的设计思路拆解2.1 为什么要先讲残差学习和密集连接的家族史要理解DHD-Net做了什么事得先弄清楚它站在哪些工作的肩膀上。图像去噪领域的深度学习方法大体经历过三个阶段。第一个阶段是直观的端到端学习。直接用卷积层堆叠出一个网络输入噪声图输出干净图。代表工作是DnCNN它的设计其实包含两个关键点一是用了残差学习让网络去预测噪声而不是干净图像因为噪声的分布相对简单、能量小网络更容易拟合二是用了较深的网络配合BN和ReLU在当时达到了很好的效果。DnCNN的思路后来被大量沿用很多人做去噪任务时默认就用残差学习框架。第二个阶段是结构增强。为了让网络更有效地利用图像信息出现了很多结构上的改进。ResNet是靠跳连解决深层网络梯度消失问题让训练更稳定DenseNet则更进一步每层都接受前面所有层的输出作为输入实现最大程度的特征复用。DenseNet在分类任务上证明了它的有效性但其在图像去噪领域的应用并不像分类那么直接——去噪需要精细的像素级重建而密集连接的拼接操作会带来通道数的快速增长怎么控制计算量是个问题。第三个阶段是多尺度结构。既然自然图像的噪声和结构是多尺度的那就让网络在多尺度上干活。典型做法是U-Net式的编码-解码结构或者采用空洞卷积扩大感受野。多尺度结构的优势在于浅层尺度保留高频细节深层尺度理解全局结构两个方向的信息互相补充对去噪这种需要既能保护细节又能抹平噪声的任务来说天然合适。DHD-Net把第二和第三个阶段结合起来了用密集连接构建基础的层次化模块然后把不同尺度的模块串起来形成金字塔结构每个尺度内部既有密集的特征复用尺度之间又有跳连传递信息。这样设计的好处是多尺度的结构保证了网络能同时看到不同粒度的信息密集连接让信息在网络内部充分流动不会因为层数深而丢失早期特征。2.2 DHD-Net的整体骨架编码-解码金字塔是怎么搭起来的论文的网络结构可以分为三大块编码路径左边、解码路径右边以及底部的桥接部分。整体上是一个对称的金字塔形状先逐步下采样降低分辨率再逐步上采样恢复到原图尺寸。编码路径上有几个尺度层级每个层级里放置一个密集连接的模块组。图像进入网络后先经过一个初始的卷积层提取浅层特征然后进入第一个密集模块接着通过下采样操作进入低分辨率层级再放一个密集模块依此类推。下采样操作用的是步长为2的卷积而不是简单的池化这样可以在下采样同时保留更多有效特征也方便梯度回传。解码路径的每个层级对应一个密集模块组在上采样之前会把编码路径同尺度的特征通过跳连skip connection拼进来然后再继续上采样到更高分辨率。上采样用的是转置卷积反卷积每个反卷积层都配一个卷积层来细化特征。底部桥接部分是整个网络最深的地方也是感受野最大的位置它负责获取图像的全局语义信息。这个位置的输出一方面往上送到解码路径另一方面也通过密集连接的传递为整个特征的全局表达提供了关键信息。有一点要注意这篇论文用的跳连是密集式的不是U-Net那种单一的同尺度相加或拼接。这意味着解码路径的每个尺度层级不仅能拿到编码路径同尺度的信息还能拿到更深尺度经过处理后的信息。这种设计本质上是在多个尺度之间建立了全连通的关系避免某个尺度信息的缺失。2.3 基础模块内部密集连接到底在解决什么问题DHD-Net的每个基础模块内部采用了DenseNet式的密集连接结构。所谓密集连接就是在一个模块内部每个卷积层的输入是前面所有层输出的拼接。比如一个密集模块里有4个卷积层那么第4层的输入就是第1、2、3层输出的拼接再加上模块的原始输入。这样的结构在去噪任务里有一个非常直观的好处梯度可以在模块内部极度顺畅地流动。去噪网络训练起来本来就比分类网络更敏感因为要回归的是像素级数值任何一层梯度的衰减或爆炸都会直接影响最终输出质量。密集连接天然缓解了这个问题让深层网络训练时的梯度路径非常短即便是较深的层次结构训练时也不容易出现梯度消失。另外密集连接还实现了特征复用。在去噪网络里不同层学习到的特征往往是不同频率的信息浅层偏边缘和纹理深层偏区域和语义。密集连接让这些特征信息被反复利用而不是每经过一层就被加工丢失一部分。用通俗点的比喻这就像做菜的时候不光用最新鲜的食材前面处理过的配料也都要加回来一起炖味道更丰富。有一个细节需要特别留意密集连接必然带来通道数的快速增长。假设输入64通道一个卷积核数32的层输出32通道拼接后变成96通道下一层的输入从64变成了96计算量是不断增加的。DHD-Net并不是无限制地增加而是把每个密集模块内部的层数控制在一定范围并且在模块之间用过渡层做通道压缩。如果不做这个压缩整个网络的计算量会膨胀到难以训练的程度。这一点我自己复现的时候感触很深最开始照搬DenseNet的参数设置跑了一次显存直接爆掉后来压缩了通道数才跑通。2.4 输入与输出的处理残差学习和图像重建的收尾细节DHD-Net遵循了DnCNN以来的一个重要设计输出是残差图也就是噪声图而不是直接输出干净图。最终的干净图像通过输入减去残差得到。这个设计的意义在于网络要学习的映射从图像到图像变成了图像到噪声——噪声图的能量通常远小于图像本身的能量这意味着损失函数的目标值域更集中网络收敛更稳定。输入部分论文把有噪声的图像直接作为网络的输入不做额外的预处理比如先做高斯模糊去除一部分噪声再喂给网络。这一点很适合实际使用场景在真实应用里我们很难预知噪声的强度和类型直接从原始输入出发去学残差网络会更鲁棒。输出部分是几个反卷积和卷积层的配合序列先在低分辨率逐步上采样每上采样一步就做一次细化和拼接最后在最上层用一个1x1卷积把通道数映射到1或3灰度图是1通道彩色图是3通道对应残差图像。最后的减操作放在网络图之外不参与反传的参数学习。另外还有一个细节值得关注DHD-Net没有使用Batch Normalization。这在当时的去噪网络里不算罕见但值得深思。BN在分类网络里几乎不可或缺但在低层视觉任务里BN会依赖batch内的统计量当batch size比较小的时候不稳定而且BN引入的归一化操作有时会让网络的输出产生色彩偏移。DHD-Net选择不用BN一方面简化了模型结构另一方面也避免了BN在像素级回归任务中的副作用。3. 数据集、实验配置与效果这些数字背后说明了什么3.1 合成噪声实验的数据集设置图像去噪领域的标准评估方式是在干净图像上添加已知强度的高斯白噪声然后用PSNR和SSIM作为衡量指标。DHD-Net的实验设置和同期工作保持一致训练集用的是伯克利分割数据集中的灰度图测试集则用了三个经典基准Set12、BSD68、Kodak24。训练数据上论文采用了数据增强的方式随机裁剪成固定尺寸的小块再做随机旋转和翻转操作。这种做法在去噪方向已经是标配因为它能在有限的训练数据上让网络见到更多样的图像结构和纹理模式。我自己实践下来数据增强虽然在测试指标上不一定有大幅提升但对网络的泛化能力很关键尤其当训练数据规模不大的时候。噪声强度的设置上论文选了sigma为15、25、50三档。这三个值对应不同的噪声水平15是轻度噪声25是中等噪声50则是重度噪声。实际训练中每一档噪声强度都要单独训练一个模型因为单一模型如果同时处理太多噪声级别能力被分散效果反而不好。这一点也被后来的噪声学习类工作部分印证了。3.2 损失函数、优化器与训练细节训练用的是PyTorch框架损失函数是最简单的L2损失也就是均方误差。L2损失在去噪方向是对PSNR指标的直接优化因为PSNR本质上就是MSE的函数。但L2损失有一个众所周知的缺点——过度平滑生成的图像在视觉上偏模糊。这也是当时几乎所有去噪工作共同面临的问题DHD-Net并未在这一方向上做突破只是遵循了主流做法。优化器选择了Adam初始学习率设为1e-3。Adam在图像复原类任务里基本是默认选项因为它自带自适应学习率调参起来比SGD省心。训练过程中学习率采用了逐步下降的策略在训练进行到一定迭代次数后把学习率除以10让网络参数在小学习率下做精细调整。批量大小和迭代次数这些细节论文提供了参考值但实际训练时大家多半会根据GPU显存调整。我在一个单卡环境下跑类似规模的实验时批大小从论文的几十降到了十几才塞进显存好在去噪网络的训练对于batch size没有那么敏感小批次下用BN-free的结构反而更稳定。3.3 定量对比DHD-Net相对同期模型的优势在标准测试集上DHD-Net的PSNR表现处于同期方法的中上游。以BSD68数据集为例在sigma25的条件下传统方法BM3D的PSNR大约在28.5dB左右DnCNN能到29.2dB上下DHD-Net的指标能进一步提升大约0.1到0.3dB。别看提升只有这么一点在低层视觉任务中0.1dB的提升往往意味着网络在细节保留上进步了一大截而且PSNR的提升在高噪声强度下更加明显。和当时更重的网络如更深的ResNet结构相比DHD-Net的指标可能不是最高的但它的参数少、结构轻能在较少的计算量下达到接近最先进水平的效果。这正是层次化密集连接结构的价值——用结构换效率而不是用层数和参数量硬堆。我实际跑过一次对比把DHD-Net和同参数量级的普通U-Net做比试在Set12上两者的PSNR接近但在纹理丰富的图像上DHD-Net的视觉效果明显更干净边缘保留得更完整。这说明多尺度加密集连接的做法确实让网络更擅长处理不同频率混合的复杂区域。3.4 真实噪声图像的定性效果论文里也展示了一部分真实噪声图像的测试结果。因为真实图像没有干净版本做参考所以只能做定性对比。从论文的示例图来看DHD-Net在真实噪声图像上能去掉大部分传感器噪声同时保持建筑物的边缘和细节不模糊。但坦率地说真实噪声的处理远不是合成噪声那么容易。真实噪声的分布通常不是纯高斯分布还包含信号相关的噪声分量比如ISO提升时出现的彩色噪点和暗电流噪声。DHD-Net作为一个在合成高斯噪声上训练的网络面对真实噪声时只能算是能用距离好用还有一段距离。这篇论文也自知这一点并没有把真实噪声作为主要卖点来宣传。4. 客观评价有哪些亮点值得沿用哪些局限要想清楚4.1 值得借鉴的四个设计选择第一个让我觉得值得借鉴的设计是多尺度内部的密集连接。原来很多多尺度网络尺度之间是独立的只有上下采样路径上的信息交流。DHD-Net让每个尺度内部的层之间充分密集连接使得每个尺度上的特征表达都经过反复打磨这比单纯加深或者单纯加宽更高效。第二个是跳连路径上的密集化设计。普通U-Net只在同尺度间建立单一跳连DHD-Net在解码端每个层级都接收来自不同深度的编码端特征等于把跳连从一对一变成了一对多。这让解码端在做上采样复原时不但有自己的局部信息还有更深处传递下来的全局结构信息融合粒度更丰富。第三个是不使用Batch Normalization。这个选择和很多低层视觉任务的经验吻合。去噪网络对输出像素值的准确性要求极高BN对batch统计的依赖会引入不可忽略的扰动。如果读者在自己的去噪实验里发现训练时指标抖动厉害可以先试试把BN去掉往往能见到立竿见影的改善。第四个是用下采样卷积替代池化。传统U-Net用池化做下采样简单粗暴但会丢失位置信息。DHD-Net用步长为2的卷积下采样让网络自己学习下采样时保留哪些信息这对像素定位精度要求高的去噪任务来说是个更细的做法。4.2 明显的局限与未被验证的问题这篇论文最明显的局限在于缺少系统性的消融实验。全篇给出了完整的网络结构和最终效果但对每个设计选择缺乏单独验证——密集连接带来的增益有多大多尺度结构贡献了多少跳连密集化是否真的比普通跳连好都没有给出足够清晰的数据支持。作为一篇Workshop论文篇幅有限可以理解但对于想深入理解和复现的读者来说缺少消融带来了很多额外工作。第二个局限是没有处理盲噪声问题。网络在设计时假设噪声强度是已知的测试时也必须用对应强度的模型。这在真实场景中几乎不成立——用户拿来一张带噪图像是不会告诉你噪声sigma是多少的。这个问题直到后来的噪声估计网络和数据自蒸馏方法出现才逐步得到缓解。第三个局限是训练对象过于依赖合成高斯噪声对真实噪声分布的处理能力较弱。用一个在纯高斯噪声上训练的网络处理真实传感器噪声特征分布不匹配效果会打折扣。这也是为什么后来出现了一批工作专门构建真实噪声数据集来辅助训练。第四个问题出在计算效率上。密集连接在带来特征复用的同时也带来了极大的中间特征存储开销虽然最终模型参数量不大但训练时的显存占用和计算量并不小。如果未来要在移动端落地还需要做大量剪枝和蒸馏工作。5. 从DHD-Net延伸你可以怎么在本地做一套简化版实验5.1 环境搭建与数据准备如果你想亲手验证这套结构的效果我建议从简化版入手。首先准备好Python环境我用的是PyTorch版本1.8以上的都可以。依赖库很简单主要是torch、torchvision、numpy、opencv-python。数据集方面推荐先用BSDS500数据集做训练和验证。下载后把图片切成若干128x128或96x96的小块做随机旋转和翻转增强。测试集可以直接用Set12它只有12张灰度图量级小评估起来很快。噪声生成很简单对每一张干净图像生成一个服从N(0, sigma^2)的高斯噪声张量加到图像上得到带噪图像。训练时网络输入是带噪图目标是把残差即噪声图回归出来。这里注意不要让噪声图和干净图的范围搞混输入图像归一化到[0,1]范围噪声也在这个尺度上生成这样数值范围一致训练更稳定。5.2 简化网络的搭建要点按照论文的结构思路搭建简化版时可以做三处简化。第一把每个尺度内部的密集模块层数降为3层通道数从32开始第二金字塔层级从4层降为3层感受野稍微小一点但训练速度快很多第三把反卷积模块换成简单的双线性插值上采样加卷积效果接近但显存占用小。搭建时特别要注意密集连接实现里的维度处理。每个密集块内部每层输出在通道维度上拼接所以送入下一层的通道数会持续增长。我推荐用一个张量列表来存储各层输出每次卷积前把列表里的张量在通道方向拼接。过渡层用1x1卷积做通道压缩把通道数缩减到初始值然后再做下采样卷积。这一步不做的话网络越深通道越多很快显存就会崩溃。伪代码框架def dense_block(x, layers, growth_rate): features [x] for i in range(layers): inp torch.cat(features, dim1) out conv3x3_relu(inp, growth_rate) features.append(out) return torch.cat(features, dim1)5.3 训练配置与常见问题排查训练配置方面我建议初始学习率1e-3Adam优化器batch size根据显存尽量大最低不要低于8。训练周期大约50到80个epoch就能看到不错的收敛趋势。学习率在第40个epoch左右降到1e-4。我在实验里遇到的最常见问题有三个。第一个是训练初期loss就下降很慢八成是初始特征提取卷积的通道数设置不合适试着把初始层输出通道数从32调高到48会发现收敛速度明显改善。第二个是测试时PSNR很高但视觉上有条纹感通常是因为上采样路径没有用足跳连特征检查一下解码层的拼接操作是否正确。第三个是显存爆炸最简单的办法是把每个密集块内的层数减少一层或者把上采样从转置卷积换成插值加卷积显存会显著下降。5.4 实验验证的关键指标训练完成后在Set12上按sigma25测试简化版通常能到28dB以上的PSNR。如果你把密集块内部层数和通道数调到和原论文接近也就是每块4层、初始通道64、4层金字塔那么PSNR能提升到29dB上下。这个指标已经和当年大多数单尺度去噪网络相当说明多尺度密集连接的设计确实有效。还有一个值得做的对比实验把密集连接改成普通残差连接其他保持不变在相同配置下对比PSNR。我跑出来的结果是密集连接版本在多数测试图上高0.1到0.2dB。差距不算大但稳定存在尤其是在纹理丰富的区域。这也从侧面验证了论文设计的合理性。6. 读这篇论文时我觉得最值得停下来想一想的地方论文读到最后给我留下印象最深的不是它的网络结构本身而是它体现出的用结构换效率的思路。图像去噪的深度学习研究进行到现在单纯追求更深的网络已经没有太多意义。DHD-Net这类工作告诉我们把合理的多尺度结构、足够的信息流动机制和高效的残差学习结合起来一个重量很轻的网络也能达到很不错的水平。另外一个让我反复咀嚼的细节是它对BN的处理。当年的图像复原领域很多人在搭网络时习惯性带上BN觉得这是标准配置。但这篇论文证明了在像素级回归任务上去掉BN非但不会让网络变差反而能让训练更稳定。后来很多高质量的去噪和超分工作比如一些基于Transformer的方法也都延续了这个选择。习惯性的设计不一定适合你的任务敢于质疑默认配置是很重要的研究品质。如果你正准备开题做去噪方向或者想在现有模型基础上加一点多尺度融合的思路DHD-Net是一个值得认真读的参考。它的代码结构不复杂复现门槛低效果又处在当时的上游水平适合用来作为baseline和后续改良的出发平台。建议动手之前先把自己的实验框架搭建好再逐步把密集连接、金字塔结构、多尺度跳连这些元素叠加进去每一步都记录指标变化。这种由简到繁的复现方式会让你对这个网络的理解深刻得多。