近年来,倾向于开发更复杂的深度学习模型从而提高模型的准确性,但这也导致了模型计算资源消耗和广泛可用性的问题,因为我们不能在资源受限的设备(如移动设备和嵌入式设备)上使用如此巨大的模型。这是否意味我们必须使用更轻量化的模型,即使精度有所下降?是否有可能在智能手机或树莓派(Raspberry Pi)甚至微控制器等设备上部署这些复杂的模型呢?
使用TensorFlow Lite优化模型 是这些问题的答案。这篇博文将全面介绍TF Lite。您将学习各种支持的模型优化,并分析优化后的模型在边缘设备上的性能。本博客是TensorFlow Lite系列的第一篇。
1. TensorFlow Lite
TensorFlow Lite(缩写TF Lite)是一个开源的跨平台框架,通过使模型运行在移动、嵌入式和物联网设备上,提供设备端的机器学习。
有两种方法可以生成TensorFlow Lite模型:
- 将TensorFlow模型转换为TensorFlow Lite模型。
- 从头开始创建TensorFlow Lite模型。
我们将主要关注将TensorFlow模型转换为TensorFlow Lite模型。
2. TensorFlow Lite的优势
使用TensorFlow Lite模型进行设备上机器学习的优点:
- 低延迟:由于在边缘端进行推断,由于不需要从服务器往返,因此延迟很低。
- 数据隐私:由于模型在边缘端推理,数据不跨网络进行共享。因此,个人信息不会离开设备,解决任何有关数据隐私的问题。
- 网络连接:由于不需要互联网连接,所以不存在网络连接问题。
- 模型大小:TensorFlow Lite模型是轻量级的,特别合适边缘端。
- 低功耗:高效推理和和不需要网络连接,从而具有低功耗的特点。
3.将TensorFlow模型转换为TensorFlow Lite模型
TF Lite Converter 将TensorFlow 模型转换为TF Lite 模型。TF Lite模型以一种特殊的高效可移植格式表示,这种格式称为FlatBuffers ,具有.tflite 文件扩展名。相比于TensorFlow的 protocol buffer 模型格式具有多个优势,比如减小了模型大小和更快的推理速度,它使TF Lite能够在计算和内存资源有限的设备上高效地执行。
优化模型有几种方法。TF Lite目前只支持量化技术,不需要任何外部库/依赖。目前TFLite支持的量化类型有:
- 1.Float-16 量化
- Integer 量化
- Dynamic Range 量化
在深入研究这些技术并获得转换后的TF Lite模型之前,让我们首先训练一个基础模型。下载代码
4. Base Model Training
我们训练一个简单的图像分类器,将图像分类为猫或狗,将这个作为一个基础模型。我们将优化这个模型,并将结果与使用不同技术进行比较。
导入库
import os
import numpy as np
import tensorflow as tf
from tensorflow import keras
import tensorflow_datasets as tfds
from tensorflow.keras.models import Model
import tensorflow_model_optimization as tfmot
from tensorflow.keras.layers import Dropout, Dense, BatchNormalization
%load_ext tensorboard
4.1准备数据集
我们可以直接从TensorFlow数据集(tfds) 导入数据集。在这里,我们将数据集划分为训练集、验证集和测试集,分割比例为0.7:0.2:0.1 。as_supervised 参数保持True ,因为我们需要图像的标签进行分类。
(train_ds, val_ds, test_ds), info = tfds.load('cats_vs_dogs', split=['train[:70%]', 'train[70%:90%]', 'train[90%:]'], shuffle_files=True, as_supervised=True, with_info=True)
我们看一看tfds.info() 中提供的数据集信息。数据集有两个类,分别是“猫”和“狗”,有16283、4653、2326张训练、验证和测试图像。
print("Number of Classes: " + str(info.features['label'].num_classes))
print("Classes : " + str(info.features['label'].names))
NUM_TRAIN_IMAGES = tf.data.experimental.cardinality(train_ds).numpy()
print("Training Images: " + str(NUM_TRAIN_IMAGES))
NUM_VAL_IMAGES = tf.data.experimental.cardinality(val_ds).numpy()
print("Validation Images: " + str(NUM_VAL_IMAGES))
NUM_TEST_IMAGES = tf.data.experimental.cardinality(test_ds).numpy()
print("Testing Images: " + str(NUM_TEST_IMAGES))
Output:
Number of Classes: 2
Classes : ['cat', 'dog']
Training Images: 16283
Validation Images: 4653
Testing Images: 2326
函数tfds.visualization.show_examples()显示图像及其对应的标签。当我们想要在一行代码中可视化一些图像时,它非常方便!
vis = tfds.visualization.show_examples(train_ds, info)
设置16作为批大小,224×224作为图像大小,以便能够有效地处理数据集。为了准备数据集,已经相应地调整了图像的大小。
batch_size = 16
img_size = [224, 224]
train_ds = train_ds.map(lambda x, y: (tf.image.resize(x, img_size), y))
val_ds = val_ds.map(lambda x, y: (tf.image.resize(x, img_size), y))
让我们确保使用buffered prefetching 来从磁盘生成数据。预取与训练步骤的预处理和模型执行重叠。这样做可以减少训练的步骤时间和提取数据所需的时间。
train_ds = train_ds.cache().batch(batch_size).prefetch(buffer_size=10)
val_ds = val_ds.cache().batch(batch_size).prefetch(buffer_size=10)
test_ds = test_ds.cache().batch(batch_size).prefetch(buffer_size=10)
为了将图像提供给TF Lite模型,我们需要提取测试图像及其标签。我们将把它们存储到变量中,并将它们提供给TF Lite进行评估。
test_images = []
test_labels = []
for image, label in test_ds.take(len(test_ds)).unbatch():
test_images.append(image)
test_labels.append(label)
4.2加载模型
为了进行图像分类,我们选择了在imagenet 数据集上预先训练的EfiicientNet B0 模型。effecentnets 是最先进的图像分类模型。它的表现明显优于其他ConvNets 。
让我们从tf.keras.applications() 中导入模型。最后一层通过设置include_top = False 来移除。我们已经设置了输入图像的大小为224×224像素,并保持池化层为GlobalMaxPooling2D。让我们加载模型并解冻所有层,使它们可训练。
efnet = tf.keras.applications.EfficientNetB0(include_top = False, weights ='imagenet', input_shape = (224, 224, 3), pooling = 'max')
for layer in efnet.layers:
set_trainable = True
添加一个全连接层给预训练模型并训练它。这一层为推理层。并添加Dropout和BatchNormalization以减少过拟合。
x = Dense(512, activation='relu')(efnet.output)
x = BatchNormalization()(x)
x = Dense(64, activation='relu')(x)
x = Dropout(0.2)(x)
predictions = Dense(2, activation='softmax')(x)
4.3模型的编译
我们准备编译模型。我们使用初始学习率为0.0001的Adam Optimizer ,sparse categorical cross-entropy 作为损失函数,精度作为度量特征。编译之后,我们查看模型。
model = Model(inputs=efnet.input, outputs=predictions)
model.compile(optimizer=tf.keras.optimizers.Adam(0.0001), loss =tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics = ["accuracy"])
model.summary()
Output:
=====================================================================Total params: 4,740,453
Trainable params: 4,697,406
Non-trainable params: 43,047
我们使用 Model Saving Callback和 Reduce LR Callback.
- 模型
Saving Callback 以最佳的验证精度保存模型 - 如果验证损失连续3次保持不变,则Reduce LR Callback将学习率降低0.1倍。
filepath = '/content/model.h5'
model_save = tf.keras.callbacks.ModelCheckpoint(
filepath,
monitor="val_accuracy",
verbose=0,
save_best_only=True,
save_weights_only=False,
mode="max",
save_freq="epoch")
reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='loss', factor=0.1, patience=3, verbose=1, min_delta=5*1e-3,min_lr =5*1e-9,)
callback = [model_save, reduce_lr]
4.4 模型的训练
调用方法model.fit()来训练模型并对模型进行15个epoch的训练。
model.fit(train_ds, epochs=15, steps_per_epoch=(len(train_ds)//batch_size), validation_data=val_ds, validation_steps=(len(val_ds)//batch_size), shuffle=False, callbacks=callback)
4.5评估模型
完成训练,检查模型在测试集中的性能。
_, baseline_model_accuracy = model.evaluate(test_ds, verbose=0)
print('Baseline test accuracy:', baseline_model_accuracy*100)
Output:
Baseline test accuracy: 98.53 %
5. 使用TF Lite进行训练后量化
量化的工作原理是降低用于表示模型参数的数字的精度 ,默认情况下这些参数是32位浮点数。量化可以获得更小的模型和更快的计算。以下量化类型在TensorFlow Lite中可用:
我们逐个探索不同类型的量化并比较它们的性能。
5.1 Float-16量化
在Float-16量化中,权值被转换为16位浮点值。这导致了模型尺寸的2倍减少。模型大小有了显著的减少,并使得对延迟和准确性的最小影响。
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_fp16_model = converter.convert()
with open('/content/fp_16_model.tflite', 'wb') as f:
f.write(tflite_fp16_model)
我们已经将Float 16量化传递给了 converter.target_spec.supported_type来指定量化的类型。为了获得模型精度,定义了evaluate()函数,它接受tflite模型并返回模型精度。
def evaluate(interpreter):
prediction= []
input_index = interpreter.get_input_details()[0]["index"]
output_index = interpreter.get_output_details()[0]["index"]
input_format = interpreter.get_output_details()[0]['dtype']
for i, test_image in enumerate(test_images):
if i % 100 == 0:
print('Evaluated on {n} results so far.'.format(n=i))
test_image = np.expand_dims(test_image, axis=0).astype(input_format)
interpreter.set_tensor(input_index, test_image)
interpreter.invoke()
output = interpreter.tensor(output_index)
predicted_label = np.argmax(output()[0])
prediction.append(predicted_label)
print('\n')
prediction = np.array(prediction)
accuracy = (prediction == test_labels).mean()
return accuracy
在测试集中查看通过FP-16 量化TF Lite模型性能。
interpreter = tf.lite.Interpreter('/content/fp_16_model.tflite')
interpreter.allocate_tensors()
test_accuracy = evaluate(interpreter)
print('Float 16 Quantized TFLite Model Test Accuracy:', test_accuracy*100)
print('Baseline Keras Model Test Accuracy:', baseline_model_accuracy*100)
Output:
Float 16 Quantized TFLite Model Test Accuracy: 98.58 %
Baseline Keras Model Test Accuracy: 98.53 %
5.2 Dynamic Range 量化
在Dynamic Range 量化中,权重被转换为8位精度。Dynamic Range 量化实现了模型尺寸的4倍减少。模型大小有了显著的减少,并确保对延迟和准确性的最小影响。
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()
with open('/content/dynamic_quant_model.tflite', 'wb') as f:
f.write(tflite_quant_model)
我们在测试数据集中评估这个TF Lite模型。
interpreter = tf.lite.Interpreter('/content/dynamic_quant_model.tflite')
interpreter.allocate_tensors()
test_accuracy = evaluate(interpreter)
print('Dynamically Quantized TFLite Model Test Accuracy:', test_accuracy*100)print('Baseline Keras Model Test Accuracy:', baseline_model_accuracy*100)
Output:
Dynamically Quantized TFLite Model Test Accuracy: 98.15 %
Baseline Keras Model Test Accuracy: 98.53 %
5.3 Integer量化
Integer量化是一种优化策略,它将32位浮点数(如权值和激活输出)转换为最接近的8位定点数。这导致了一个更小的模型和更快的推理速度,这对低功耗设备,如微控制器非常有价值
Integer量化需要一个代表性的数据集,也就是训练数据集中的一些图像,才能进行转换。
converter = tf.lite.TFLiteConverter.from_keras_model(model)
def representative_data_gen():
for input_value in tf.data.Dataset.from_tensor_slices(test_images).take(100):
yield [input_value]
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_data_gen
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.uint8
converter.inference_output_type = tf.uint8
int_quant_model = converter.convert()
with open('/content/int_quant_model.tflite', 'wb') as f:
f.write(int_quant_model)
评估测试数据集上获得的Integer Quantized TF Lite 模型。
interpreter = tf.lite.Interpreter('/content/int_quant_model.tflite')
interpreter.allocate_tensors()
test_accuracy = evaluate(interpreter)
print('Integer Quantized TFLite Model Test Accuracy:', test_accuracy*100)
print('Baseline Keras Model Test Accuracy:', baseline_model_accuracy*100)
Output:
Integer Quantized TFLite Model Test Accuracy: 92.82 %
Baseline Keras Model Test Accuracy: 98.53 %
6. TF Lite模型在树莓派上的性能评价
所有转换后的TF Lite模型的性能都在拥有4GB RAM的树莓派4上进行了评估。以下是性能结果。
6.1 测试精度
FP-16量化模型的精度略有提高,而 dynamic range量化模型的精度略有下降。在Interger量化模型的情况下,可以观察到一个非常大的降幅(~6%)。
6.2 Model 大小
dynamic range范围和Interger量化模型中,模型尺寸惊人地减少了10倍。在FP-16量化模型的情况下,模型尺寸减少了6倍。
6.3推理时间
从测试数据集中随机选取100幅图像,检查TF Lite模型的推断时间。所提到的推理时间是一个模型对100幅图像进行推理所花费的平均时间。
FP-16量化模型和dynamic range量化模型的推理速度比基准模型快2.5倍。而Interger量化模型的推理速度快3.5倍。
下载代码
|