
几个月前我们团队在推进一个老照片修复工具的离线版候选方案里有一款开源超分模型当时在公开测试集上的指标很好PSNR、SSIM在同级模型里都排得上号。结果一上真实用户照片翻车得相当彻底人脸的皮肤纹理被抹成类似塑料的质感老旧印刷品的网点纹路被放大成奇怪的伪影甚至有些原本清晰的文字边缘被补出了不存在的弯曲。团队围着指标调了一周后来才意识到问题不在模型容量也不在训练技巧而在我们对“真实世界图像退化”这件事的理解太理想化了。这也是我后来认真研究TinySR这类轻量级扩散模型的原因——它在“真实世界”和“轻量级”这两个看似矛盾的目标之间走了一条平衡路线。图像超分辨率本身是个老问题但过去几年主流方案集中在CNN和Transformer的回归式重建扩散模型虽然效果惊艳却因为参数量和采样步数的问题难以落地。TinySR把两者拉回到同一张桌子上试图用可控的推理成本换取真正接近真实照片的细节生成。这篇文章就围绕它的设计思路、训练细节、部署优化和我们在实测中踩过的坑展开适合正在做超分模型落地、或者考虑用扩散模型做图像增强的工程师参考。1. 真实世界的图像退化合成数据与落地场景之间的“代差”1.1 经典退化模型为什么一到真实照片就失灵很多刚接触超分的人会默认一个前提高分辨率图像经过双三次下采样得到低分辨率图像模型去学逆过程就行。这个假设在公开 benchmark 上跑得通因为评测集就是用同样的方式构造的。但真实照片完全不是这么回事。手机拍摄的照片经过镜头光学像差、传感器噪声、去马赛克算法、ISP 锐化、压缩编码等一系列处理放大后还可能有运动模糊或对焦不实的问题。老照片、扫描文档、监控截图又各有各的退化来源。Real-ESRGAN 提出的高阶退化模型是解决这个问题的经典尝试它把多种模糊、噪声、下采样和 JPEG 压缩按随机顺序叠加试图覆盖更广的退化空间。但这里有个关键问题合成退化再怎么组合本质上是从一个有限参数空间里抽样而真实世界的退化和场景内容强相关不同区域的退化程度也是不均匀的。比如一张照片可能是中心清晰、边缘模糊同时暗部噪声明显、亮部出现高光溢出。这种情况如果要硬套一个全局退化模型模型就只能学到退化的“平均形态”在具体图片上往往两边不讨好。我在实际测试里做过一个对比用同一套超分模型分别处理双三次下采样的合成图和真实手机拍摄的低光照片。合成图效果接近完美真实照片则出现明显的过平滑和伪影。这个现象说明训练分布决定了能力的上限模型再大也无法弥补训练数据和真实场景之间的偏差。1.2 合成退化与真实退化的差异下面这个表格是我自己整理的一个粗略对照能帮助理解两者的差距退化因素合成退化典型真实世界退化常见模糊来源高斯核、各向异性核镜头像差、运动模糊、对焦偏移噪声来源高斯噪声、泊松噪声传感器读取噪声、暗电流、ISP处理残留压缩伪影JPEG 单次压缩多次压缩、不同编码参数、色度抽样退化分布全局统一空间不均匀、内容相关附加问题基本没有暗角、眩光、色偏、水印、划痕如果训练数据里只有第一列的退化模型在第二列的真实场景上表现不佳几乎是必然的。TinySR 这类面向真实世界的方案第一步就是把训练数据的构造方式和评测方式都搬到第二列的场景里去。1.3 真实世界超分的评价困境PSNR 高不代表“看得对”还有一个容易踩的坑用 PSNR/SSIM 这类全参考指标来评估真实世界超分结果。这两个指标在理想退化条件下比较有效但到了真实场景我们根本没有对应的 ground truth 高清图。即便有PSNR 对纹理细节的判断也很不敏感它更偏向于逐像素的数值接近。一个过度平滑的结果可能 PSNR 很高因为它的像素误差小但视觉上就是糊的而一个纹理丰富但位置略偏的结果PSNR 反而不高却更接近人眼的真实观感。所以面向真实世界的超分项目评测体系一般要换成无参考指标加主观评测。无参考指标常用的有 NIQE、MANIQA、MUSIQ 这些虽然它们也都有各自的偏差但至少不依赖 ground truth。更可靠的做法是组织人力评估比如让5个人盲评“哪张更接近真实照片的质感”。TinySR 在评测时也采用了类似的思路在量化指标之外更关注纹理真实感和伪影控制。2. 扩散模型做超分效果惊艳代价也大2.1 扩散模型天然适合超分任务的原因扩散模型的核心思路是先定义一个前向过程把数据逐步加噪变成纯噪声再训练一个网络反向去噪逐步恢复原始数据。用在超分任务上就是把低分辨率图像作为条件让网络在去噪过程中“生成”出与低分辨率输入一致的细节纹理。这和传统回归式超分有一个本质区别传统模型学习的是“平均预期”给定一个低分辨率输入网络输出的高频细节往往是多种可能性的平均所以容易显得平滑、模糊。扩散模型则是从数据分布中采样它生成的高频细节更像是“从分布里抽出来的合理结果”而不是平均结果。拿人脸举例传统超分可能把眼睛区域重建得轮廓正确但缺乏细节扩散模型则可能补出符合真实皮肤质感的纹理看起来更自然。我之前在做老照片修复时也有类似体会生成式的做法在纹理丰富的区域明显更有优势。但代价也很现实扩散模型通常需要几十到上千步的迭代去噪每一步都要跑一次神经网络。这就引出了市面上扩散超分模型普遍存在的两个问题——大和慢。2.2 常规扩散模型的“大”与“慢”具体体现在哪里先看参数规模。早期比较有代表性的 SR3它的主干网络是 U-Net 结构参数量在数千万到上亿级别。后来出现的 Stable Diffusion Upscaler 这类基于潜在扩散模型的方案更夸张因为它在潜在空间里做去噪放弃了对像素级别的直接建模整个底座的参数量都在十亿量级。再看推理速度。扩散模型采样一次需要迭代很多步。完整 DDPM 采样要 1000 步尽管后续 DDIM 等方法能压缩到 50 步左右但每一步仍需完整跑一遍几十层甚至上百层的 U-Net。我实测过用一张 512×512 的图像在 V100 上跑 50 步 DDIM单张推理时间大概在 10 到 30 秒量级。作为对比一个轻量 CNN 超分模型在同一张图上通常几十毫秒就能出结果。这个差距意味着如果不做专门的工程优化扩散模型超分很难直接用到实时或批量场景里。这里并不是说扩散模型不值得用而是需要找到“怎么把生成质量保住同时把采样步数和模型体积压下来”的路径。TinySR 的“轻量级”设计其实就是奔着这个目标去的。3. TinySR 的轻量化设计给扩散模型“瘦身”但不牺牲效果3.1 结构重参数化与通道稀疏化从模型本身挤水分扩散模型通常是一个 U-Net主体由卷积层、注意力层、上下采样模块组成。轻量化最直接的思路是缩减通道数和深度但盲目裁剪会导致模型容量不足生成质量下降明显。TinySR 这类方案通常会采用结构重参数化的技巧在训练阶段使用多分支结构比如把 3×3 卷积、1×1 卷积和恒等连接并行叠加让模型有更丰富的特征表达能力在推理阶段再把多分支融合成一个 3×3 卷积减少实际计算量。这种做法的好处是训练时的容量不受影响推理时的计算量却能降下来。实际落地时我会先跑一个完整版模型再用通道稀疏化工具分析每个通道对最终损失的贡献度剪掉贡献低的通道然后做一轮微调。剪枝率一般控制在 30% 到 50% 之间再高就会出现明显的画质下降。剪完之后的模型参数可能只剩原来的三分之一到一半。通道稀疏化的另一种形式是层剪枝或模块级剪枝。比如 U-Net 中的多头注意力层在超分任务里不一定每个头都重要可以尝试裁剪掉对结果影响最小的注意力头这对整体生成质量的影响通常比直接裁剪通道更小。3.2 从采样步数上“抠”成本少步采样与蒸馏结构压缩之外另一块大头是采样步数。完整的扩散模型推理往往需要上百步即便用 DDIM 也要 50 步左右。TinySR 从两个方向解决这个问题一个是使用高阶采样器比如 DPM-Solver、DPM-Solver它利用扩散 ODE 的形式可以用更少的步数达到相似的采样质量实际使用中 10 到 20 步就能得到可用结果另一个是采用蒸馏的方法让轻量模型直接模仿教师扩散模型在较少步数下的输出从而把推理步数进一步压到 4 到 8 步。蒸馏的思路和常规的知识蒸馏相似但有一个更贴合扩散模型的做法在训练时随机采样不同的噪声强度然后让轻量学生模型输出尽量接近教师模型在相同噪声强度下的输出。这样让学生模型学会在每一步都模仿教师的行为而不是只学会最终结果。我在实践中的一个经验是少步采样容易出现“过度平滑”或“细节不足”的问题尤其是当你把步数从 20 压到 4 时。解决这个问题的关键不在采样器而在训练阶段就要随步数变化做准备。比如在蒸馏时随机抽取不用的步数组合让学生模型适应不同步数下的输出分布等模型部署时切换步数就不会有太明显的画质落差。3.3 条件注入方式的选择concat 还是 cross-attention扩散模型做超分时必须把低分辨率图像作为条件注入到去噪网络里。最常见的两种方式是通道拼接concat和交叉注意力cross-attention。通道拼接简单直接把低分辨率图上采样后和噪声图合并作为一个多通道输入计算开销小但对条件信息的利用相对粗糙。交叉注意力更灵活能够在不同去噪阶段动态选择条件特征但计算量明显更高。TinySR 这类轻量级模型在条件注入上通常选择 concat 为主、注意力为辅的混合方式。这样既保持了可控的计算开销又让模型在局部细节上有条件地依赖低分辨率输入。实际使用中需要关注的是低分辨率图在输入前要保证尺寸和噪声图一致并且最好做一次轻量的上采样比如双线性插值而不是直接靠网络里的卷积层学习放大这样能减少条件信息的空间失真。4. 训练细节与损失设计高保真重建的关键不只是网络结构4.1 数据准备伪退化对与真实配对的组合拳真实的低分辨率/高分辨率配对数据非常难获取因为同一场景几乎不可能同时拍两张不同分辨率的照片。业界通用的方案是用合成退化来构造伪退化对。TinySR 在数据构造上做了更精细的控制模糊核的类型和尺寸随机化噪声类型在高斯噪声、泊松噪声之间随机切换JPEG 压缩质量也随机覆盖较宽的范围这样让模型尽量看到多样的退化形态。但仅靠合成数据还是不够的。更贴近真实世界的手段是用伪退化对做预训练再用少量真实配对数据做微调。真实配对数据可以通过固定相机拍摄印刷品、屏幕显示内容来构造或者用高分辨率相机和普通手机在同一场景下拍摄后再做配准。这类数据量不大但能有效拉近训练分布和真实场景分布之间的距离。4.2 损失函数组合重建、感知、对抗与一致性扩散模型本身训练时用的通常是简单的预测噪声的损失比如 epsilon-prediction 或 v-prediction。但在做真实世界超分时只靠噪声预测损失还不够。为了让生成结果既有真实感又不会产生明显偏色的伪影一般会用一组组合损失重建损失L1 或 L2 损失约束生成结果和真实高清图在像素层面的接近程度。L1 比 L2 在边缘处更稳定不容易产生过度平滑。感知损失在预训练好的 VGG 或更深的网络特征空间里计算误差能约束纹理和结构的相似性避免“像素对不上但视觉上很接近”的情况。对抗损失判别器网络判断生成结果是否真实逼迫模型产生锐利的高频细节。这里需要注意梯度惩罚避免训练不稳定。一致性损失在潜空间或者特征空间里约束生成结果与低分辨率输入的语义一致性避免模型凭空编造细节产生“幻觉”。实际操作中这几个损失的权重需要反复调。我偏向于先用重建损失和感知损失训练到相对稳定的阶段再加入对抗损失微调这样比一开始就全量上各损失收敛更稳。对抗损失加入太早容易出现色彩偏移或明显的棋盘格伪影。4.3 一个容易翻车的细节噪声调度与条件输入的值域扩散模型的噪声调度决定了前向加噪的过程以及训练和推理时每一步的噪声水平。很多复现失败其实不是模型结构有问题而是噪声调度和条件输入的值域没有对齐。比如低分辨率图像输入网络前如果没有和噪声图做相同的归一化处理模型在推理时可能会看到和训练时分布不一致的输入导致生成结果整体偏灰或者偏亮。我踩过的一个具体坑是直接用 PIL 读进来的图像值域是 [0, 1] 或 [0, 255]而扩散模型内部用的是标准化到 [-1, 1] 的分布。如果条件输入没做同样映射模型学到的条件信息就会打折扣。另一个坑是在训练时对低分辨率图像做随机裁剪增强却没有同步对噪声图做同样变换导致噪声图和条件图的空间位置对不上模型能力再强也学不好。提示检查训练和推理的预处理流程完全一致包括缩放、裁剪、值域变换、通道顺序是排查超分模型效果差的第一步。4.4 教师模型的选择与蒸馏边界使用蒸馏压缩模型时教师模型的选择直接影响学生模型的上限。如果我们拿一个本身就存在偏色的教师模型去蒸馏学生模型只会继承它的错误。所以在蒸馏之前我会先用一批真实场景图片对教师模型做“摸底测试”重点看它的纹理生成能力和伪影控制情况。教师模型在训练数据上表现最好的区域往往也是学生模型最容易学好的区域教师模型本身不稳定的区域学生模型学了反而容易在推理时放大这种不稳定。蒸馏时另一个经验是不要一味追求学生模型匹配教师模型的输出否则可能把教师模型的噪声也学过来。常见做法是加一个感知损失或频率域损失来约束学生输出让它在确保结构一致性的同时不会过度复制教师的高频噪声。5. 部署与推理优化模型小了还不够工程侧还能再压缩一截5.1 量化FP16、INT8 与敏感层处理模型体积缩小之后推理速度还有一个量级的提升空间来自量化。把 FP32 权重变成 FP16 几乎是无损的显卡和部分手机 NPU 走 FP16 甚至比 FP32 更快。进一步做 INT8 量化时问题就来了扩散模型对量化比较敏感尤其是有注意力计算的层直接量化容易导致生成结果出现边缘断裂或色斑。应对办法是混合精度量化把卷积层和残差块量化到 INT8将注意力层保留 FP16 或 FP32。一般先用训练后的校准集统计每层的激活值分布量化后逐层检查输出的误差定位误差最大的层将对应层回退到 FP16。这个流程重复两三轮通常就能在量化精度和推理速度之间找到平衡点。如果模型本身还有 BatchNorm 层需要先把 BN 层融合到卷积层里再做量化否则会影响量化后的数值分布。我在实测中发现TinySR 这种经过结构重参数化融合的模型量化起来反而比更复杂的注意力模型要容易。因为推理时主干已经是单分支卷积结构没有多分支带来的数值分布差异INT8 量化的掉点会小很多。5.2 推理框架与算子融合的取舍超分模型的部署框架选择取决于目标平台。下面是几种常见方案的对比目标平台推荐框架优势注意点云端 GPUTensorRT算子融合、精度高、支持多卡构建时间较长须按目标 GPU 定制通用 CPUONNX Runtime OpenVINO部署简单、跨平台对动态形状不太友好需固定输入尺寸手机/端侧NCNN、MNN体积小、支持 ARM算子支持有限需提前确认算子兼容性浏览器/WebWASM、WebGL无需安装、部署方便性能受限适合小模型算子融合是推理优化的重要步骤。最常用的融合方式是把 ConvBNReLU 合并成一个算子减少内核启动次数和中间张量读写。TensorRT 会自动做大量此类融合但如果你是部署到自研推理引擎就需要手动实现融合逻辑。还有一类优化是注意力计算的融合把 QKV 投影和多头注意力合并成一个大运算减少矩阵乘法次数。这些优化在扩散模型中收益尤其明显因为去噪网络结构重复每个 block 节省一点点几十步采样累积下来就是很大的速度提升。5.3 缓存复用与批处理策略端侧和云端的不同取舍扩散模型推理的输入是噪声图和条件图每次采样都要跑完全部去噪步数这个计算过程是可以利用缓存和批处理来优化的。在云端场景中如果同一个工作进程要处理多张低分辨率图可以将多张图打包成一个 batch 输入。GPU 在 batch 为 1 时往往利用率不高适当提高 batch 可以显著提高吞吐量但代价是单张图片的延迟变大。所以对实时性要求高、并发量低的场景我会用 batch1 加多进程并行对批量处理场景比如老照片修复工具的后台任务用 batch4 到 8 能明显提升 GPU 利用率。在端侧场景中一个比较实用的小技巧是条件图的上采样结果可以缓存。有些模型输入阶段会对低分辨率图做一次固定上采样如果同一个低分辨率图要连续生成多次可以把这个中间结果缓存起来避免重复计算。还有一个更激进的缓存思路相同类别图像在去噪早期的中间特征可能非常相似比如同一批证件照可以使用跨样本的特性缓存来减少前几步的重复推理。但这个方案需要非常谨慎地设计缓存失效策略否则容易引入伪影。6. 真实场景实测与踩坑记录最有效的“轻量”有时候不在模型里6.1 老照片修复、监控截图、手机数码变焦三个场景的实测结果我在实际项目中用 TinySR 逻辑做了一轮验证覆盖三个典型场景分别遇到了一些代表性问题。第一个是老照片数字化。老照片的噪声和划痕分布比较特殊退化在空间上非常不均匀有的区域因为底片损伤已经完全丢失了纹理。TinySR 对这类区域的恢复比较克制没有编造太多细节边缘相对自然。整体效果比传统回归法好但一些极端破损区域仍需要人工修饰。第二个是监控视频的截图。这里的问题是低分辨率人脸在放大后容易出现身份特征错误比如把眼睛位置偏移一点点人眼看起来“像但不对”。TinySR 的输出在轮廓上比较稳定但遇到极低分辨率的人脸仍然会出现纹理细节和真实身份的偏差。这说明在身份敏感的场景不能盲目追求纹理生成需要配合其他约束。第三个是手机数码变焦的模拟。从 1 倍变焦图像放大到 2 倍或 4 倍TinySR 在纹理合成上表现很好墙面、草地这类高频纹理区域没有出现明显的涂抹感和重复伪影这比许多传统超分模型强很多。但在文字区域偶尔会出现笔画变形的问题需要额外加约束或者用下游 OCR 任务来衡量效果。6.2 训练不稳时先查数据再调结构在实际训练 TinySR 的过程中我们遇到过一个非常典型的排查过程模型在训练初期 loss 下降正常但到了中后期开始剧烈抖动生成结果一会儿偏绿一会儿偏灰。第一反应通常是调学习率、换优化器或者检查梯度裁剪但排查了很久都没有明显改善。后来仔细检查数据预处理才发现问题出在训练数据的归一化方式上有一部分图像来自某个 SOP 流程像素值域被错误地映射到了 [0, 0.5]另一部分图像则是正常的 [0, 1]。模型在不同 batch 之间看到的输入分布来回跳变导致 loss 震荡。把数据集的预处理统一后训练立刻稳定下来。这个坑给我的教训是训练不稳时优先检查数据加载、预处理和数据统计比反复调超参更有意义。6.3 少步采样和量化叠加后的画质下降问题另一个实际踩过的坑是少步采样和量化叠加后的画质下降。单独做少步采样时模型效果还能接受单独做 INT8 量化时画质损失也不明显但两者叠加后生成结果出现了明显的纹理发糊和边缘抖动。排查后发现采样步数和量化误差存在放大关系。少步采样时每一步的误差本身更大量化误差相当于在这个基础上又加了一层噪声模型没有足够的迭代步数去修正它。解决方法是先做量化感知训练让模型适应量化后的权重分布或者把量化的敏感层回退到 FP16。经过调整后两者的叠加效应得到明显缓解。6.4 给工程落地的一点个人建议回过头来看TinySR 这类轻量级扩散模型真正重要的不是某一个具体模块而是整套流程的系统配合。数据构造决定了模型能力的上限结构设计决定了下限训练和蒸馏决定了实际能到达的位置部署优化决定了最终能不能真正投入使用。如果让我重新做一遍这个项目我会把更多时间花在数据构造和评测体系搭建上而不是一开始就扑在结构设计上。真实世界图像超分的难点从来不只是“把分辨率变大”而是要在放大过程中保持纹理的真实性和内容的正确性。模型参数、采样步数、量化精度这些都可以通过工程手段压缩但评测标准和数据分布这两件事压缩不了也绕不开。最后再分享一个经验无论模型多“轻量”在上线前一定要做一轮人类主观评测让不参与开发的同事盲选“哪个更像真实照片”。很多时候你觉得指标已经拉满了真实用户看到的却是另一种感觉。