量化配置

量化配置#

QConfig 通过设置配置变量来配置量化行为。

备注

QConfig 对象在 C++ 中由节点系统(node system)支持,并且可以与 Python 和 C++ 之间交换参数。

不要直接构造,而是使用 qconfig()

一旦实例被构造,由 C++ 节点支持的字段就是不可变的。有关字段的信息请参见 tvm.relay.quantize.QConfig._node_defaults

小技巧

tvm.ir.make_node() 通过其类型键和字段创建新的 IR 节点。如果创建的节点是 AttrsNode 的实例,则创建函数还将运行绑定检查和 Attrs 支持的默认值设置。

比如:

x = tvm.ir.make_node("IntImm", dtype="int32", value=10)
assert isinstance(x, tvm.tir.IntImm)
assert x.value == 10
from tvm import relay
relay.quantize.QConfig._node_defaults

备注

/*! \brief Attribute for simulated quantize operator */
struct SimulatedQuantizeAttrs : public tvm::AttrsNode<SimulatedQuantizeAttrs> {
  int kind;
  bool sign;
  std::string rounding;

  TVM_DECLARE_ATTRS(SimulatedQuantizeAttrs, "relay.attrs.SimulatedQuantizeAttrs") {
    TVM_ATTR_FIELD(kind).describe("kind of field, hint for nbit/dtype configuration.");
    TVM_ATTR_FIELD(sign).set_default(true).describe("whether to use signed data type.");
    TVM_ATTR_FIELD(rounding).set_default("round").describe(
        "rounding mode. Can be 'floor', 'ceil', 'round'");
  }
};

SimulatedQuantizeAttrs 结构体用于表示模拟量化算子的属性。该结构体继承自 tvm::AttrsNode 类,并使用 TVM_DECLARE_ATTRS 宏来声明属性。

在结构体中,定义了三个成员变量:

  • kind:表示字段的类型和配置提示,用于指导量化算子的 nbit/dtype 设置。

  • sign:表示是否使用带符号的数据类型,默认为 true

  • rounding:表示舍入模式,可以是 floor(向下取整)、ceil(向上取整)或 round(四舍五入),默认为 round

通过 TVM_ATTR_FIELD 宏来指定每个成员变量的名称和描述信息。

这个结构体可以用于定义模拟量化算子所需的属性,例如在模型编译期间传递给量化层的配置参数。

用于存储量化配置选项的类:

class QConfigNode : public Object {
 public:
  int nbit_input = 8; // 输入张量的位宽,默认为8位。
  int nbit_weight = 8; // 权重张量的位宽,默认为8位。
  int nbit_activation = 32; // 激活函数输出张量的位宽,默认为32位。
  DataType dtype_input = DataType::Int(8); // 输入张量的数据类型,默认为整数类型(8位)。
  DataType dtype_weight = DataType::Int(8); // 权重张量的数据类型,默认为整数类型(8位)。
  DataType dtype_activation = DataType::Int(32); // 激活函数输出张量的数据类型,默认为整数类型(32位)。
  std::string calibrate_mode = "global_scale"; // 校准模式
  double global_scale = 8.0; // 全局缩放因子
  std::string weight_scale = "power2"; // 权重缩放模式
  bool skip_dense_layer = true; // 是否跳过 dense 层
  Array<Expr> skip_conv_layers = Array<Expr>(ObjectPtr<Object>(nullptr)); // 要跳过的卷积层的列表,默认为空。
  bool do_simulation = false; // 是否进行模拟计算
  bool round_for_shift = true; // 是否为移位操作进行舍入
  Array<Expr> debug_enabled_ops = Array<Expr>(ObjectPtr<Object>(nullptr)); // 要启用调试的算子列表,默认为空。
  std::string rounding = "UPWARD"; // 舍入模式
  int calibrate_chunk_by = -1; // 按块进行校准的块大小
  std::string partition_conversions = "disabled"; // 分区转换模式

  // 用于访问和处理属性的成员函数
  void VisitAttrs(AttrVisitor* v) {
    ...
  }

  static constexpr const char* _type_key = "relay.quantize.QConfig"; // 标识该类的类型
  TVM_DECLARE_FINAL_OBJECT_INFO(QConfigNode, Object); // 声明该类为 final 类
};

QConfig 类和 QConfigContext 结构体,用于管理构建配置的上下文。

QConfig 类是容器类,它继承自 ObjectRef 类。它具有以下成员函数:

  • 默认构造函数和带有 ObjectPtr<Object> 参数的构造函数。

  • 重载了箭头运算符 ->,以支持获取 QConfigNode 对象。

  • EnterQConfigScope 静态函数,用于将新的 BuildConfig 上下文压入线程本地栈中。

  • ExitQConfigScope 静态函数,用于从线程本地上下文栈中弹出 BuildConfig 上下文,恢复之前的配置作为当前上下文。

  • Current 静态函数,用于从线程本地存储中获取当前的 BuildConfig 上下文,如果没有进入 BuildConfig 范围,则返回默认配置。

  • 使用 ContainerType 别名指定 QConfigNode 为容器类型。

QConfigContext 结构体是 RAII(资源获取即初始化)容器,用于提供受控的 BuildConfig 上下文。它在构造时将配置压入上下文栈中,并在析构时弹出上下文。

Python 端 tvm.relay.quantize.qconfig 参数解读:

  • nbit_dict: QAnnotateKind: 每种注释字段的位数。

  • calibrate_mode: str: 校准模式:‘global_scale’ 或者 ‘kl_divergence’。

    • global_scale: 使用 global scale。

    • kl_divergence: 在数据集中通过 KL 散度查找 scale。

  • global_scale: float: 校准的全局 scale。

  • weight_scale: str: 计算权重的 scale 的方法(用 QAnnotateKind.WEIGHT 注解)。

    • power2: 找到张量绝对值的最大值,然后将其向上取到 \(2\) 的幂次方。

    • max: 找到张量绝对值的最大值。

  • skip_dense_layer: bool = True: 是否跳过所有的 nn.dense 层类型。

  • skip_conv_layers: list[int]: 指定要跳过的层。提供索引列表,指示哪些卷积层保持不变。从 \(0\) 开始。

  • do_simulation: bool: 是否仅使用浮点运算进行模拟。

  • round_for_shift: bool: 是否在移位时添加舍入偏差。

  • debug_enabled_ops: list[str]|None = None: 部分量化指定的算子进行调试。默认值为 None,这意味着将尝试调用所有算子的注解重写函数。

  • rounding: str: 定点乘法的舍入方向。“UPWARD” 或者 “TONEAREST”。

  • partition_conversions: str = 'disabled': 可选值:'disabled''enabled' 或者 'fully_integral'。如果设置为 'enabled''fully_integral',则将量化的结果划分到模块容器,其中包含前缀函数(由输入转换为量化数据空间组成),中间函数(由核心量化网络组成),后缀函数(由输出反量化组成)以及主函数(依次调用前缀、中间和后缀函数)。如果设置为 'fully_integral' 且结果中存在未量化的算子,则会引发异常。默认值为 'disabled'