2. 模型转换与量化基础
任务目标
本案例完成一个最小可复现的模型转换闭环:训练一个简单的Keras分类模型 → 转换为TFLite格式 → 应用动态范围量化 → 生成适用于STM32工程的C数组文件 → 验证转换前后模型输出一致性。
环境依赖
- Python 3.8+,TensorFlow 2.12+(含Keras)
- NumPy
- 无需额外硬件,纯PC环境即可完成
完整实现代码
import numpy as np
import tensorflow as tf
from tensorflow import keras
# 1. 构造并训练一个极简Keras模型(用于演示转换流程)
model = keras.Sequential([
keras.layers.Dense(8, activation='relu', input_shape=(4,)),
keras.layers.Dense(3, activation='softmax')
])
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
# 生成固定随机种子的小型训练数据(保证可复现)
np.random.seed(42)
x_train = np.random.rand(200, 4).astype(np.float32)
y_train = keras.utils.to_categorical(np.random.randint(0, 3, 200), 3)
model.fit(x_train, y_train, epochs=3, verbose=0)
# 2. 转换为TFLite格式(float32基线)
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
# 3. 应用动态范围量化(权重从float32量化为int8,激活仍为float32)
converter_quant = tf.lite.TFLiteConverter.from_keras_model(model)
converter_quant.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter_quant.convert()
# 4. 生成C数组文件(供STM32工程直接包含)
def generate_c_array(model_data, filename, array_name):
hex_str = ', '.join(f'0x{b:02x}' for b in model_data)
with open(filename, 'w') as f:
f.write(f'const unsigned char {array_name}[] = {{\n{hex_str}\n}};\n')
f.write(f'const unsigned int {array_name}_len = {len(model_data)};\n')
generate_c_array(tflite_model, 'model_float32.h', 'g_model_float32')
generate_c_array(tflite_quant_model, 'model_quant.h', 'g_model_quant')
# 5. 验证:比较原始Keras模型与两个TFLite模型的输出
def run_tflite_inference(interpreter, input_data):
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
return interpreter.get_tensor(output_details[0]['index'])
# 固定测试输入
test_input = np.array([[0.1, 0.5, 0.3, 0.9]], dtype=np.float32)
# Keras模型输出
keras_output = model.predict(test_input, verbose=0)
# Float32 TFLite输出
interp_f32 = tf.lite.Interpreter(model_content=tflite_model)
interp_f32.allocate_tensors()
tflite_f32_output = run_tflite_inference(interp_f32, test_input)
# 量化TFLite输出
interp_quant = tf.lite.Interpreter(model_content=tflite_quant_model)
interp_quant.allocate_tensors()
tflite_quant_output = run_tflite_inference(interp_quant, test_input)
# 打印对比结果
print("Keras输出: ", keras_output[0])
print("TFLite f32输出:", tflite_f32_output[0])
print("TFLite量化输出:", tflite_quant_output[0])
print("f32最大误差:", np.max(np.abs(keras_output[0] - tflite_f32_output[0])))
print("量化最大误差:", np.max(np.abs(keras_output[0] - tflite_quant_output[0])))
运行方式
- 将上述代码保存为
convert_tflite.py - 执行
python convert_tflite.py - 运行结束后,当前目录生成
model_float32.h和model_quant.h两个C头文件
验证方法与通过标准
- 文件生成验证:确认两个.h文件存在且非空,文件末尾包含对应的
_len变量定义。 - 输出一致性验证:程序打印的
f32最大误差应接近0(通常<1e-6),证明转换过程无损;量化最大误差通常<0.05,证明动态范围量化在可接受精度损失范围内。 - C数组可用性验证:在STM32工程中,通过
#include "model_quant.h"即可引用g_model_quant数组,配合TensorFlow Lite Micro解释器加载该模型。
关键说明
- 动态范围量化仅将权重从float32压缩为int8,激活值在推理时动态量化,因此无需校准数据集,适合快速部署。
- 若需进一步压缩为全整型量化(int8激活),需提供代表性数据集进行校准,本案例不涉及。
- 生成的C数组可直接用于STM32的TensorFlow Lite Micro框架,加载方式为
tflite::GetModel(g_model_quant)。