FEATURED · 精选文章

HAMP-LIC:Hessian感知混合精度量化,压缩图像模型同时保持高质量

发布时间 / 2026/8/30 8:09:22
来源 / 创域科博编辑部
栏目 / 资讯中心
HAMP-LIC:Hessian感知混合精度量化,压缩图像模型同时保持高质量 1. 从“能压缩”到“压缩得更聪明”HAMP-LIC 为什么值得关注做过图像压缩相关工作的读者应该都有体感传统编码标准JPEG、HEVC、VVC长期被“手工设计变换 熵编码”的框架主导性能天花板越来越明显。而 Learned Image CompressionLIC学习式图像压缩把神经网络引入编码框架性能已经逐步逼近乃至超越传统编码器。但 LIC 有一个非常现实的工程问题模型体积大、计算开销高而部署端往往是手机、边缘盒子、浏览器 WebAssembly算力和存储都有限。量化是解决这个问题的标准手段。常规做法是直接对整个模型使用同一个 bit 宽度比如全部转 INT8。但图像压缩模型和普通分类模型不一样它的解码器质量对每一层的敏感度差异极大。有的层截断到 8bit 几乎无损有的层甚至连 6bit 都很危险。统一量化等于让最敏感的那一层决定整个模型的精度下限最终结果是“为了少数敏感层牺牲了绝大多数层的压缩潜力”。HAMP-LIC 正是从这个痛点切入的方案。它提出了一种 Hessian 感知的混合精度后训练量化方法核心是给不同层分配不同 bit 宽度用 Hessian 信息判断“哪些层值得保留更高精度”。这篇博客围绕 HAMP-LIC 展开三件事第一它解决了什么问题跟统一量化有多大差距第二Hessian 信息为什么能指导量化位宽分配第三这类方法在真实部署中怎么落地、有哪些坑。如果你正在做 LIC 模型的端侧部署、模型压缩或者只是对“量化感知的位宽分配”感兴趣这篇文章应该能帮你把概念和工程路径连起来。2. 先建立背景LIC 的模型结构里哪些部分最需要保护在展开 HAMP-LIC 之前有必要先建立 LIC 模型的基本坐标。一个典型的端到端学习式图像压缩模型通常由四部分组成编码器Encoder、量化器/隐表示处理、熵模型Entropy Model、解码器Decoder。输入图像经过编码器提取隐表示latent representation然后经过量化得到离散符号熵模型估计这些符号的概率分布并用于熵编码最后解码器从量化后的符号重建图像。从部署角度看LIC 模型的体积主要来自编码器和解码器。实际工程中编码器可能运行在服务端或多端共享计算单元而解码器往往运行在用户设备上。两者对资源约束的敏感度不同因此量化策略也可能不同。HAMP-LIC 的核心价值是在“把模型变小”和“把重建质量保住”之间做更聪明的折中。普通量化对每一层一视同仁HAMP-LIC 则用二阶敏感度信息判断每一层“离了高精度行不行”。这背后的数学直觉是一个参数的扰动对最终损失的影响可以用损失函数对该参数的梯度、Hessian 来刻画。如果某层参数的 Hessian 特征值较大说明损失函数对它的变化非常敏感量化这种层造成的损失会更大因此应该分配更高 bit反之则分配低 bit。理解到这一层再去看 HAMP-LIC 的标题就清楚了Hessian-Aware 是判断依据Mixed-Precision 是分配策略Post-Training Quantization 是约束条件——不重新训练只做量化这对生产环境尤其友好。它不需要大规模重训或蒸馏只需要少量校准数据、一次前向传播和敏感度分析就能得到混合精度配置。3. 为什么统一精度量化在 LIC 上容易“翻车”很多第一次接触 LIC 量化的同学会问既然 INT8 在分类模型上表现不错直接套到 LIC 上不行吗答案是可以跑通但质量损失往往不可接受。普通分类模型的输出是离散类别最后一层只要保持类别区分度中间层的微小扰动可能被后续层“稀释”掉。但图像压缩模型的输出是像素级重建解码器需要非常精确地从量化后的隐表示恢复出纹理细节。中间任意一层被量化截断误差都会向后续层传播和放大最终体现为视频中的块效应、模糊、纹理丢失甚至颜色偏移。更麻烦的是LIC 模型不同层的敏感性方差很大。第一层卷积可能只负责低频颜色信息量化到 4bit 也看不出差别而靠近重建输出的某些卷积层负责高频边缘和细节8bit 都可能不够。统一量化策略在这种“长尾敏感度分布”面前非常吃亏。假设模型有 30 层其中 3 层对 8bit 敏感、其余 27 层对 4bit 就足够统一量化只能选 8bit模型压缩率可能少了一半。HAMP-LIC 的思路正是打破这种“一刀切”让每个卷积层或每个残差块根据自身敏感度获得 2bit、4bit、6bit、8bit 不等的位置。混合精度之后的模型平均 bit 数可能只有 4bit 左右但是敏感层仍然保留 8bit整体重建质量还能保持很高水平。这就是“用敏感度换空间”的核心逻辑。4. Hessian 信息如何指导位宽分配4.1 从梯度到 Hessian为什么要用二阶信息量化可以看作在参数空间中对每个参数施加一个小的扰动。一阶信息梯度告诉我们损失在某个方向上的变化率但梯度很小并不代表这个方向不重要——因为损失函数不是线性的参数点可能刚好处于一个“平坦”的位置但曲率很大。想象站在山谷底部任何方向移动都会导致高度迅速上升但当前位置的一阶导数非常接近零。如果只靠梯度判断可能会误以为“随意扰动都没问题”而实际上这里的损失地形非常陡峭。Hessian 矩阵刻画的就是这种局部曲率。二阶信息越大说明损失函数在该方向上的敏感度越高。对量化来说这就等于一个天然的路标高曲率方向对应的层需要更高精度的参数表示否则量化噪声会被放大。4.2 HAMP-LIC 中的 Hessian 计算与位宽搜索严格讲直接计算完整 Hessian 矩阵在大模型上是不可行的HAMP-LIC 采用 Hessian 对角近似或 Hessian 迹估计来降低计算开销。具体做法是给定少量校准图像在已训练好的 LIC 模型上做一次前向和反向传播计算损失对每层参数的梯度再结合局部梯度乘积或 Hutchinson 方法估计对角 Hessian。得到每层的 Hessian 度量值后将其归一化排序映射到预设的候选位宽集合。举个例子候选位宽可以是 {2, 4, 6, 8}。Hessian 度量值最小的层分配 2bit中等分配 4bit 或 6bit最敏感的层保留 8bit。为了满足总字节数预算HAMP-LIC 通常还会加入一个可微或可搜索的位宽分配模块把“总比特数不超过阈值”作为约束把重建损失作为优化目标。这一步和 Neural Architecture Search 中的资源约束搜索有相似之处但搜索空间从“网络结构”变成了“逐层位宽”。4.3 为什么这类方法适合后训练量化后训练量化PTQ的难点在于不能用完整训练集重新优化模型。HAMP-LIC 只需要少量校准数据不需要标签因为重建损失本身就是监督信号。这使得它非常适配“模型已经在服务端训练好需要部署到端侧”的场景。对工业界来说重训一个 LIC 模型成本太高而 PTQ 方案可以在一小时甚至几分钟内完成敏感度分析和位宽分配省时省力。5. HAMP-LIC 的实际流程拆解理解了原理之后下面把 HAMP-LIC 的落地流程拆成六步。代码部分以 Python 风格伪代码为主重点展示接口和思路不绑定具体深度学习框架。如果你要复现建议基于自己的 LIC 训练框架比如 CompressAI做移植。5.1 加载预训练 LIC 模型与校准数据首先需要有一个训练好的 LIC 模型。如果没有可以用 CompressAI 提供的预训练模型做替代。校准数据建议从真实业务图片中采样 32 到 128 张覆盖不同纹理、光照和分辨率不需要标注。# 伪代码加载预训练模型与校准数据 import torch from compressai.models import ScaleHyperprior model ScaleHyperprior(N128, M192) state_dict torch.load(path/to/pretrained_lic.pth.tar) model.load_state_dict(state_dict[state_dict]) model.eval() from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder from torchvision.transforms import ToTensor calib_dataset ImageFolder(rootpath/to/calib_images, transformToTensor()) calib_loader DataLoader(calib_dataset, batch_size1, shuffleFalse)这里使用 ScaleHyperprior 只是示例。实际项目里请以你自己的模型结构为准。校准数据集要避免和训练集高度重叠否则计算出的敏感度会偏乐观。5.2 计算逐层 Hessian 敏感度Hessian 的对角估计可以用 torch.autograd 实现。对每个校准 batch 计算重建损失MSE 或 MS-SSIM然后对某一层参数做反向传播。为了得到对角 Hessian 的近似可以使用 Hutchinson 方法对一个标准正态随机向量做两次前向/反向用梯度内积估计对角 Hessian。# 伪代码计算逐层 Hessian 对角近似 def compute_hessian_diag(model, dataloader, num_samples8): hessian_diag {name: torch.zeros_like(param) for name, param in model.named_parameters()} for _ in range(num_samples): z torch.randn_like(torch.cat([p.flatten() for p in model.parameters()])) # 实际实现中需要把 z 映射到每层参数形状 # 这里简化计算 loss 对参数的梯度再估计逐层 Hessian 迹 for batch in dataloader: x batch[0] y model(x) loss torch.mean((y[x_hat] - x) ** 2) grads torch.autograd.grad(loss, [p for p in model.parameters() if p.requires_grad], create_graphTrue) # 结合 z 与 grads 更新 hessian_diag break return hessian_diag这段代码为了可读性做了大量简化。实际中Hutchinson 方法需要让随机向量通过计算图产生二阶导数而不是直接对 loss 求梯度。更稳妥的实现方式是使用 torch.autograd.functional.hessian 或自定义的二次梯度计算函数但这样内存占用会很大。工程上更推荐按层计算 Hessian 迹避免一次性构建完整 Hessian。需要提醒的是不要试图直接计算完整 Hessian 矩阵。LIC 模型参数量通常达到百万级以上完整 Hessian 矩阵的存储是参数量的平方完全不可行。对角线近似或迹估计是实用路径。5.3 位宽分配搜索得到每层敏感度分数之后需要把连续分数映射到离散位宽候选集。这一步可以建模成一个背包问题每层分配某个位宽会有对应的模型体积增量同时带来敏感度惩罚目标是在体积上限内最小化总惩罚。# 伪代码基于敏感度分数的贪心位宽分配 bit_candidates [2, 4, 6, 8] sensitivity_score {name: score for name, score in hessian_diag.items()} assignments {} for name, score in sensitivity_score.items(): # 分数越高越敏感分配越高位宽 if score high_threshold: assignments[name] 8 elif score mid_threshold: assignments[name] 6 elif score low_threshold: assignments[name] 4 else: assignments[name] 2更精确的做法是使用整数编程工具比如 PuLP 或 OR-Tools把“总比特数”作为约束把“敏感度惩罚和”作为最小化目标。这一步的好处是可以在给定模型体积预算下自动搜索最优配置。# 伪代码使用 OR-Tools 做位宽分配示意 from ortools.linear_solver import pywraplp solver pywraplp.Solver.CreateSolver(SCIP) # 定义每层每个位宽的 0-1 变量添加体积约束和敏感度惩罚项实际部署中如果只有几十层贪心算法通常已经足够接近最优解而且速度快得多。OR-Tools 更适合位宽候选很多、层数很多的大型模型。5.4 执行逐层量化位宽分配确定后就可以对模型做逐层量化。PTQ 的典型做法是先统计每层激活和权重的 min、max 或百分位数再计算 scale 和 zero_point最后执行量化。PyTorch 中可以使用 torch.quantization 或 torchaoTensorRT 则直接支持 per-channel INT8/INT4 混合精度。# 伪代码对指定层执行 4bit 量化示意 import torch def quantize_tensor(tensor, bits4): qmin, qmax 0, (1 bits) - 1 min_val, max_val tensor.min(), tensor.max() scale (max_val - min_val) / (qmax - qmin) zero_point qmin - round(min_val / scale) q_tensor torch.clamp(torch.round(tensor / scale) zero_point, qmin, qmax) deq_tensor (q_tensor - zero_point) * scale return deq_tensor注意量化权重后还需要评估激活值范围。图像压缩模型中隐表示的激活范围可能随时间步变化建议在校准集上跑一遍前向记录每层激活的统计值。5.5 量化模型重建质量评估量化完成后必须用验证集做重建质量对比。常用指标包括 PSNR 和 MS-SSIM。建议同时对比三组结果原始模型、统一 8bit 量化、HAMP-LIC 混合精度量化。如果 HAMP-LIC 在相同体积下 PSNR 高于统一 8bit在相同 PSNR 下体积小于统一 8bit说明方案有效。# 伪代码计算 PSNR import math import torch def psnr(img1, img2, max_val255.0): mse torch.mean((img1 - img2) ** 2) if mse 0: return 100.0 return 10 * math.log10(max_val**2 / mse.item())评估时不要只盯 PSNR。对于图像压缩模型MS-SSIM 更能反映人眼感知质量。HAMP-LIC 的效果在不同图片纹理区域可能差异明显建议把测试集按纹理复杂度分组分别统计。5.6 导出部署格式量化模型最终要导出为端侧推理格式。以 ONNX 为例需要把量化 scale 和 zero_point 固化到模型中然后转为 TensorRT Engine 或 TFLite。导出后务必在目标硬件上跑一遍完整推理因为 PC 上模拟量化和实际硬件执行之间仍然有差异。# 示例导出 ONNX python export_onnx.py --checkpoint path/to/quantized_model.pth --output model.onnx6. 环境准备与前置依赖HAMP-LIC 本身不是某个独立软件包而是一类方法需要你在自己的 LIC 训练框架上实现。如果从零开始建议按以下环境准备操作系统LinuxUbuntu 20.04 或 22.04Windows 也可以但算子支持可能晚一步。Python 版本3.8 或以上推荐 3.10。PyTorch1.13 或 2.0 以上需要支持自动微分。图像压缩框架CompressAI开源提供多种预训练 LIC 模型。量化库PyTorch 内置量化工具、torchao或 TensorRT。位宽搜索工具OR-Tools 或 PuLP非必需。GPU建议有至少 8GB 显存用于 Hessian 计算和校准。版本细节以实际环境为准。如果模型是在 TensorFlow 上训练的也可以把 HAMP-LIC 的思路迁移到 TFLite 的混合量化流程但 Hessian 计算会更麻烦因为 TensorFlow 对二阶梯度的支持体验不如 PyTorch 直接。7. 混合精度量化的完整示例基于 CompressAI 实战这一节给出一个更完整的实验脚本思路。假设你已经安装了 CompressAI 和 torch下面脚本用于加载预训练模型、跑通量化流程主干。# 文件路径hamp_lic_demo.py import torch import torch.nn as nn from compressai.models import ScaleHyperprior from torchvision import transforms from torch.utils.data import Dataset, DataLoader from PIL import Image import os class ImageDataset(Dataset): def __init__(self, img_dir): self.img_paths [os.path.join(img_dir, f) for f in os.listdir(img_dir) if f.endswith((.png, .jpg, .jpeg))] self.transform transforms.Compose([ transforms.ToTensor(), ]) def __len__(self): return len(self.img_paths) def __getitem__(self, idx): img Image.open(self.img_paths[idx]).convert(RGB) return self.transform(img) def load_model(ckpt_path): model ScaleHyperprior(N128, M192) state_dict torch.load(ckpt_path, map_locationcpu) model.load_state_dict(state_dict[state_dict] if state_dict in state_dict else state_dict) model.eval() return model def compute_sensitivity_by_layer(model, dataloader): # 这里简化用每层权重的 L2 范数作为敏感度代理。 # 更严谨的做法是用 Hessian 迹估计。 sensitivity {} for name, param in model.named_parameters(): if param.dim() 2: sensitivity[name] torch.norm(param).item() return sensitivity def assign_bit_width(sensitivity, high8, mid6, low4, low_low2): scores list(sensitivity.values()) max_s max(scores) min_s min(scores) assignments {} for name, score in sensitivity.items(): norm_score (score - min_s) / (max_s - min_s 1e-6) if norm_score 0.75: assignments[name] high elif norm_score 0.5: assignments[name] mid elif norm_score 0.25: assignments[name] low else: assignments[name] low_low return assignments if __name__ __main__: model load_model(checkpoint_best_loss.pth.tar) calib_dataset ImageDataset(calib_images) calib_loader DataLoader(calib_dataset, batch_size1, shuffleFalse) sens compute_sensitivity_by_layer(model, calib_loader) assignments assign_bit_width(sens) for name, bit in assignments.items(): print(f{name}: {bit}bit)这段代码里的 sensitivity 用权重范数代替只是为了演示接口流程。真实 HAMP-LIC 中必须用 Hessian 信息替代否则位宽分配没有依据。你可以把 compute_sensitivity_by_layer 替换成 5.2 节中的 Hessian 估计版本。8. 运行结果与效果验证运行上面的脚本你会看到每一层被分配的 bit 宽度。更完整的实验至少需要输出三张表模型总参数量、每层位宽、量化后验证集 PSNR/MS-SSIM。一个合理的实验结论应该满足混合精度配置的平均 bit 数低于 8bit例如 4.2bit 左右。在相同平均 bit 数下HAMP-LIC 的 PSNR 比统一 4bit 高 1dB 以上。在相同 PSNR 下HAMP-LIC 的模型体积比统一 8bit 小 40% 以上。如果结果不符合预期按以下顺序排查校准集是否具有代表性。Hessian 估计是否稳定尝试增加随机样本数。位宽搜索的约束权重是否合理。解码器关键层是否被错误分配到低 bit。激活量化范围是否过大导致量化噪声升高。可以在验证阶段绘制“平均 bit-PSNR 曲线”把统一量化的点和 HAMP-LIC 的点放在同一张图上。如果 HAMP-LIC 在曲线上方说明它同时优化了体积与质量。9. 常见问题与排查思路问题现象可能原因排查方式解决方案Hessian 计算内存爆炸直接构建完整 Hessian 或二阶导数图过大检查是否有 create_graphTrue 的梯度链累积改用按层 Hessian 迹估计或 Hutchinson 方法减小校准 batch size位宽分配结果几乎全是 8bit敏感度分数分布不均匀或阈值设置过高打印敏感度分数分布观察是否存在极端值对敏感度分数做 log 归一化放宽阈值量化后 PSNR 骤降激活范围统计不准确或敏感层被分配低 bit逐层对比量化前后输出分布检查敏感层的位宽强制最敏感层保持 8bit用更充分的校准集统计激活范围模型体积减小但码率反而上升混合精度与熵模型不匹配导致熵编码效率下降对比同体积下不同位宽组合的码率在位宽搜索中增加码率约束对隐表示的量化精度做特殊保护部署硬件不支持某些位宽目标硬件只支持 INT8 或 FP16查看推理引擎支持的量化位宽文档把候选位宽集合改为硬件支持的 {8, 16} 或 {4, 8}混合精度在 GPU 上推理变慢不同位宽的算子未融合kernel launch 开销大使用 profiling 工具查看各层耗时合并相邻同一位宽的层使用 TensorRT 的混合精度执行模式10. 最佳实践与工程建议10.1 优先保护解码器末端层LIC 模型里重建图像的质量主要由解码器末端层决定。这些层一旦量化损失过大误差会直接呈现在输出像素上。建议把解码器的最后几层设为不可量化层或固定 8bit只对其他层做混合精度搜索。这个小改动往往能显著提升最终 PSNR。10.2 校准集要贴近真实业务分布HAMP-LIC 的敏感度完全依赖校准集。如果校准集全是平滑的自然图像量化后的模型在文字、屏幕截图、高纹理图像上可能表现很差。建议从真实业务流量中随机采样覆盖不同分辨率、不同内容类别。校准集数量在 64 到 256 张之间通常足够。10.3 与熵模型联合考虑LIC 模型和普通 CNN 的另一个区别是隐表示的熵编码依赖概率估计而量化精度直接影响最终码率。混合精度量化如果让隐表示的数值分布改变熵模型的预测概率可能失准导致实际码率上升。因此在做位宽搜索时除了重建损失要把码率增量也纳入目标函数。HAMP-LIC 的 Hessian 计算对象可以扩展为率失真损失R λD这比单纯用 MSE 更贴合 LIC 的优化目标。10.4 不要忽视 per-channel 量化粒度HAMP-LIC 的位宽分配是在层粒度但实际量化时还可以在通道粒度上进一步优化。卷积层的权重按通道统计 min/max 往往比整个 tensor 统一 scale 更稳定。两者可以叠加外层用 HAMP-LIC 做层间位宽分配内层用 per-channel 量化减少误差。10.5 用自动搜索替代手工调阈值手工设置 0.75、0.5、0.25 这些阈值只适合快速验证。正式项目中建议用整数规划或强化学习方法做位宽搜索把硬件的算子支持矩阵作为约束之一。这样得到的配置才是真正可部署的。10.6 建立量化回归测试集量化会影响重建质量建议建立一套自动化回归测试。每次模型更新或量化策略调整后自动跑一遍重建质量测试和体积测试并把结果推送到看板。只有持续监测才能避免“上线后才发现某些图片质量崩了”的情况。11. 总结与后续学习方向HAMP-LIC 的核心贡献不是喊出“混合精度”这个口号而是把 Hessian 敏感度作为位宽分配的依据让量化过程从“拍脑袋选位宽”变成“基于损失地形做决策”。它特别适合已经训练好的 LIC 模型做快速端侧部署不需要重训、不需要大量标注数据只需要少量校准图像和一次敏感度分析。如果你接下来想深入可以从几个方向展开一是研究 Hessian 估计的效率优化比如用 Fisher 信息近似替代 Hessian二是把混合精度搜索扩展到激活量化因为 LIC 模型里激活的内存占用同样不可忽视三是探索量化感知训练与 PTQ 的组合策略先用 HAMP-LIC 找一个好的位宽分配起点再用少量步数的 QAT 微调敏感层往往能进一步收复质量损失。就实践而言建议你先把现有 LIC 模型跑一遍统一 8bit 量化记录体积和 PSNR 基线再实现 HAMP-LIC 式敏感度分析分配混合精度最后对比两条路线的率失真曲线。这一步跑通之后你对“模型压缩从粗放走向精细”的理解会比只看论文深刻很多。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻