测试时偏好优化:让图像恢复按用户审美自适应调整
最近跟一个常做图像后期的朋友聊到AI修复老照片他提到一个很真实的痛点模型把照片修得越“猛”皮肤纹理反而越容易被磨成塑料。这句话背后其实藏着一个长期被低估的问题——图像恢复的结果本质上非常主观可绝大多数深度学习模型只输出一个默认答案。一张被噪声、压缩痕迹或雾霾污染的图有人希望锐利到能看清每根发丝有人宁可整体干净平滑有人要保留颗粒质感有人要彻底磨皮。这种主观偏好传统方法要么用一堆后处理滑杆去“手搓”要么干脆忽视。某高校视觉课题组最近在推进的一项工作——用于图像恢复的测试时偏好优化——给了个很不一样的思路把“个性化”完全搬到推理阶段模型针对单张图像、结合用户的少量偏好信号现场自我调整。它不是重新训练模型也不是让部署方去收集海量偏好标注而是用轻量化适配层加多轮反馈在测试时直接优化出符合当前用户审美的输出。这篇内容我就围绕这个方向把技术动机、整体设计、实现细节和踩坑复盘完整拆一遍给准备入手或者正在做类似方向的同学一个参考。1. 为什么“偏好”必须搬进测试时图像恢复的主观性困境1.1 所有恢复模型默认输出的是“平均脸”先看一个基础问题一个典型的深度学习恢复模型训练时最小化的是训练集上的平均误差。无论你用L1、L2还是感知损失本质都是让输出尽可能接近“大多数样本的某个平均状态”。可问题在于这个“平均状态”在真实用户的主观空间里往往没有任何锚点。就像给人脸磨皮一部分用户要的是“明显处理过但好看”另一部分用户要的是“看不出处理但自然”模型只能在这两种诉求之间取一个中间点。这个矛盾在图像恢复上的表现特别明显同一张退化图用同一个模型有人觉得去噪过头细节没了有人觉得去噪不够依然很脏。原因不是模型能力不行而是模型根本没有表达“用户想要什么”的通道。我试过把一个恢复结果拿给几类人看修图师、普通用户、做监控取证的人关注点完全不同。修图师在意纹理是否自然普通用户在意是否“一眼好看”监控场景则要求不能凭空脑补细节。一个固定权重的恢复模型不可能同时满足这些需求。1.2 已有偏好调节手段为什么很别扭有人会说传统方案里也有“调节”手段比如条件化输入、后处理曲线、参考图引导。但这些方案离“用户偏好”其实都隔着一层。条件化输入像给模型喂噪声水平估计、模糊核参数或退化类型标签这只能控制物理退化强度比如“去噪重一点”或“轻一点”。可用户偏好并不完全等于物理退化强度有人就是喜欢在低噪声情况下也保留锐化质感有人则宁可牺牲一点细节换平滑。条件标签表达不了这种维度。后处理曲线例如亮度、对比度、饱和度的调整更是只能动全局统计量。它无法影响网络内部的恢复决策更不可能做到“在保留发丝纹理的情况下把背景噪点抹掉”这种局部语义层面的偏好。参考图引导倒是灵活要求用户提供一张“我希望大致长这样”的参考图模型再向参考图迁移风格。但普通用户根本不会为了修一张图去搜参考素材交互成本太高而且参考图本身和退化图的内容、构图差距越大效果越不稳定。至于收集大规模偏好标注来训练一个多偏好模型理论上可行实践中却很难持续。偏好数据是主观的、会漂移的今天大家喜欢高饱和过阵子风向变成低饱和训练一次的成本动辄成百上千小时。偏好本质上是一种非平稳信号放训练阶段处理是最笨的选择放测试时处理反而是最自然的时机。1.3 测试时优化的真正价值以反馈为中心而不是以数据为中心测试时偏好优化这个名字容易让人误解成“测试时训练”但它和常见的测试时适应Test-Time AdaptationTTA不是一回事。TTA解决的是训练分布和测试分布不一致的问题比如模型在晴天训练、雨天测试测试时用目标域的信息快速调整归一化层统计量它对准的是“物理退化环境”。而偏好优化对准的是“主观目标”是用户喜欢什么、当前场景适合什么。这带来的核心能力是模型不需要为每个用户重新训练也不需要收集大量个性化数据只需要在推理过程中用很少的反馈信号完成局部参数或输出的定向调整。实际操作里用户负担可以压缩到“看两个候选结果选一个更喜欢”这种级别每张图点两三次就够。如果没有人工参与也能用自动偏好代理模型替代用户面向大众审美做自动化优化。这个思路还有个容易被忽略的好处它的每次优化都是独立的。用户A的偏好不会污染用户B的结果因为测试时优化只在推理阶段发生不写入全局权重。这种隔离性对产品化非常重要相当于每个用户拿到的是一个“只属于自己偏好”的瞬时模型版本。2. 整体方案与底座设计什么样的恢复模型适合测试时优化2.1 底座网络的三个硬性要求测试时优化不是空中楼阁它需要站在一个足够强大的预训练恢复模型之上。根据我自己复现相关工作的经验底座网络至少要满足三个条件。第一基础恢复能力要强。如果底座本身连常见退化都处理不了测试时优化只能在小范围里“打转”解决不了能力短板。它应该能覆盖噪声、模糊、下采样、压缩伪影等主流退化类型而不是只适配某一种退化。第二网络的表示空间要连续可导。这点很多人会忽略。测试时优化的本质是在参数空间或特征空间里做梯度搜索如果网络结构里存在大量跳跃性输出、不连续的激活偏好信号对参数的梯度就会充满噪声最终结果像在雷区里探路极不稳定。第三要具备“可局部调整”的结构基础。全量参数微调在单张图上几乎必炸模型只有把大部分权重冻住、只留一小部分可调参数时测试时优化才可能稳定收敛。这里自然引出一个通用技术低秩适配。2.2 底座模型怎么选判别式优先还是生成式优先我在实验里对比过两大类底座一类是确定性判别式网络典型如基于Transformer或CNN的去噪/超分模型另一类是生成式模型典型如扩散类恢复方法。两者都能做测试时优化但体验差异很大。底座类型速度优化稳定性偏好表达自由度显存压力判别式快高相对有限低扩散生成式慢有挑战高高判别式网络输出路径短梯度传播稳定单张图几十步优化就能看到明确变化但它的解空间有限偏好方向只能在“轻度锐化”到“轻度平滑”这类区间里游走。扩散生成式模型天然建模多模态输出分布同样的输入理论上可以给出一整条偏好谱系表达自由度更高但推理慢、显存大优化时还容易在去噪采样的随机性里震荡。实际项目中不必迷信生成式一个调教得当的判别式底座加合适的适配结构可能比强行套扩散模型更实用。文章标题里的方案最终采用的是“生成式底座加轻量适配”的路线但我在复现时发现把同样的适配逻辑搬到判别式底座上收敛更快效果也完全可用。2.3 适配层设计为什么是低秩更新而不是全量微调测试时优化的可调参数集中在适配层。低秩更新的思路很简单把权重的更新量约束成两个低秩小矩阵的乘积$\Delta W BA$其中 $B \in R^{d \times r}$$A \in R^{r \times d}$训练时只更新 $A$ 和 $B$原始权重完全冻结。为什么这么设计假设底座是70M参数模型全量微调意味着每张图都要对7000万个参数求梯度单张图的信息量根本撑不起这么大参数空间的可靠更新模型会在无关区域产生灾难性漂移。只更新低秩矩阵后实际参与更新的参数量大约只有原始模型的0.1%到0.5%换成70M模型就是不到35万参数。用这么小的参数量去拟合“单张图上的一种偏好方向”信息量和参数量才基本匹配。放置位置也有讲究。我在实验里比较过把适配层放在网络浅层、编码器附近和解码器输出端附近的效果结论是放在靠近输出端的注意力层效果收益最明显放在浅层更新几乎看不到稳定变化。原因也好解释浅层特征还处在“通用视觉特征”阶段和任务目标距离远输出端附近才真正决定像素级的细节取舍改动这里的表示偏好信号才能直接作用到最终画面上。2.4 一次完整的测试时优化流程长什么样整理一下我实际跑的流程一共六步输入退化图像 $x$用冻结的底座网络先产出初始恢复结果 $y_0$把它当“可靠锚点”。在适配层参数上加入小扰动生成 $k$ 个候选输出版本比如 $k3$ 或 $k4$。用偏好信号评估这些候选有人工参与就让人点选“哪张更合我意”没有人工参与就让自动偏好代理模型给每个候选打分。根据反馈构造排序损失或加权损失反传更新适配层参数。回到第2步重新生成候选循环1到3轮。最终输出由最后一次迭代的适配层参数决定。核心伪代码是这样的# 测试时偏好优化主循环伪代码 x load_degraded_image(path) base_model.eval() adapter LoRAAdapter(base_model.hidden_dim, rank16) # 只保留低秩矩阵 y0 base_model(x, adapterNone) # 初始默认输出作为锚点 optimizer AdamW(adapter.parameters(), lr3e-4) pref_score_ema 0.0 for round in range(max_rounds): # 通常2轮最多3轮 candidates sample_candidates(base_model, adapter, x, k4) # 人工点选或代理模型打分返回候选a优于候选b的偏好结果 a, b, pref get_preference(candidates) for step in range(steps_per_round): # 每轮内做50步左右 loss preference_loss(adapter, x, y0, a, b, pref) optimizer.zero_grad() loss.backward() clip_grad_norm_(adapter.parameters(), 1.0) optimizer.step() pref_score_ema 0.9 * pref_score_ema 0.1 * pref_score(a, b)第2步里的扰动不能太大否则候选画面会脱离“可信恢复”的范围变成纯风格迁移。我用的是标准差为0.01的参数空间高斯扰动再在相同种子下推理。3. 偏好信号从哪来人工反馈与自动代理模型的两条路径3.1 人工反馈为什么“二选一”比打分更可靠测试时优化的输入信号是偏好而偏好信号设计得好不好直接决定优化能不能收敛到用户真正想要的东西。我在交互实验中比对过两种反馈形式请用户按5分制打分以及请用户在两个候选中做二选一。结论非常一致打分结果的跨用户波动远大于二选一。原因是人对自己“喜欢什么”其实没有精确量化的能力。同样一张图用户心情不同、屏幕亮度不同、上一个对比样本不同打分就可能浮动1到2分。但二选一这种相对判断对人类的认知系统来说就轻巧得多。所以在方案里人工反馈一律采用成对比较。成对比较的数据怎么变成可优化的信号通常用Bradley-Terry模型把偏好概率建模为$$P(a b) \frac{\exp(r_a)}{\exp(r_a) \exp(r_b)}$$其中 $r_a$、$r_b$ 是候选在偏好模型下的分数。训练时去最大化“用户选了a而不是b”的概率等效于最小化交叉熵损失$$-\log P(a b)$$这里还有一个主动采样的问题。一共只有两三轮交互机会不能让用户随便看两个候选就定方向最好每次选出来让用户对比的那一组候选信息量最大。怎么做优先选偏好模型打分最接近、用户最可能犹豫的那一对。如果a和b的得分几乎相同说明这个维度上的偏好还没被观察到让用户在这一对里选择能最快暴露真实意图。3.2 自动偏好代理没有真人时谁来当“口味代表”测试时偏好优化并不是每一项应用都能拉到真人点选。ROI不够大、集成在后台自动流程里、或者交互弹窗会影响体验时就得有自动化偏好信号源。这时通常用几个预训练的评估网络来组合成“代理口味”整体美学评分判断画面构图、色彩、曝光这类“好看”维度感知清晰度评分判断有没有明显锐化痕迹、边缘是否自然自然度评分判断是否出现伪影、塑料感、色块断层语义一致性评分判断恢复后的内容是否和退化图的内容匹配。这些分数不是简单相加而是加权求和权重本身就是一种“口味预设”。比如“细节优先”模式里清晰度权重占大头“自然优先”模式里自然度权重占大头。测试时优化完全可以用代理模型替代人在后台跑完整流程。代理模型最大的坑在于它会骗人。某些输出局部出现高反光、边缘超锐化真实用户一眼就觉得奇怪但代理模型却可能给它高分。一旦偏好优化顺着代理模型的“错误审美”走下去结果会越来越奇怪。所以我在实际操作中加入了一道校准把代理模型的打分和少数真实用户的二选一结果做一次线性回归校准后再用于优化。不要小看这个步骤它能明显减少“自动优化结果看起来很假”的问题。3.3 偏好冲突与置信度代理打架时怎么办偏好的另一个复杂之处是冲突。一个代理模型说“锐化方向更好”另一个说“自然平滑更好”两个分数组合后梯度可能互相抵消。我在测试时踩过不少这类坑总结出几条处理原则第一冲突激烈时降低更新步长。可以计算两个主要代理分数变化方向的一致性如果夹角接近90度以上就把学习率临时降到原来的1/5避免在两个矛盾方向上横跳。第二低置信度时不更新。当两个候选在偏好模型下的概率都接近0.5时说明这个维度对当前用户而言并不重要此时强行优化只会引入噪声。直接跳过这一步等下一轮候选信息更丰富再决定。第三预设风格向量可以缓解冷启动冲突。如果是摄影师用户可以预制“纹理优先”方向让代理模型在锐度维度上有更高的基础权重如果是人像修图则让平滑维度占主导。这样大多数冲突在进入优化前就被风格向量消解了不会都压到实时反馈环节。4. 跑通收敛的关键损失函数、优化器设置与过拟合防线4.1 损失函数不能只有一个偏好项我最早踩的坑是天真地以为损失函数只要包含“偏好”就行。跑出来结果很快垮掉输出画面对偏好模型分数越来越高但离真实视觉越来越远。原因很直接单张图像提供的信息太少纯偏好驱动会让模型走上对抗性捷径。合理的损失组合必须有三部分偏好项、锚点一致性项、正则项。我用的总损失形式如下$$L -\lambda_{pref}\log\sigma(r_a - r_b) \lambda_{cons}\ d(y, y_0) \lambda_{reg}\left(|A|^2 |B|^2\right)$$第一项是偏好信号驱动输出向用户选择的方向移动。第二项的 $y_0$ 是预训练模型的默认输出$d$ 是感知距离度量作用是把优化结果约束在离默认输出适当远的范围内避免灾难性漂移。第三项是对低秩矩阵的L2正则保证适配层更新幅度可控。关键在于锚点一致性项的强度。太强输出会和默认结果几乎一样偏好优化失去意义太弱输出自由漂移可能生成“偏好模型很满意但人看着很怪”的图。我初始一般设置 $\lambda_{pref}1.0$$\lambda_{cons}0.3$$\lambda_{reg}0.01$然后观察输出的主观变化以每次迭代后画面“看得出变化但不是崩坏”为基准微调。不同底座和不同图像尺寸最优值差异很大下表只作为起点参考超参初始值调整方向$\lambda_{pref}$1.0偏好信号太弱时增大$\lambda_{cons}$0.3输出漂移过快时增大$\lambda_{reg}$0.01参数更新噪声大时增大学习率3e-4梯度震荡时降到1e-4秩 $r$16无法表达复杂偏好时增加到324.2 优化器与学习率策略低秩参数同样需要细腻调度优化器我直接用AdamW这是默认选择但对于测试时优化这种只有少量参数、且训练步数极少的场景学习率策略比优化器本身更关键。实践经验是学习率从 $3\times 10^{-4}$ 开始前面5步做warmup避免第一轮更新太猛随后按余弦退火衰减到原来的1/10。每个轮次内大约50步轮次间继续沿用上一轮的学习率状态不做重置。另一条重要规则是梯度裁剪clip到1.0。因为偏好模型产生的梯度偶尔会出现异常大的峰值不裁剪的话一个异常步就能把适配层参数推出正常范围。还有一个容易被忽视的点偏好分数的指数滑动平均。我在主循环里用pref_score_ema记录历史偏好分数当当前候选的偏好分数和历史均值相差超过阈值时直接跳过这一步更新。它的价值在于偏好模型本身有噪声如果连续两轮的偏好方向严重不一致说明当前信号质量差硬更新只会加深震荡。4.3 防止“过拟合到偏好模型”的三道防线单图像测试时优化天然容易过拟合——不是内容上的过拟合而是“过拟合到偏好模型的漏洞上”。对策可以总结成三道防线。第一道防线是锚点约束上面已经说过。第二道防线是参数噪声扰动在向适配层参数加噪声后再推理候选相当于给优化过程加了空间上的“抖动”能一定程度模糊偏好模型的对抗性区域。第三道防线是候选筛选计算候选与 $y_0$ 的感知距离距离太远的候选直接丢弃不让它参与反馈。这三道防线加在一起我遇到过的大部分“越优化越假”问题都能压住。需要注意这些防线的本质都是限制自由度代价是优化速度变慢。如果只是为了跑通Demo可以直接只用锚点约束后面两道尽量轻否则交互轮次拉长用户会失去耐心。5. 实测踩坑复盘显存瓶颈、梯度震荡与评测陷阱5.1 显存瓶颈一张512的图就把卡吃满测试时优化最大的硬件压力来自反向传播。我之前在复现时用一块消费级显卡去跑一个中等规模的生成式恢复底座输入512×512图像LoRA适配层只更新少量参数但显存仍然飙到接近16GB。原因是反向传播必须计算整个底座网络对适配层参数的梯度虽然只更新部分参数中间激活值却要全部保存。实际项目中我一般用三种办法缓解。一是patch级优化把图像切成256×256的patch让每个patch独立做30到50步优化最后用重叠区域高斯加权融合。这个方法能极大降低显存压力但要注意patch边界不能露出块效应重叠区至少8到16像素融合权重用距离加权。二是梯度检查点技术前向时放弃保存部分中间激活反向时重新计算用少量时间换显存一次能省下30%左右。三是全流程混合精度偏好模型和适配层梯度用半精度计算但锚点一致性损失里的感知距离度量需要更高精度最好保持在单精度下。如果这些手段都用上还是跑不动可以考虑两步式优化先只更新靠近输出端的适配层让偏好方向大致定型再解冻少量中间层做精细调整。这样做牺牲了一点最终效果但显存可控很多。5.2 梯度震荡输出在“变锐利”和“变平滑”之间反复横跳另一个高频问题是梯度震荡。表现是第一轮优化后画面明显朝“锐利”方向走第二轮更新后又退回“平滑”偏好分数在高位徘徊视觉结果忽好忽坏。我复盘过原因主要是偏好模型在候选差异不大时给出的信号本身太多噪声。比如候选A和B只在局部纹理有细微差别偏好模型可能随机地给A或B更高分这种信号作为梯度毫无意义。解决方式我放在两处一是低置信度跳过机制候选之间的偏好概率差距小于0.15时不做更新二是对偏好分数做指数滑动平均用平滑后的趋势替代单步瞬时值。两者配合后震荡幅度明显下降。另外一个容易忽视的环节是随机种子。生成式底座每次推理都有随机性如果测试时优化时不固定种子观察到的“优化效果变化”可能有很大一部分来自采样的随机波动。我的做法是同一轮内的所有候选必须在同一个基础种子上通过给适配层参数加扰动来产生差异跨轮次之间允许换种子但评估效果时必须用固定种子回放对比。5.3 评测陷阱为什么指标赢了用户不一定满意这类方案最终成果很难用PSNR、SSIM这类指标证明价值。我刚跑通时做了一个小对比优化后的输出比默认输出偏好分数显著提高我满心欢喜拿去给人看结果一批用户说“太假了”。后来复盘才明白偏好模型分数的提升不一定对应真实用户的主观满意度。要有效评测测试时偏好优化我的建议是做双盲配对对比。同一张退化图生成默认恢复结果和偏好优化结果顺序随机让用户在三个维度上分别判断清晰度、自然度、整体满意度。每次只改动一个方向比如一个候选是“更多细节纹理”另一个是“更平滑干净”不要在一个候选里同时把锐化、饱和度、对比度全变了否则用户根本不知道自己在选什么。我在一次小样本实验里观察到很有意思的结果偏好优化后的输出在PSNR上比默认输出低了0.3dB左右但70%的受试者选择了“整体满意度更高”。这个对比足以说明这类工作的评价指标应该以主观偏好为主客观质量指标只能当作判断“是否发生灾难性漂移”的护栏。6. 下一步扩展交互式修图、视频稳定与扩散引导6.1 可落地的产品形态滑块释放后的一次快速优化测试时偏好优化最自然的产品落地场景是在修图软件里做“更合我意”按钮。用户不需要理解任何技术概念只需要做完一次初选模型在后台跑50到80步优化返回一个更贴合当前偏好的版本。消费级设备上如果直接跑完整流程延迟还是偏高我的建议是做成两段式先用自动偏好代理生成几个风格预设点让用户快速点选大方向再用人工二选一在预设点附近做小幅精调。这样交互次数被压缩到1到2次每次等待时间控制在1秒以内。端侧小型化也很关键。低秩适配层本身参数很少可以很方便地随模型一起量化我在测试中发现适配层权重量化为int8后偏好优化的改善幅度几乎不受影响这为移动端部署留了很大空间。6.2 视频场景逐帧优化的时域闪烁怎么解把测试时偏好优化从单帧推广到视频立刻会撞上时域一致性问题。每一帧单独做测试时优化适配层参数可能漂移结果就是画面在帧间“闪”一会儿偏锐利一会儿偏平滑视觉上非常难受。我尝试过两种解法效果靠谱程度不同。第一种是整段视频联合优化把视频抽成关键帧用一个共享的适配层做多帧联合优化并在损失函数里加入光流一致性约束让相邻帧在对应位置的输出差异被压小。这个方案效果好但显存和时间开销成倍增长。第二种是先对第一个关键帧做偏好优化后续帧沿用同一个适配层参数不再逐帧更新。这个方案快但无法跟踪连续发生的偏好变化只能保证整段视频风格一致。实际项目里可以根据场景需求取舍偏专业剪辑建议用第一种走快速预览管线可以用第二种。6.3 与扩散模型结合的后续空间扩散类恢复模型天然适合嵌入偏好信号。不需要额外训练一个适配模型采样过程的每一步都能在预测的噪声或潜变量上施加偏好模型的梯度相当于一种偏好引导。这个方向和测试时偏好优化并不冲突反而互补扩散引导负责在采样阶段把“物理恢复”和“偏好方向”同时考虑低秩适配层则负责在输出端做最终修正。如果再引入自然语言形成偏好向量例如“更干净但保留毛发纹理”这类口语化描述那么文本-图像对比模型能把它解码成可优化的梯度方向最终系统的表达能力会上升一个台阶。我自己在实际复现里的体会是这个方向最容易踩的坑其实不是模型或损失函数而是“评测方式还没跟上”。如果你准备做类似的工作我的建议是先把偏好信号和真实人类选择的校准工作做扎实再谈优化算法把测试时的主观评测设计得严格一些否则即使训练出来的优化效果再明显也很难讲清楚它到底提升了什么。最后一条经验用户反馈不等于真假判断用户要的从来不是“绝对清晰”而是“符合自己审美的那一种清晰”。搞清楚这一点测试时偏好优化的价值才能真正体现出来。