声明:本文主要基于公开资源学习整理,如有错漏,欢迎评论交流~
1. reset_scale 的作用
源码位于 horizon_plugin_pytorch/quantization/observer_v2.py。
校准完成后,每个 HistogramObserver 实例已累积了直方图数据并算出了 scale。但如果默认搜索策略(如 mse)的精度不满足预期,想换一种策略(如 percentile)重新搜索 scale,怎么办?重新跑一遍校准数据耗时太长——reset_scale 正是为了解决这个问题:
它遍历模型中所有模块,找到使用 HistogramObserver 的伪量化节点,利用已有的直方图数据,用新的搜索策略重算 scale,无需重新跑校准。
2. 参数详解
参数 | 类型 | 含义 |
|---|---|---|
cls | 类对象 | @classmethod 的隐式参数,绑定到调用时的类本身(如 HistogramObserver),而非某个实例。后文详述 |
model | torch.nn.Module | 待操作的校准模型,reset_scale 会遍历其中所有模块来查找目标 observer |
method | str | 搜索策略名称。可选 "percentile"、"mse"、"kl" 等,决定如何从直方图中选取 max_val |
method_kwargs | Dict | 搜索策略的额外参数。如 method="percentile" 时可传 {"percentile": 0.999}。默认为空字典 |
prefix | Tuple[str] | 模块名前缀过滤。只对名称以该前缀开头的模块生效,如 prefix=("conv1",) 只处理 conv1、层。默认为 None,即处理所有模块 |
dtype | quant dtype | 数据类型过滤。只对指定 dtype 的层生效,如只重算 qint8 层的 scale,跳过 qint16 层。默认为 None,即不区分 dtype |
3. 源码逻辑
三层过滤(prefix → cls类型 → dtype)+ 空直方图检查,确保只对符合条件的已校准 observer 重算 scale。
4. cls 与 self:为什么 reset_scale 用 cls
注意到 reset_scale 的第一个参数是 cls,不是 self。这是由其语义决定的。
cls 与 self 的核心差异
self 绑定实例,cls 绑定类。
- self 是实例方法的第一个参数,指向一个具体对象。通过 self 能访问实例独有属性(如 self.histogram),必须先有实例才能调用。
- cls 是 @classmethod 的第一个参数,指向类本身。无需实例即可调用,能做类型判断(isinstance(obj, cls)),但无法触及实例属性。
总结:self 关心"我自己的数据",cls 关心"我们这类对象该怎么操作"。
reset_scale 用 cls 的三个优势
1. 语义正确
reset_scale 是"HistogramObserver 类,请帮我重算模型中所有你类实例的 scale"——这是类级别的批量操作。如果用 self,调用变成:
用一个 observer 实例去操作其他 observer,语义上说不通——它们之间没有从属关系。而 cls 的调用方式语义清晰:
2. 类型判断支持继承多态
源码中 isinstance(m.activation_post_process, cls) 用 cls 而非写死类名。如果子类继承并调用 reset_scale,cls 自动指向子类,判断和操作都针对子类,无需修改代码。
3. 无需实例即可调用
校准完成后,用户手上的是模型而非某个 observer 实例。cls 方式让用户直接通过类名调用,使用门槛最低。
那 cls 无法访问实例属性怎么办?reset_scale 通过 model 参数遍历找到目标实例,再通过实例引用调用 calculate_qparams 等实例方法完成具体操作。这是一种常见模式:cls 方法负责发现和编排,self 方法负责具体的数据操作,各司其职。
