携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
在之前的文章中,我们学习了循环神经网络的数据padding、序列式问题、文本生成、LSTM长短期记忆网络、subword文本分类
今天我们来介绍模型保存与部署。进行Keras模型转化为,签名函数转化为SavedModelSavedModel,签名函数、SavedModel和keras模型到具体函数转换方法的介绍
-
1.1 模型保存与部署
-
-
-
1.1.1 模型保存
-
文件格式
- Checkpoint与graphdef(tf1.0)
- keras(hdf5),SavedModel(tf2.0)
-
保存的是什么?
- 参数
- 参数+网络结构
-
1.1.2 TFLite
- TFLite Converter
- 模型转化
- TFLite Interpreter
- 模型加载
- 支持android与iOS设备
- 支持多种语言
- TFLite Converter
-
1.1.3 TFLite - FlatBuffer
- Google开源的跨平台数据库序列化库,可以很方便的给模型做序列化和反序列化,非常高效
- 优点:
- 直接读取序列化数据
- 高效的内存使用和速度,无需占用额外内存
- 灵活,数据前后向兼容,灵活控制数据结构
- 使用少量的代码即可完成功能
- 强数据类型,易于使用
-
1.1.4 TFLite —— 量化
- 参数从float变成8-bit整数
- 模型准确率会有些许损失
- 模型大小变为原理啊四分之一
- 量化方法
- 真是float值 = (int值 - 归零点) * 因子
- 参数从float变成8-bit整数
-
实战: keras-保存参数与保存模型+参数
keras, 签名函数到SavedModel
keras,SavedModel,签名函数到具体函数
keras,SavedModel,具体函数到tflite
Tflite 量化
Tensorflow js部署模型
Android部署模型
-
-
2.1 保存模型构建加参数与保存参数
- 使用ModelCheckpoint中的save_weights_only = False,:True是只保存参数,默认情况(False)是保存结构加参数
-
2.2 Keras模型转化为SavedModel
- 这个实战主要介绍使用saved_model将keras模型保存成SavedModel模型,介绍!saved_model_cli工具,在保存的模型中有一个最重要的信息就是签名信息,在签名信息中定义了输出和输出格式是什么,还介绍如何在程序中将模型导入尽力啊
- 使用fashion_mnist数据集训练一个模型,详细请见之前的模型归一化处理,如果想保存成SaveModel格式只需要调用一个API即可
第一个参数是搭建好的模型,第二个参数是存储的路径
tf.saved_model.save(model, "./keras_saved_graph")
可以看到,保存好的文件夹由三个,其中对我们有用的就是saved_model.pb
对savemodel存储好之后,我们可以通过一个工具来查看——saved_model_cli,因为这是一个命令行工具,如果想在notebook里运行命令行工具的话,需要在前面加一个“!”
!saved_model_cli show --dir ./keras_saved_graph --all
我们还可以去指定不同的tag-set和 SignatureDefs
!saved_model_cli show --dir ./keras_saved_graph \
--tag_set serve --signature_def serving_default
运行结果:
The given SavedModel SignatureDef contains the following input(s):
inputs['flatten_input'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 28, 28)
name: serving_default_flatten_input:0
The given SavedModel SignatureDef contains the following output(s):
outputs['dense_2'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 10)
name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
这样的话,我们就会只打印出来serving_default的信息了, 而我们在SignatureDef中定义的信息的作用就在于我们如何把信息传给它,以及如何把信息取出来
我们还可以通过这个工具对SaveModel进行测试,--input_exprs之后可以输出python表达式
!saved_model_cli run --dir ./keras_saved_graph --tag_set serve \
--signature_def serving_default \
--input_exprs 'flatten_input=np.ones((2, 28, 28))'
运行结果:
[[0.07439692 0.12902586 0.06762592 0.04435977 0.1261992 0.01800182
0.22577088 0.04594292 0.26419848 0.00447821]
[0.07439692 0.12902586 0.06762592 0.04435977 0.1261992 0.01800182
0.22577088 0.04594292 0.26419848 0.00447821]]
这里可以发现,输出的是两个概率分布,这两个概率分布应该是一样的,因为我们输入的两个样本都是一样的,每个分布都有十个值,意味着在十类中的预测,通过这个工具就可以检查我们的SavedModel是不是正常可以工作的。
除了工具之外,我们还可以使用程序对它进行测试
loaded_saved_model = tf.saved_model.load('./keras_saved_graph')
print(list(loaded_saved_model.signatures.keys()))
运行结果:
['serving_default']
通过这个签名可以获得一个inference
inference = loaded_saved_model.signatures['serving_default']
print(inference)
ConcreteFunction signature_wrapper(*, flatten_input)
Args:
flatten_input: float32 Tensor, shape=(None, 28, 28)
Returns:
{'dense_2': <1>}
<1>: float32 Tensor, shape=(None, 10)
也可以查看outputs
print(inference.structured_outputs)
运行结果:
{'dense_2': TensorSpec(shape=(None, 10), dtype=tf.float32, name='dense_2')}
通过inference可以获得一个推理,输入是测试集中一个样本
results = inference(tf.constant(x_test_scaled[0:1])) print(results['dense_2'])
运行结果:
tf.Tensor(
[[3.94980771e-06 3.30364219e-06 3.71269880e-06 2.58380169e-06
1.66972529e-06 1.78842749e-02 1.42412055e-05 5.17248968e-03
2.42425900e-04 9.76671398e-01]], shape=(1, 10), dtype=float32)
-
2.3 签名函数转化为SavedModel
将Signature保存成SavedModel
# 保存成SavedModel
to_export = tf.Module()
to_export.cube = cube
tf.saved_model.save(to_export, "./signature_to_saved_model")
和2.2一样,也可以使用saved_model_cli进行查看
!saved_model_cli show --dir ./signature_to_saved_model --all
使用程序对它进行测试
imported = tf.saved_model.load('./signature_to_saved_model')
imported.cube(tf.constant([2]))
运行结果:
<tf.Tensor: shape=(1,), dtype=int32, numpy=array([8], dtype=int32)>
-
2.4 签名函数、SavedModel和keras模型到具体函数转换
把keras model转化为concrete_function
loaded_keras_model = keras.models.load_model(
'./graph_def_and_weights/fashion_mnist_model.h5')
loaded_keras_model(np.ones((1, 28, 28)))
# 把keras function用tf.function封装一遍
run_model = tf.function(lambda x : loaded_keras_model(x))
# get_concrete_function 来获得concrete_function,参数是定义的输入格式
keras_concrete_func = run_model.get_concrete_function(
tf.TensorSpec(loaded_keras_model.inputs[0].shape,
loaded_keras_model.inputs[0].dtype))
测试一下是否可以正常运行:
keras_concrete_func(tf.constant(np.ones((1, 28, 28), dtype=np.float32)))
运行结果:
<tf.Tensor: shape=(1, 10), dtype=float32, numpy=
array([[0.12054981, 0.13891368, 0.06380311, 0.02520141, 0.03194021,
0.01083281, 0.05653547, 0.145804 , 0.37682512, 0.02959438]],
dtype=float32)>
在2.2中keras model到SavedModel转换的过程中,用到了一个inference的具体函数,这个函数是从SavedModel中的sugnatures得到的,这个inference就是一个concrete_function
在下一篇文章中我们将来介绍tflite保存与理解与量化