模型转换基础:使用 TFLiteConverter 将 SavedModel 或 Keras 模型转换为 .tflite 格式

好,咱们今天聊聊模型转换。这是把训练好的模型搬到移动端的第一步,也是很多人容易踩坑的地方。

说白了,TensorFlow 训练出来的模型是给服务器用的,体积大、依赖多。而 .tflite 是专门为手机、嵌入式设备设计的轻量格式。怎么把前者变成后者?靠的就是 TFLiteConverter

转换前的准备工作

动手之前,先确认两件事:

  • 你的模型是 Keras 的 .h5.keras 格式
  • 或者是 SavedModel 格式(一个文件夹,里面有 saved_model.pb

我个人习惯用 Keras 格式,因为代码更简洁。但如果你从 TF Hub 下载模型,基本都是 SavedModel。

小提示: 转换前最好先跑一遍推理,确认模型能正常加载。我在项目中遇到过好几次,模型训练时好好的,加载时却报错——原来是保存路径里带了中文。

方法一:从 Keras 模型转换

这是最常用的方式。假设你有一个训练好的分类模型:

import tensorflow as tf

# 加载 Keras 模型
model = tf.keras.models.load_model('my_model.h5')

# 创建转换器
converter = tf.lite.TFLiteConverter.from_keras_model(model)

# 执行转换
tflite_model = converter.convert()

# 保存到文件
with open('model.tflite', 'wb') as f:
    f.write(tflite_model)

嗯,就这么几行代码。但注意,converter.convert() 返回的是字节数据,不是文件对象。你得自己写文件。

方法二:从 SavedModel 转换

如果你拿到的是 SavedModel 格式:

converter = tf.lite.TFLiteConverter.from_saved_model('saved_model_dir')
tflite_model = converter.convert()

这里有个坑——SavedModel 目录里可能包含多个签名(SignatureDef)。默认情况下,转换器会选第一个。如果你想指定某个签名,可以传 signature_keys 参数。

注意: 我曾经遇到一个模型,转换后推理结果全错。查了半天才发现,SavedModel 里有两个签名,转换器选了训练用的那个,而不是推理用的。所以,转换后一定要验证结果。

量化:让模型更小更快

直接转换出来的 .tflite 模型,精度还是 float32。对于移动端来说,这太大了。我们通常要做量化。

最常用的是 动态范围量化

converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

这样模型大小能减少约 4 倍,推理速度提升 2-3 倍。精度损失?一般不到 1%。

如果你需要更极致的压缩,可以用 全整数量化。但这需要校准数据:

def representative_dataset():
    for _ in range(100):
        data = np.random.randn(1, 224, 224, 3).astype(np.float32)
        yield [data]

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_quant_model = converter.convert()
重要: 全整数量化后,输入输出都是 int8 类型。你的移动端代码需要做相应的反量化处理。否则,你输入 float 数据,模型会直接报错。

验证转换结果

转换完别急着部署。先跑一下推理,看看结果对不对:

# 加载 TFLite 模型
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()

# 获取输入输出张量
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

# 准备测试数据
test_input = np.random.randn(1, 224, 224, 3).astype(np.float32)

# 推理
interpreter.set_tensor(input_details[0]['index'], test_input)
interpreter.invoke()
output = interpreter.get_tensor(output_details[0]['index'])

print('推理结果:', output)

对比一下原始模型的输出。如果差异在可接受范围内,那就没问题。

常见问题与避坑

问题 原因 解决方法
转换时报错 Unsupported ops 模型里用了 TFLite 不支持的算子 启用 converter.target_spec.supported_ops 添加 Flex 算子
转换后模型变大 没有启用量化 添加 converter.optimizations = [tf.lite.Optimize.DEFAULT]
推理结果全为 0 输入数据没有归一化 检查输入范围,TFLite 模型通常期望 [0,1] 或 [-1,1]
转换速度很慢 模型太大或校准数据集太大 减少校准数据量,100 张图片通常足够
我的经验: 如果你用 Flex 算子,TFLite 模型会依赖 TensorFlow 运行时。这在 Android 上没问题,但 iOS 上会增大包体积。尽量用 TFLite 原生算子,实在不行再考虑 Flex。

总结一下

模型转换其实不复杂,核心就三步:

  1. 加载模型(Keras 或 SavedModel)
  2. 创建转换器并设置量化参数
  3. 保存 .tflite 文件

但细节决定成败。量化参数、输入输出类型、算子兼容性——这些都要心里有数。你想想看,如果转换后模型在手机上跑不起来,前面所有工作都白费了。

下一章咱们聊聊如何在 Android 上加载和运行这个 .tflite 模型。到时候我会分享一个我在项目里踩过的坑——关于内存泄漏的。