Int8推理运行机制解析及相关技术疑问咨询
Int8 量化推理运行时详解与示例
输入处理
推理时的输入通常是float32格式,第一步会用校准阶段得到的输入层缩放参数,将其量化为Int8:
# 伪代码 input_int8 = np.clip(np.round(input_float32 / input_scale), -127, 127).astype(np.int8)
如果你的数据源本身是已经量化好的Int8数据(比如特定传感器输出),可以直接跳过这一步,但绝大多数AI推理场景都是从float32输入开始转换。
核心计算流程
Int8量化推理的核心逻辑是:乘积累加类操作(卷积、全连接)全程用Int8/Int32完成,非核心操作(如激活、偏置融合)会根据需要在Int8与float32间转换。
- 权重在量化完成后已经是Int8格式,推理时直接复用;
- 输入先转Int8,和Int8权重做卷积/矩阵乘,由于Int8相乘会溢出,中间结果用Int32存储;
- 偏置通常会提前量化为Int32(基于输入和权重的缩放因子),直接和Int32的卷积结果相加;
- 相加后的结果会反量化回float32执行激活函数,再根据下一层的需求量化为Int8,或者直接输出float32结果。
CONV2D+BIAS+ReLU 层推理示例
假设我们有以下预量化好的参数(校准阶段得到):
- 卷积权重:
W_int8(Int8类型,形状为[输出通道数, 输入通道数, 核高, 核宽]),对应权重缩放因子W_scale(按输出通道维度,形状[输出通道数]) - 输入缩放因子:
X_scale(单值,对应输入特征图的量化参数) - 偏置:
B_float32(float32类型,原始模型的偏置值) - ReLU输出缩放因子:
Y_scale(校准得到,用于将ReLU输出量化为Int8)
推理步骤(伪代码)
- 输入量化:将float32输入转为Int8
X_int8 = np.clip(np.round(X_float32 / X_scale), -127, 127).astype(np.int8) - Int8卷积计算:用Int8输入和Int8权重做卷积,输出用Int32存储防止溢出
# 模拟Int8卷积的乘积累加,结果为Int32 conv_out_int32 = conv2d_int8(X_int8, W_int8) - 偏置融合:先将float32偏置量化为Int32,再与卷积结果相加
# 偏置的缩放因子是输入缩放×权重缩放 bias_scale = X_scale * W_scale B_int32 = np.round(B_float32 / bias_scale).astype(np.int32) conv_bias_out_int32 = conv_out_int32 + B_int32 - 反量化到float32:将Int32结果转回float32,用于执行ReLU
conv_bias_out_float32 = conv_bias_out_int32 * bias_scale - ReLU激活:执行非线性激活
relu_out_float32 = np.maximum(conv_bias_out_float32, 0) - 输出量化(可选):如果下一层是Int8计算,将ReLU输出转为Int8
# ReLU输出非负,所以裁剪范围是0~127 Y_int8 = np.clip(np.round(relu_out_float32 / Y_scale), 0, 127).astype(np.int8)
关键补充
- 部分优化框架会把激活的量化和反量化步骤合并,减少浮点转换的开销;
- 按通道量化的权重,每个输出通道对应一个缩放因子,推理时需要对应通道的缩放参数做反量化;
- 如果推理的最终输出需要float32格式,最后一步可以跳过量化,直接输出反量化后的float32结果。
内容的提问来源于stack exchange,提问作者ИванКарамазов
相关产品推荐
相关产品推荐

