## 模型保存
```
export_dir = './layer0'
tf.saved_model.save(model,export_dir)
```
模型保存后的问题树结构如下图所示:
![](https://mythidea.oss-cn-beijing.aliyuncs.com/image-20230521184731004.png)
## 模型转换
```
converter = tf.lite.TFLiteConverter.from_saved_model(export_dir)
tflite_model = converter.convert()
import pathlib
tflite_model_file = pathlib.Path('./model.tflite')
tflite_model_file.write_bytes(tflite_model)
```
模型转换后生成`model.tflite`。
使用 [`tf.lite.TFLiteConverter`](https://tensorflow.google.cn/api_docs/python/tf/lite/TFLiteConverter?hl=zh-cn) 转换 TensorFlow 2.x 模型。TensorFlow 2.x 模型是使用 SavedModel 格式存储的,并通过高阶 `tf.keras.*` API(Keras 模型)或低阶 `tf.*` API(用于生成具体函数)生成。
以下是将 [Keras](https://tensorflow.google.cn/guide/keras/overview?hl=zh-cn) 模型转换为 TensorFlow Lite 模型。
```python
import tensorflow as tf
# Create a model using high-level tf.keras.* APIs
model = tf.keras.models.Sequential([
tf.keras.layers.Dense(units=1, input_shape=[1]),
tf.keras.layers.Dense(units=16, activation='relu'),
tf.keras.layers.Dense(units=1)
])
model.compile(optimizer='sgd', loss='mean_squared_error') # compile the model
model.fit(x=[-1, 0, 1], y=[-3, -1, 1], epochs=5) # train the model
# (to generate a SavedModel) tf.saved_model.save(model, "saved_model_keras_dir")
# Convert the model.
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
# Save the model.
with open('model.tflite', 'wb') as f:
f.write(tflite_model)
```
`
## 模型推断
TensorFlow Lite 解释器(interpreter)是一个库(library),它接收一个模型文件(model file),执行模型文件在输入数据(input data)上定义的运算符(operations),并提供对输出(output)的访问。
```
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
input_det = interpreter.get_input_details()
output_det = interpreter.get_output_details()
```
输入1个数进行推断。
```
to_pre = np.array([[1.5]]).astype(np.float32)
interpreter.set_tensor(input_det[0]['index'],to_pre)
interpreter.invoke()
tflite_ret = interpreter.get_tensor(output_det[0]['index'])
```
模型转换后生成`model.tflite`。
## 模型优化
将优化推断的复杂性降至最低,降低权重的精确表示,并且可选的降低存储和计算的激活值。
优化可能会导致模型准确率发生变化,这在应用开发过程中必须予以考虑。
准确率的变化取决于被优化的单个模型,而且很难提前预测。一般来说,针对大小或延迟进行优化的模型会损失少量准确率。根据您应用的不同,这可能会或可能不会影响您的用户体验。在极少数情况下,某些模型可能会因为优化过程而获得准确性的小幅提升。
### 量化
[量化](https://tensorflow.google.cn/model_optimization/guide/quantization/post_training?hl=zh-cn)的工作原理是降低用于表示模型参数的数字(默认情况为 32 位浮点数)的精度。这样可以获得较小的模型大小和较快的计算速度。
[使用 int16 激活的量化](https://tensorflow.google.cn/model_optimization/guide/quantization/post_training?hl=zh-cn)是一个具有 int16 激活和 int8 权重的全整数量化方案。与激活和权重均为 int8 的全整数量化方案相比,这种模式可以提高量化模型的准确率,并保持相似的模型大小。建议在激活对量化敏感时使用。
#### 训练后量化
![](https://pic3.zhimg.com/80/v2-163a4ae526eebcc2097f3b4a6e486b9e_720w.webp)
##### 量化权重
权重可能会转换为精度降低的类型,例如 16 位浮点数或 8 位整数。我们通常建议将 16 位浮点数用于 GPU 加速,而将 8 位整数用于 CPU 执行。
```python
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()
```
**权重量化为float16,其他为float32**。
##### 权重和激活的全整数量化
通过确保量化权重和激活,可以改善延迟、处理时间和功耗,并访问仅支持整数的硬件加速器。这需要一个较小的代表性数据集,通过`representative_dataset_gen`估算所有可变数据的动态范围。
```python
import tensorflow as tf
def representative_dataset_gen():
for _ in range(num_calibration_steps):
# Get sample input data as a numpy array in a method of your choosing.
yield [input]
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset_gen
tflite_quant_model = converter.convert()
```
**权重和激活量化为float16,其他为float32**,生成的模型仍采用浮点输入和输出。
##### 整数量化
将 32 位浮点数(如权重和激活输出)转换为 8 位定点数。
```python
converter = tf.lite.TFLiteConverter.from_keras_model(model)
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
tflite_model_int8_quant = converter.convert()
interpreter = tf.lite.Interpreter(model_content=tflite_model_int8_quant)
input = interpreter.get_input_details()
output = interpreter.get_output_details()
```
该模型使用整数数据作为模型的输入和输出张量,因此它兼容仅支持整数的硬件。
##### float16 量化
将权重转换为 16 位浮点值。
```python
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_model_float16_quant = converter.convert()
tflite_model_float16_quant_file = tflite_models_dir/"mnist_model_float16_quant.tflite"
tflite_model_float16_quant_file.write_bytes(tflite_model_float16_quant)
interpreter_fp16 = tf.lite.Interpreter(model_path=str(tflite_model_float16_quant_file))
interpreter_fp16.allocate_tensors()
```
#### 量化张量的表示
8 位量化近似于使用以下公式得到的浮点值。
$real_value = (int8_value - zero_point) \times scale$
该表示包含两个主要部分:
- 由 int8 补码值表示的逐轴(即逐通道)或逐张量权重,范围为 [-127, 127],零点等于 0。
- 由 int8 补码值表示的按张量激活/输入,范围为 [-128, 127],零点范围为 [-128, 127]。
### 剪枝
[剪枝](https://tensorflow.google.cn/model_optimization/guide/pruning?hl=zh-cn)的工作原理是移除模型中对其预测影响很小的参数。剪枝后的模型在磁盘上的大小相同,并且具有相同的运行时延迟,但可以更高效地压缩。这使剪枝成为缩减模型下载大小的实用技术。
模型剪枝有两种方式,剪神经元,或者剪权重(不破坏原来的网络结构)。剪神经元对模型的影响较大,剪权重对模型的精度影响较小。
一个简单且实践有效的思路是:magnitude-based weight pruning,即按照参数(或特征输出)绝对值大小来评估重要性,《Pruning Filters for Efficient ConvNets》
基于上述这个简单有效的思路,TensorFlow Model Optimization工具包,直接提供了prune_low_magnitude API函数来实现。
#### 微调预训练模型和剪枝
**Fine-tune pre-trained model with pruning**
```
batch_size = 128
epochs = 2
validation_split = 0.1
num_img = test_img.shape[0]*(1-validation_split)
end_step = np.ceil(num_img/batch_size).astype(np.int32)*epochs
```
```python
import tensorflow_model_optimization as tfopt
prune_low_magnitude = tfopt.sparsity.keras.prune_low_magnitude
pruning_params = {
'pruning_schedule':tfopt.sparsity.keras.PolynomialDecay(
initial_sparsity=0.5,
final_sparsity=0.8,
begin_step=0,
end_step=end_step
)
}
model_for_pruning = prune_low_magnitude(model,**pruning_params)
```
`prune_low_magnitude`修改要在训练期间修剪的tf.keras图层或模型,剪的是权重,对网络构架无影响。
`PolynomialDecay`提供学习率按多项式衰减的策略。
- 以稀疏性为initial_sparsity,到达到稀疏性为final_sparsity结束。多少稀疏就代表着多少权重将会消失(变成0)。
- 以begin_step为开始,到end_step这个期间,每隔frequency的steps,就修剪一次模型。
- power是多项式衰减系数
按多项式衰减的方式进行权重剪枝。经过剪枝后的参数量多了Non-trainable params参数,这些是不可训练的参数。是tensorflow-model-optimization为网络中的每个权重添加的不可训练掩码,表示是否要修剪该权重,掩码为0或1。
```python
callbacks = [
tfopt.sparsity.keras.UpdatePruningStep(),
tfopt.sparsity.keras.PruningSummaries(log_dir=str(logdir))
]
```
UpdatePruningStep回调,使其在训练过程中处理修剪更新。
PruningSummaries提供用于跟踪进度和调试的日志。
在修剪完模型后,我们需要使用strip_pruning来删除Non-trainable params。使用标准压缩算法对比前后模型的变化。
```python
model_for_export = tfopt.sparsity.keras.strip_pruning(model_for_pruning)
```
### 聚类
[聚类](https://tensorflow.google.cn/model_optimization/guide/clustering?hl=zh-cn)的工作原理是将模型中每一层的权重归入预定数量的聚类中,然后共享属于每个单独聚类的权重的质心值。这就减少了模型中唯一权重值的数量,从而降低了其复杂性,从而带来部署优势。
聚类首先将每层的权重分组成 N 个聚类,然后共享属于相应聚类的所有权重的聚类形心值。
使用cluster_weights()应用于整个预训练模型,压缩后可有效缩减模型大小,还能保持良好的准确率。
```python
cluster_weights = tfopt.clustering.keras.cluster_weights
centroinit = tfopt.clustering.keras.CentroidInitialization
clustering_params = {
'number_of_clusters':16,
'cluster_centroids_init':centroinit.LINEAR
}
cluster_model = cluster_weights(model,**clustering_params)
opt = tf.keras.optimizers.Adam(learning_rate=1e-5)
cluster_model_for_export = tfopt.clustering.keras.strip_clustering(cluster_model)
converter = tf.lite.TFLiteConverter.from_keras_model(cluster_model_for_export)
tflite_cluster_model = converter.convert()
cluster_tflite_file = keras_dir/'mnist_model_cluster.tflite'
cluster_tflite_file.write_bytes(tflite_cluster_model)
```
聚类方法使用CentroidInitialization(中心初始化),使用strip_pruning来删除Non-trainable params,最后转换成tflite模型。
再经过量化处理,继续压缩。
```python
converter = tf.lite.TFLiteConverter.from_keras_model(cluster_model_for_export)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
cluster_quant_model = converter.convert()
cluster_quant_tflite_file = keras_dir/'mnist_model_cluster_quant.tflite'
cluster_quant_tflite_file.write_bytes(cluster_quant_model)
interpreter = tf.lite.Interpreter(model_content=cluster_quant_model)
interpreter.allocate_tensors()
cluster_test_acc = evaluate_model(interpreter)
```
### 协作优化
优化方式示意图如下。
![](https://tensorflow.google.cn/static/model_optimization/guide/combine/images/collaborative_optimization.png)
优化结果如下图所示。
![](https://tensorflow.google.cn/static/model_optimization/guide/combine/images/collaborative_optimization_dist.png)