1. 引言
使用J6工具链进行量化部署时,涉及 bool 类型数据计算时,会有一些与全float计算不同的情况,需要特别注意。
2. bool计算示例
在 PyTorch 中,bool 类型的数据用于表示 掩码(mask),常见的操作包括 torch.logical_not()、比较运算符(如 ==)等。当 bool 类型数据与其他数据类型进行算术运算时,PyTorch 会遵循 类型提升(Type Promotion) 规则,将 bool 转换为更高精度的数值类型。例如:
在这个例子中,bool 类型的 y 在计算时被提升为 float32 类型,因此计算结果仍然是 float32。
torch.mul 官方文档里确实没明确说支持 bool 类型输入(官网链接),但 PyTorch 底层的张量操作支持 bool,是一种 隐式支持,官方文档默认大家了解 PyTorch 的 类型提升(type promotion)规则。
3. 模型中bool量化问题分析
在量化模型中,当 bool 数据在计算过程中发生类型提升,特别是浮点数过了quant,再进行比较,可能会出现很大的量化误差。代码示例如下:
使用 actors_mask = actors_data[:, :, :, -1] == 1 来生成布尔掩码。
打印的结果如上,可以看到,float 输出没有任何问题。
然而,在 量化模型 中,这种 bool mask 运算会由于微小的量化误差发生非常大的变化,原因在:
数值1经过量化反量化后,可能会产生一个或多个scale的误差,原本是1的位置就不再是1了,会变成0.9x或1.0x,这样就==1就不再是True了。

打印看到actors_mask全部均为False。
这种结果明显是不符合预期的。
4. bool量化问题解决
怎么修改呢?如下所示
0经过对称量化,依旧是0,再经过logical_not即可。此时输出变为:结果是正确的。
这种方案一定正确吗?答案:不一定是正确的,需要考虑极值问题。另外,由于mul不支持输入为bool类型,这儿还会出现cpu算子问题。

生成的quantized.onnx可以看到,确实mul运行在cpu上。

4.1 CPU算子问题
主要原因是:右侧工具自动进行:bool->float32

第一个思路是:直接将actors_mask转torch.int16,
这样是不行的。因为过了quant的actors_input是Qtensor,而.to(torch.int16)强转的actors_mask是常规torch tensor,这也是不行的。
接着就可以想到,应该转float,然后过quant,修改如下:
此时mul左右两边都是qtensor,打印信息如下:
可以看到,是全一段BPU。

在不考虑极值的影响下,改动完成,此时代码如下:
4.2 极值问题
bool 被其他极大值 影响
如果模型输入actors_input有极大值存在,例如70000,int16量化,会将actors_mask原本是1的地方给变为0,量化输出示例如下:
根据量化公式:
scale:缩放因子(scale factor),用于将浮点数缩放到整数范围(量化尺度)。
round(float/scale):对缩放后的值进行四舍五入,得到量化的整数表示。
clamp(..., qmin, qmax):将量化值限制在 最小值 qmin 和 最大值 qmax 之间,防止溢出。
在这个示例中,scale = 70000/32767 = 2.1263。bool类型的1,经过量化:round(1 / 2.1263)=0,由于round舍入误差的存在,原来的1也被变为了0,再经过反量化也拯救不了这个舍入误差了。
bool 1作为 极大值 的影响
如果模型输入actors_data都是非常小的数值,由于bool类型1的存在,会导致1成为极大值,影响量化scale的统计,继而影响其他数值的量化精细程度。
所以,最稳妥的方式,是将actor_data与actor_mask分开送入模型。actor_data自己过quant,actor_mask自己过quant_mask。
4.3 解决方案示例
bool类型已经变 为0/1的float,可以这么写。需要注意,一定是只有 0 / 1的float,在模型中间也可以这么写。

如果是模型首部,且确实是bool输入


