TensorFlow Lite 是 TensorFlow 在移动和 IoT 等边缘设备端的解决方案,提供了 Java、Python 和 C++ API 库,可以运行在 Android、iOS 和 Raspberry Pi 等设备上。目前 TFLite 只提供了推理功能,在服务器端进行训练后,经过如下简单处理即可部署到边缘设备上。
个人使用总结:
我们可以通过以下两种方式将Tensorflow模型 转换成 TF Lite:
我们可以根据保存模型的方式来选择转换成TF lite的方式:
1、tf.lite.TFLiteConverter.from_saved_model()(推荐):转换 SavedModel
View Code
2、tf.lite.TFLiteConverter.from_keras_model():转换 Keras 模型
View Code
3、tf.lite.TFLiteConverter.from_concrete_functions():转换 具体函数
View Code
由于 TensorFlow 中 RNN API 的变体很多,我们的转换方式包括两个方面:
Tensorflow官方提供了一个一个非常棒的案例:Keras LSTM fusion Codelab.ipynb
TensorFlow Lite 推理通常遵循以下步骤:
import numpy as np
import tensorflow as tf
# 加载TFLite模型并分配张量
interpreter = tf.lite.Interpreter(model_path="converted_model.tflite")
interpreter.allocate_tensors()
input_details = interpreter.get_input_details() # 输入
output_details = interpreter.get_output_details() # 输出
input_shape = input_details[0]['shape'] # 获取输入的shape
input_data = np.array(np.random.random_sample(input_shape), dtype=np.float32)
interpreter.set_tensor(input_details[0]['index'], input_data) # 输入给模型
interpreter.invoke() # 运行
# 函数`get_tensor()`返回张量数据的副本
# 使用`tensor()`来获得一个指向这个张量的指针
output_data = interpreter.get_tensor(output_details[0]['index'])
print(output_data)
定义了Signature的TFLite 推理
View Code
Tensorflow Lite 和 Tensorflow Model Optimization Toolkit (Tensorflow模型优化工具包)提供了最小优化推理复杂性的工具,可将优化推断的复杂性降至最低。深度神经网络的量化使用了一些技术,这些技术可以降低权重的精确表示,并且降低存储和计算。 TensorFlow Model Optimization Toolkit 目前支持通过量化、剪枝和聚类进行优化。
剪枝:剪枝的工作原理是移除模型中对其预测影响很小的参数。剪枝后的模型在磁盘上的大小相同,并且具有相同的运行时延迟,但可以更高效地压缩。这使剪枝成为缩减模型下载大小的实用技术。
未来,TensorFlow Lite 将降低剪枝后模型的延迟。
聚类:聚类的工作原理是将模型中每一层的权重归入预定数量的聚类中,然后共享属于每个单独聚类的权重的质心值。这就减少了模型中唯一权重值的数量,从而降低了其复杂性。这样一来,就可以更高效地压缩聚类后的模型,从而提供类似于剪枝的部署优势。
开发工作流程
有两种形式的量化:训练后量化和量化感知训练。请从训练后量化开始,因为它更易于使用,尽管量化感知训练在模型准确率方面的表现通常更好。
等我将代码进行了训练后量化后,再来整这个量化感知训练
量化的工作原理是降低模型参数的精度(默认情况为 32 位浮点数)。这样可以获得较小的模型大小和较快的计算速度。TensorFlow Lite 提供以下量化类型:
技术 | 数据要求 | 大小缩减 | 准确率 |
---|---|---|---|
训练后 Float16 量化 | 无数据 | 高达 50% | 轻微的准确率损失 |
训练后 动态范围量化 | 无数据 | 高达 75%,速度加快 2-3倍 | 极小的准确率损失 |
训练后 int8 量化 | 无标签的代表性样本 | 高达 75%,速度加快3+倍 | 极小的准确率损失 |
量化感知训练 | 带标签的训练数据 | 高达 75% | 极小的准确率损失 |
以下决策树可帮助您仅根据预期的模型大小和准确率来选择要用于模型的量化方案。
权重(float32) 会在训练后量化为 整型(int8),激活会在推断时动态量化,模型大小缩减至原来的四分之一:
TFLite 支持对激活进行动态量化(激活始终以浮点进行存储。对于支持量化内核的算子,激活会在处理前动态量化为 8 位精度,并在处理后反量化为浮点精度。根据被转换的模型,这可以提供比纯浮点计算更快的速度)以实现以下效果:
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
# 启动默认的 optimizations 来量化所有固定参数(权重)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()
此优化提供的延迟接近全定点推断。但是,输出仍使用浮点进行存储,因此使用动态范围算子的加速小于全定点计算。
我们来吃一个完整的栗子,构建一个MNIST模型,并且对它使用动态范围量化,对比量化前后的精度变化:
View Code
更多细节请参考:Tensorflow官方文档 训练后动态范围量化
整型量化是将32位浮点数 转换为8位定点数。这样可以缩减模型大小并加快推理速度,这对低功耗设备(如微控制器)很有价值。仅支持整数的加速器(如 Edge TPU)也需要使用此数据格式。
对于全整数量化,需要校准或估算模型中所有浮点张量的范围,即 (min, max)。与权重和偏差等常量张量不同,模型输入、激活(中间层的输出)和模型输出等变量张量不能校准,除非我们运行几个推断周期。因此,转换器需要一个有代表性的数据集来校准它们。这个数据集可以是训练数据或验证数据的一个小子集(大约 100-500 个样本)。请参阅下面的 representative_dataset() 函数。
从 TensorFlow 2.7 版本开始,您可以通过签名指定代表数据集,示例如下:
View Code
如果给定的 TensorFlow 模型中有多个签名,则可以通过指定签名密钥来指定多个数据集:
View Code
您可以通过提供输入张量列表来生成代表性数据集:
View Code
从 TensorFlow 2.7 版本开始,我们推荐使用基于签名的方法,而不是基于输入张量列表的方法,因为输入张量排序可以很容易地翻转。
出于测试目的,您可以使用如下所示的虚拟数据集:
View Code
1、模型整型量化,输入输出还是浮点
启动默认的 optimizations 只能量化固定参数(如:权重),但是模型输入/输出 和层之间的状态值 依然是浮点型的,要量化可变中间值,您需要提供 RepresentativeDataset。这是一个生成器函数,它提供一组足够大的输入数据来代表典型值。converter 可以通过该函数估算所有可变数据的动态范围(相比训练或评估数据集,此数据集不必唯一)为了支持多个输入,每个代表性数据点都是一个列表,并且列表中的元素会根据其索引被馈送到模型。
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 启动默认的 optimizations 来量化所有固定参数(权重)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
# 要量化可变数据(模型输入/输出 和层之间的中间体),提供 RepresentativeDataset,来估算所有可变数据的动态范围
converter.representative_dataset = representative_data_gen
tflite_model_quant = converter.convert()
现在,所有权重和可变数据都已量化,并且与原始 TensorFlow Lite 模型相比,该模型要小得多。
但是,为了与传统上使用浮点模型输入和输出张量的应用保持兼容,TensorFlow Lite 转换器将模型的输入和输出张量保留为浮点,这通常对兼容性有利,但它无法兼容执行全整形运算的设备(如 Edge TPU)。
interpreter = tf.lite.Interpreter(model_content=tflite_model_quant)
input_type = interpreter.get_input_details()[0]['dtype']
print('input: ', input_type) #
output_type = interpreter.get_output_details()[0]['dtype']
print('output: ', output_type) #
2、全整型量化
为了量化输入和输出张量,我们需要使用一些附加参数再次转换模型:
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT] # 先启动默认的optimizations将模型权重进行量化
converter.representative_dataset = representative_data_gen # 使用代表数据集量化模型中间值
# 如果有任何的 ops不能量化,converter 将抛出错误
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
# 将输入和输出tensors设置为int8 类型
converter.inference_input_type = tf.int8 # or tf.uint8
converter.inference_output_type = tf.int8 # or tf.uint8
tflite_model_quant = converter.convert()
现在我们可以看到输入和输出张量现在是整数格式:
interpreter = tf.lite.Interpreter(model_content=tflite_model_quant)
input_type = interpreter.get_input_details()[0]['dtype']
print('input: ', input_type) #
output_type = interpreter.get_output_details()[0]['dtype']
print('output: ', output_type) #
我们来整一个栗子,您将从头开始训练一个 MNIST 模型、将其转换为 TensorFlow Lite 文件,并使用训练后整型量化。最后,将检查转换后模型的准确率并将其与原始浮点模型进行比较。
View Code
更多细节请参考:Tensorflow官方文档训练后整型量化
现在,TensorFlow Lite 支持在模型从 TensorFlow 转换到 TensorFlow Lite FlatBuffer 格式期间将权重转换为 16 位浮点值。这样可以将模型的大小缩减至原来的二分之一。某些硬件(如 GPU)可以在这种精度降低的算术中以原生方式计算,从而实现比传统浮点执行更快的速度。可以将 Tensorflow Lite GPU 委托配置为以这种方式运行。但是,转换为 float16 权重的模型仍可在 CPU 上运行而无需其他修改:float16 权重会在首次推理前上采样为 float32。这样可以在对延迟和准确率造成最小影响的情况下显著缩减模型大小。
float16 量化的优点如下:
float16 量化的缺点如下:
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_quant_model = converter.convert()
在本教程中,您将从头开始训练一个 MNIST 模型,并在 TensorFlow 中检查其准确率,然后使用 float16 量化将此模型转换为 Tensorflow Lite FlatBuffer 格式。最后,检查转换后模型的准确率,并将其与原始 float32 模型进行比较。
View Code
仅整数:具有 8 位权重的 16 位激活(实验性)
这是一个实验性量化方案。它与“仅整数”方案类似,但会根据激活的范围将其量化为 16 位,权重会被量化为 8 位整数,偏差会被量化为 64 位整数。这被进一步称为 16x8 量化。
这种量化的主要优点是可以显著提高准确率,但只会稍微增加模型大小。
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]
tflite_quant_model = converter.convert()
如果模型中的部分算子不支持 16x8 量化,模型仍然可以量化,但不受支持的算子会保留为浮点。要允许此操作,应将以下选项添加到 target_spec 中。
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8,
tf.lite.OpsSet.TFLITE_BUILTINS]
tflite_quant_model = converter.convert()
这种量化的缺点是:
注:这是一项实验性功能。
可以在此处找到该量化模型的教程。
模型准确率
由于权重是在训练后量化的,因此可能会造成准确率损失,对于较小的网络尤其如此。TensorFlow Hub 为特定网络提供了预训练的完全量化模型。请务必检查量化模型的准确率,以验证准确率的任何下降都在可接受的范围内。有一些工具可以评估 TensorFlow Lite 模型准确率。
另外,如果准确率下降过多,请考虑使用量化感知训练。但是,这样做需要在模型训练期间进行修改以添加伪量化节点,而此页面上的训练后量化技术使用的是现有的预训练模型。