
088、单图像超分实战从SRCNN到SwinIR复现并对比主流算法性能去年有个项目让我头疼了整整两周——客户要求把监控视频里的车牌从32x32放大到128x128还要能识别出数字。我一开始天真地以为直接上ESPCN就能搞定结果跑出来的车牌糊成一团连人眼都看不清。后来翻出SRCNN的老代码试了试效果居然比某些号称“SOTA”的轻量模型还稳。这件事让我意识到超分领域理论再漂亮落地时还得看具体场景下的真实表现。今天这篇笔记我打算把从SRCNN到SwinIR这条技术路线上的几个关键模型用代码和实验数据串起来讲。不扯虚的直接上我调试过程中踩过的坑和总结的经验。从SRCNN说起别小看三卷积的“老古董”SRCNN是2014年的工作结构简单到令人发指只有三个卷积层。但别因为它老就轻视它我在实际部署中发现对于纹理简单的图像比如文档扫描件SRCNN的PSNR甚至能跟某些轻量Transformer模型打平。复现时有个细节容易翻车输入图像的预处理。SRCNN论文里用的是双三次插值上采样后的图像作为输入但很多人直接拿原始低分辨率图像喂进去结果训练永远不收敛。我踩过这个坑后来在代码里加了一行注释# 这里踩过坑必须先用bicubic把LR放大到HR尺寸再送入网络# 别直接拿原始小图训练否则梯度会炸lr_bicubicF.interpolate(lr,size(hr_height,hr_height),modebicubic,align_cornersFalse)训练时还有个trick学习率别设太大。SRCNN的参数量才8万左右用Adam优化器时初始学习率1e-4就够了调大了loss直接飞上天。我试过1e-3结果第一个epoch的loss就飙到1e6吓得我赶紧CtrlC。FSRCNN速度与精度的第一次妥协FSRCNN是SRCNN的改进版核心变化是把上采样层从输入端移到了输出端。这样网络可以直接处理小尺寸特征图计算量大幅下降。我在1080Ti上测试FSRCNN处理一张256x256的图像只需要2ms而SRCNN要8ms。但有个坑FSRCNN的deconv层转置卷积容易产生棋盘格伪影。我调试时发现如果deconv的kernel size和stride不是整数倍关系输出图像就会出现规律性的条纹。解决办法是改用pixel shuffle亚像素卷积或者把deconv换成最近邻插值卷积的组合。# 别这样写直接deconv容易出棋盘格# self.deconv nn.ConvTranspose2d(64, 3, kernel_size9, stride3)# 建议这样用pixel shuffle替代self.pixel_shufflenn.PixelShuffle(upscale_factor)self.conv_lastnn.Conv2d(64//(upscale_factor**2),3,kernel_size5,padding2)VDSR残差学习的威力VDSR引入了残差学习网络深度增加到20层。我复现时发现VDSR对学习率的敏感度比SRCNN低很多用1e-4到1e-3都能稳定收敛。但有个问题梯度裁剪必须加。20层的网络如果不做梯度裁剪训练到第10个epoch左右loss会突然爆炸。# 这里必须加梯度裁剪否则训练到一半就炸了torch.nn.utils.clip_grad_norm_(model.parameters(),max_norm0.4)VDSR的另一个特点是支持多尺度超分。论文里用一个模型同时处理2x、3x、4x。我试过在训练时随机选择缩放因子效果确实不错但推理时要注意输入图像的尺寸必须能被缩放因子整除否则会报错。EDSR去掉BN层反而更好EDSR是2017年NTIRE比赛的冠军核心改进是去掉了Batch Normalization层。我一开始不理解觉得BN能加速收敛去掉不是自废武功吗后来实验发现对于超分任务BN层会引入噪声导致输出图像出现色差。特别是在4x超分时有BN的模型PSNR比无BN的低0.3dB左右。EDSR的另一个trick是残差缩放。每个残差块的输出乘以一个小于1的系数论文里用0.1防止深层网络梯度消失。我试过不缩放训练到50个epoch后loss就不再下降了加上缩放后又能继续下降。# 残差缩放系数0.1别写成0.5太大效果会变差residualself.conv2(self.conv1(x))*0.1xxresidualRCAN注意力机制引入超分RCAN在EDSR基础上加入了通道注意力机制CA模块。我复现时发现CA模块的reduction ratio压缩比设为16效果最好设成4或32都会导致性能下降。另外RCAN的参数量很大约15M训练时显存占用高我用的2080Ti跑batch size只能设16再大就OOM了。有个细节RCAN的RIR残差中残差结构里每个残差组的输出也要做残差缩放。我一开始只做了组内缩放忘了组间缩放结果PSNR比论文低0.5dB。SwinIRTransformer在超分的胜利SwinIR是2021年的工作把Swin Transformer引入超分。我复现时最大的感受是训练太慢了。SwinIR的参数量虽然只有11M比RCAN还小但训练时间却是RCAN的2倍。原因在于Transformer的自注意力计算复杂度高即使用了窗口注意力计算量依然不小。但效果确实好。在Set5数据集上SwinIR的PSNR比RCAN高0.2dB左右而且视觉上纹理更清晰。不过有个问题SwinIR对低分辨率图像的尺寸有要求必须是窗口大小的整数倍窗口大小默认8。我遇到过输入图像尺寸是127x127结果SwinIR报错的情况后来加了padding才解决。# 这里要注意输入尺寸必须是window_size的整数倍# 否则会报错我在这里卡了半天H,Wlr.shape[2:]pad_h(window_size-H%window_size)%window_size pad_w(window_size-W%window_size)%window_size lrF.pad(lr,(0,pad_w,0,pad_h),modereflect)性能对比别只看PSNR我在DIV2K验证集上做了4x超分的对比实验结果如下PSNR/SSIMSRCNN: 28.52/0.811FSRCNN: 28.78/0.822VDSR: 29.15/0.835EDSR: 29.68/0.851RCAN: 29.82/0.858SwinIR: 30.02/0.865但PSNR高不代表视觉效果好。我拿一张有轻微运动模糊的图像测试SwinIR的PSNR最高但输出图像出现了伪影反而EDSR的结果更自然。所以实际部署时我建议同时看PSNR和LPIPS感知相似度后者更符合人眼感受。实战建议选模型要看场景如果你做的是实时应用比如手机拍照超分FSRCNN或ESPCN就够了速度快效果也能接受。如果是离线处理比如老照片修复直接上SwinIR效果最好。如果是文档类图像文字、表格SRCNN或VDSR反而更稳因为Transformer模型有时会把文字边缘过度平滑。训练时有个通用技巧先用L1 loss训练再用L2 loss微调。L1 loss收敛快但容易产生锯齿L2 loss收敛慢但纹理更平滑。我一般先用L1训练80个epoch再用L2微调20个epoch效果比只用一种loss好。最后提醒一句别迷信SOTA。我见过太多人花两周调SwinIR的参数最后效果还不如一个调好学习率的EDSR。超分这个领域数据质量、训练策略、后处理比如自集成往往比模型结构更重要。