深入浅出TinyML 21:INT8量化改变了模型中的哪些数据?

把模型文件标记为INT8并不能证明整个推理链都在执行整数计算。权重、输入、输出和中间激活可能采用不同数据类型,某些量化方式仍需要浮点输入或浮点内核。

MCU部署最常关注全整数量化:权重和激活使用整数表示,输入输出也具有明确Scale与Zero Point。是否获得速度收益还取决于运行时和目标处理器的优化内核。

|---------------------------------------|
| 检查量化模型时,要逐项确认权重、输入、输出、中间激活和算子内核的数据类型。 |

|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 完成本篇后,你应该能够 1. 说明INT8量化对权重、激活、输入输出和算子内核的改变。 2. 计算FP32与INT8张量的存储差异并识别饱和。 3. 运行一个整数全连接层,比较反量化输出与浮点基线。 INT8用-128到127共256个整数格子近似一段浮点范围。模型仍在做同类数学运算,但数值表示和内核发生改变。 |

图1:全整数量化涉及输入、权重、中间激活和输出映射

一、先把核心关系连起来

权重量化主要减少模型存储

FP32权重通常每个占4字节,INT8权重每个占1字节。仅权重量化可以显著缩小权重部分,但推理时可能将权重还原为浮点,输入和激活仍为浮点。

因此文件变小不等于整数内核已经生效。需要查看模型张量类型和目标运行时执行路径。

中间激活决定整数计算能否贯穿网络

全整数量化为每个激活张量保存量化参数,算子在整数域完成乘加、偏置和重缩放。乘法结果通常在更宽的累加类型中保存,再映射到下一层int8范围。

某个算子缺少整数实现时,转换器可能失败,或模型中出现浮点回退。MCU项目应明确禁止意外浮点算子。

表1:常见量化方案的差异

方案 数据类型路径 MCU意义
仅权重量化 权重INT8;输入和激活通常FP32 主要减小文件
动态范围量化 权重INT8;激活由运行时动态处理 需确认运行时支持
全整数量化 权重、输入和激活均使用整数路径 适合整数内核与MCU部署

二、输入输出契约随量化改变

int8输入需要把预处理后的实数按照Scale和Zero Point量化。模型输出也要反量化,或在整数域重新计算业务阈值。

量化参数来自部署模型,而非训练脚本中的经验常量。模型更新后,输入输出Scale可能变化,固件必须读取或同步新参数。

Python:要求转换器只生成全整数INT8算子

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| import tensorflow as tf converter = tf.lite.TFLiteConverter.from_saved_model("saved_model") converter.optimizations = tf.lite.Optimize.DEFAULT converter.representative_dataset = representative_dataset converter.target_spec.supported_ops = tf.lite.OpsSet.TFLITE_BUILTINS_INT8 converter.inference_input_type = tf.int8 converter.inference_output_type = tf.int8 tflite_model = converter.convert() |

三、收益要在目标固件上闭环

量化前后统一比较测试集指标、模型文件、最终Flash、Arena、推理时间和能量。模型文件缩小约四倍只是权重层面的理论关系,元数据和运行时代码不会按同比例缩小。

Cortex-M上的速度收益依赖CMSIS-NN等优化内核、算子形状和编译配置。板端测量是最终证据。

整数推理仍要表达实数尺度

量化张量由整数数组、scale和zero point共同表达。整数q对应实数r≈scale×(q-zero_point)。权重、输入、每层激活和输出可以拥有不同scale,卷积累加通常使用int32以容纳多个int8乘积。

一次整数全连接先计算 `(qx-zx)×(qw-zw)` 的int32累加,再结合输入、权重和输出尺度做requantize,最后限制到int8范围。高效内核把乘尺度转换成整数乘法与移位,避免每个元素执行浮点除法。

只有权重量化时,模型文件可能变小,输入和激活仍为浮点,MCU未必获得完整整数加速。全整数模型要求算子路径都支持INT8,并把输入输出类型明确设为int8。

|-----------------------------------------------------------------------------------|
| r = scale×(q-zero_point) int32累加:acc = Σ(qx-zx)(qw-zw) 再按 `(Sx×Sw)/Sy` 映射到输出整数。 |

量化误差集中在舍入、裁剪和异常范围

实数落到有限网格时发生舍入;超出校准范围时被裁剪到-128或127。少量极端值把scale拉大,会让大多数常见值只使用少数整数码;校准范围过窄又会在现场饱和。

比较FP32与INT8不能只看模型字节。要在同一独立测试集上比较混淆矩阵,统计输入与关键激活的饱和比例,并在目标板测Flash、Arena和延迟。

输入契约也改变。固件写入int8张量前要使用模型实际scale与zero point;直接把原始传感器整数截成int8会破坏物理尺度。输出分数需要反量化后再应用阈值,或把阈值预先映射到同一整数域。

INT8链路中的数值对象

对象 常用类型 检查
输入 int8+量化参数 范围与饱和
权重 int8/per-channel 每通道scale
累加 int32 溢出与偏置尺度
输出 int8或int16 反量化与阈值

动手:逐项执行一个INT8全连接层

脚本先计算浮点输出,再分别量化输入和两行权重,执行int32累加并反量化。它省略高效内核的定点requantize,但完整展示误差来自哪里。

实验环境与输入

  • Python 3标准库。
  • 保存为 `int8_dense.py` 并运行。
  • 参数为教学示例,不代表某个已训练模型。

按顺序完成实验

  1. 运行并比较float与dequant输出。
  2. 打印输入和权重整数码。
  3. 把输入2.4改为20,观察饱和及误差。
  4. 缩小输入scale,比较分辨率与范围冲突。

可直接运行:量化、int32累加与反量化

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| def q8(value, scale, zero=0): return max(-128, min(127, round(value/scale)+zero)) x = 0.35, -0.80, 1.20, 2.40 w = \[0.50, -0.25, 0.10, 0.35, -0.40, 0.70, -0.20, 0.15] bias = 0.05, -0.10 sx, sw = 0.02, 0.005, 0.006 float_y = sum(a\*b for a, b in zip(x, row))+bias\[i for i, row in enumerate(w)] qx = q8(v, sx) for v in x dequant_y = \[\] for i, row in enumerate(w): qw = q8(v, sw\[i) for v in row] acc = sum(a*b for a, b in zip(qx, qw)) # 偏置在真实整数模型中按 sx*swi 量化为int32 restored = acc*sx*swi + biasi dequant_y.append(restored) print("row", i, "qx", qx, "qw", qw, "acc", acc) print("float :", float_y) print("int8->fp:", dequant_y) print("error :", b-a for a, b in zip(float_y, dequant_y)) |

先读懂代码中的关键路径

  1. qx与每行qw使用不同scale,复现输入per-tensor、权重per-channel。
  2. 乘积先累加为宽整数,再乘Sx×Sw恢复实数。
  3. 偏置保留浮点只为教学;真实模型按Sx×Sw量化为int32。
  4. 输入超过scale覆盖范围时q8裁剪,信息不可恢复。

你应该观察到什么

  • 反量化输出接近浮点输出但不完全相等。
  • 累加值超出int8范围却安全保存在Python整数,对应MCU的int32累加。
  • 极端输入被裁剪后误差明显增加。

成功标准

  1. 能用scale和zero point完成量化与反量化。
  2. 能区分权重量化和全整数推理。
  3. 效果、资源和饱和统计同时验收。

失败时从哪里查起

INT8输出异常

现象 原因 检查
输出全为边界值 输入缩放或校准范围错 统计q=-128/127比例
分类索引正确但分数怪 输出未反量化 读取输出scale/zp
模型仍含浮点算子 转换未限制INT8集合 检查算子列表和I/O dtype

理解这段手工计算后,再阅读CMSIS-NN或TFLM内核中的乘法器、移位和饱和会更容易。

把实验迁移到真实MCU项目

TFLM固件直接向int8输入张量写码,量化前先执行完整物理量和标准化链。记录-128与127占比,现场饱和增多时触发输入漂移。

输出若用于阈值,可以反量化到浮点,也可以把阈值转换为整数码;两种方式必须使用输出张量自己的scale与zero point。

整数内核的最终输出还会经历定点乘法器、右移、舍入和饱和。手工浮点恢复用于理解尺度,若要逐码复现TFLM,需要从模型量化参数生成与内核相同的定点乘法器,并用边界向量验证。

还要检查类别阈值位于哪个数值域。若验证集阈值是浮点概率,固件使用整数输出时先按输出scale映射,并对阈值前后一个整数码做边界测试。

整数模型的测试向量要覆盖零点附近、正负最大常见值和饱和边界。对每条向量同时保存浮点输入、量化输入、浮点输出和整数输出,才能区分模型误差与固件量化错误。

模型文件缩小比例与固件Flash缩小比例通常不同,因为运行时和业务代码不随权重位宽等比例变化。资源报告应把两者分栏。

把结果再向前推进一步

  1. 计算600元素FP32与INT8输入张量的字节差异。
  2. 说明为什么偏置常用int32。
  3. 为真实输入增加饱和比例日志。

**收束:**INT8量化可能只改变权重,也可以贯穿输入、激活和输出。MCU项目需要明确使用全整数链路,并以模型检查和板端测量验证。

参考资料:

Google AI Edge:构建并转换微控制器模型

Google AI Edge:tf.lite API

Arm CMSIS-NN 官方文档

相关推荐
别催小唐敲代码15 分钟前
stm32_mpu6050_滤波教程
stm32·单片机·嵌入式硬件
满怀冰雪34 分钟前
23-PaddleClas 数据集、配置文件与训练参数详解
人工智能·python·深度学习·paddlepaddle
手写码匠41 分钟前
华为云Flexus+DeepSeek征文|华为云MaaS DeepSeek推理服务 × Flexus云服务器 × Dify一键部署:性能评测实战
人工智能·深度学习·算法·aigc
一条破秋裤1 小时前
STM32 学习笔记:GPIO 输出实验——LED 闪烁、流水灯与蜂鸣器
笔记·stm32·学习
十三画者2 小时前
【文献分享】ExoFILT:基于深度学习的外泌事件单颗粒追踪数据分类器
人工智能·深度学习·机器学习·数据挖掘·数据分析
冰蓝蓝2 小时前
主流强化学习框架全面解析:从经典RL到大模型RLHF实践
深度学习
梦想的颜色2 小时前
【AI科普】什么是计算机视觉:硬核科普,它和大 AI 大模型到底是什么关系
人工智能·深度学习·计算机视觉·多模态·aiagent·#vlm·ai工程实战
whitelbwwww2 小时前
RKNN静态量化
人工智能·深度学习
zxsz_com_cn2 小时前
深度学习在剩余寿命预测(RUL)中的应用综述
深度学习·工业4.0·预测性维护·剩余寿命·rul