如何导出 Keras.h5到张量流。 pb? ?

我用一个新的数据集对初始模型进行了微调,并将其保存为”。Keras 的 h 5”型号。现在我的目标是在可以接受的安卓 Tensorflow 上运行我的模型。仅限 pb 扩展。问题是,Keras 是否有图书馆或 tensorflow 来进行这种转换?到目前为止,我已经看过这篇文章: https://blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html,但还没有弄明白。

111775 次浏览

Kera 本身并不包含任何将 TensorFlow 图形导出为协议缓冲文件的方法,但是您可以使用常规的 TensorFlow 实用程序来实现这一点。给你是一篇博客文章,解释了如何使用 TensorFlow 中包含的实用脚本 freeze_graph.py来实现,这是“典型”的实现方式。

然而,我个人觉得建立一个检查点然后运行一个外部脚本来获取模型是件麻烦的事情,相反,我更喜欢用我自己的 Python 代码来做这件事,所以我使用了这样一个函数:

def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):
"""
Freezes the state of a session into a pruned computation graph.


Creates a new computation graph where variable nodes are replaced by
constants taking their current value in the session. The new graph will be
pruned so subgraphs that are not necessary to compute the requested
outputs are removed.
@param session The TensorFlow session to be frozen.
@param keep_var_names A list of variable names that should not be frozen,
or None to freeze all the variables in the graph.
@param output_names Names of the relevant graph outputs.
@param clear_devices Remove the device directives from the graph for better portability.
@return The frozen graph definition.
"""
graph = session.graph
with graph.as_default():
freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))
output_names = output_names or []
output_names += [v.op.name for v in tf.global_variables()]
input_graph_def = graph.as_graph_def()
if clear_devices:
for node in input_graph_def.node:
node.device = ""
frozen_graph = tf.graph_util.convert_variables_to_constants(
session, input_graph_def, output_names, freeze_var_names)
return frozen_graph

它的灵感来自于 freeze_graph.py的实现。参数也与脚本类似。session是 TensorFlow 会话对象。只有当您希望某个变量不被冻结(例如,对于有状态模型)时,才需要 keep_var_names,因此通常不需要。output_names是一个包含生成所需输出的操作名称的列表。clear_devices只是删除任何设备指令,使图更便携。因此,对于只有一个输出的典型 Kera model,您可以执行以下操作:

from keras import backend as K


# Create, compile and train model...


frozen_graph = freeze_session(K.get_session(),
output_names=[out.op.name for out in model.outputs])

然后你可以像往常一样用 tf.train.write_graph把图表写到一个文件中:

tf.train.write_graph(frozen_graph, "some_directory", "my_model.pb", as_text=False)

Zen _ session 方法可以正常工作。但是与保存到检查点文件然后使用 TensorFlow 附带的 free_ graph 工具相比,对我来说似乎更简单,因为它更容易维护。你需要做的只是以下两个步骤:

首先,在你的 Kera 代码 model.fit(...)之后添加并训练你的模型:

from keras import backend as K
import tensorflow as tf
print(model.output.op.name)
saver = tf.train.Saver()
saver.save(K.get_session(), '/tmp/keras_model.ckpt')

然后 cd 到 TensorFlow 根目录,运行:

python tensorflow/python/tools/freeze_graph.py \
--input_meta_graph=/tmp/keras_model.ckpt.meta \
--input_checkpoint=/tmp/keras_model.ckpt \
--output_graph=/tmp/keras_frozen.pb \
--output_node_names="<output_node_name_printed_in_step_1>" \
--input_binary=true

如果您希望模型仅用于推理,则应该首先冻结图形,然后将其写成 .pb文件。代码片段如下所示(从这里借来的代码) :

import tensorflow as tf
from tensorflow.python.framework import graph_util
from tensorflow.python.framework import graph_io
import keras
from keras import backend as K


sess = K.get_session()


constant_graph = graph_util.convert_variables_to_constants(
sess,
sess.graph.as_graph_def(),
["name_of_the_output_graph_node"])


graph_io.write_graph(constant_graph, "path/to/output/folder",
"output_model_name", as_text=False)

您可以使用 Keras _ to _ tensorflow工具: https://github.com/amir-abdi/keras_to_tensorflow完成上述操作

Keras _ to _ tensorflow工具负责上述操作,并为更加多样化的解决方案提供一些额外的特性。只需使用正确的输入参数调用它(例如 input_modeloutput_model标志)。

如果你想在张量流中重新训练模型,使用上述带有 output_meta_ckpt标志的工具来导出检查点和元图。

张量流2的更新

以 TensorFlow SavedModel格式(包含 saved_model.pb文件)将所有内容保存到单个归档文件中:

model = ...  # Get model (Sequential, Functional Model, or Model subclass)
model.save('path/to/location')

或老式的 Kera H5格式:

model = ...  # Get model (Sequential, Functional Model, or Model subclass)
model.save('model.h5')

推荐的格式是 SavedModel

返回模型:

from tensorflow import keras
model = keras.models.load_model('path/to/location')
model = keras.models.load_model('model.h5')

SavedModel包含完整的 TensorFlow 程序,包括训练过的参数(即 tf.Variables)和计算。它不需要运行原始的模型构建代码,这对于与 TFLiteTensorFlow.jsTensorFlow ServingTensorFlow Hub共享或部署非常有用。


例如 Tensorflow 2

下面的简单示例(XOR 示例)显示了如何导出 Kera 模型(h5格式和 pb格式) ,并在 Python 和 C + + 中使用该模型:


Py:

import numpy as np
import tensorflow as tf


print(tf.__version__)  # 2.4.1


x_train = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], 'float32')
y_train = np.array([[0], [1], [1], [0]], 'float32')


inputs = tf.keras.Input(shape=(2,), name='input')
x = tf.keras.layers.Dense(64, activation='relu')(inputs)
x = tf.keras.layers.Dense(64, activation='relu')(x)
x = tf.keras.layers.Dense(64, activation='relu')(x)
x = tf.keras.layers.Dense(64, activation="relu")(x)
outputs = tf.keras.layers.Dense(1, activation='sigmoid', name='output')(x)


model = tf.keras.Model(inputs=inputs, outputs=outputs, name='xor')


model.summary()


model.compile(loss='mean_squared_error', optimizer='adam', metrics=['binary_accuracy'])


model.fit(x_train, y_train, epochs=100)


model.save('./xor/')  # SavedModel format


model.save('./xor.h5')  # Keras H5 format

运行以上脚本后:

.
├── train.py
├── xor
│   ├── assets
│   ├── saved_model.pb
│   └── variables
│       ├── variables.data-00000-of-00001
│       └── variables.index
└── xor.h5

Py:

import numpy as np
import tensorflow as tf


print(tf.__version__)  # 2.4.1


model = tf.keras.models.load_model('./xor/')  # SavedModel format
# model = tf.keras.models.load_model('./xor.h5')  # Keras H5 format


# 0 xor 0 = [[0.11921611]] ~= 0
print('0 xor 0 = ', model.predict(np.array([[0, 0]])))


# 0 xor 1 = [[0.96736085]] ~= 1
print('0 xor 1 = ', model.predict(np.array([[0, 1]])))


# 1 xor 0 = [[0.97254556]] ~= 1
print('1 xor 0 = ', model.predict(np.array([[1, 0]])))


# 1 xor 1 = [[0.0206149]] ~= 0
print('1 xor 1 = ', model.predict(np.array([[1, 1]])))

转换模型到 ONNX:

ONNX是深度学习模型交换的新标准。它有望使深度学习模型便于携带,从而防止厂商锁定。

ONNX是一种开放格式,用于表示机器学习模型。ONNX定义了一组通用的操作符(机器学习和深度学习模型的构建块)和一个通用的文件格式,使人工智能开发人员能够使用具有各种框架、工具、运行时和编译器的模型。

$ pip install onnxruntime
$ pip install tf2onnx
$ python -m tf2onnx.convert --saved-model ./xor/ --opset 9 --output xor.onnx


# INFO - Successfully converted TensorFlow model ./xor/ to ONNX
# INFO - Model inputs: ['input:0']
# INFO - Model outputs: ['output']
# INFO - ONNX model is saved at xor.onnx

通过指定 --opset,用户可以覆盖默认值以生成带有所需操作集的图。例如,--opset 13将创建一个 onnx 图,该图仅使用 opset 13中的可用操作。因为在大多数情况下,较老的操作集具有较少的操作,所以一些模型可能不会在较老的操作集上进行转换。


Opencv-predict.py :

import numpy as np
import cv2


print(cv2.__version__)  # 4.5.1


model = cv2.dnn.readNetFromONNX('./xor.onnx')


# 0 xor 0 = [[0.11921611]] ~= 0
model.setInput(np.array([[0, 0]]), name='input:0')
print('0 xor 0 = ', model.forward(outputName='output'))


# 0 xor 1 = [[0.96736085]] ~= 1
model.setInput(np.array([[0, 1]]), name='input:0')
print('0 xor 1 = ', model.forward(outputName='output'))


# 1 xor 0 = [[0.97254556]] ~= 1
model.setInput(np.array([[1, 0]]), name='input:0')
print('1 xor 0 = ', model.forward(outputName='output'))


# 1 xor 1 = [[0.02061491]] ~= 0
model.setInput(np.array([[1, 1]]), name='input:0')
print('1 xor 1 = ', model.forward(outputName='output'))

Predict.cpp:

#include <cstdlib>
#include <iostream>
#include <opencv2/opencv.hpp>


int main(int argc, char **argv)
{
std::cout << CV_VERSION << std::endl; // 4.2.0


cv::dnn::Net net;


net = cv::dnn::readNetFromONNX("./xor.onnx");


// 0 xor 0 = [0.11921611] ~= 0
float x0[] = { 0, 0 };
net.setInput(cv::Mat(1, 2, CV_32F, x0), "input:0");
std::cout << "0 xor 0 = " << net.forward("output") << std::endl;


// 0 xor 1 = [0.96736085] ~= 1
float x1[] = { 0, 1 };
net.setInput(cv::Mat(1, 2, CV_32F, x1), "input:0");
std::cout << "0 xor 1 = " << net.forward("output") << std::endl;


// 1 xor 0 = [0.97254556] ~= 1
float x2[] = { 1, 0 };
net.setInput(cv::Mat(1, 2, CV_32F, x2), "input:0");
std::cout << "1 xor 0 = " << net.forward("output") << std::endl;


// 1 xor 1 = [0.020614909] ~= 0
float x3[] = { 1, 1 };
net.setInput(cv::Mat(1, 2, CV_32F, x3), "input:0");
std::cout << "1 xor 1 = " << net.forward("output") << std::endl;


return EXIT_SUCCESS;
}

编译和运行:

$ sudo apt install build-essential pkg-config libopencv-dev
$ g++ predict.cpp `pkg-config --cflags --libs opencv4` -o predict
$ ./predict

原始答案

下面的简单示例(XOR 示例)显示了如何导出 Kera 模型(h5格式和 pb格式) ,并在 Python 和 C + + 中使用该模型:


Py:

import numpy as np
import tensorflow as tf




def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):
"""
Freezes the state of a session into a pruned computation graph.


Creates a new computation graph where variable nodes are replaced by
constants taking their current value in the session. The new graph will be
pruned so subgraphs that are not necessary to compute the requested
outputs are removed.
@param session The TensorFlow session to be frozen.
@param keep_var_names A list of variable names that should not be frozen,
or None to freeze all the variables in the graph.
@param output_names Names of the relevant graph outputs.
@param clear_devices Remove the device directives from the graph for better portability.
@return The frozen graph definition.
"""
graph = session.graph
with graph.as_default():
freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))
output_names = output_names or []
output_names += [v.op.name for v in tf.global_variables()]
input_graph_def = graph.as_graph_def()
if clear_devices:
for node in input_graph_def.node:
node.device = ''
frozen_graph = tf.graph_util.convert_variables_to_constants(
session, input_graph_def, output_names, freeze_var_names)
return frozen_graph




X = np.array([[0,0], [0,1], [1,0], [1,1]], 'float32')
Y = np.array([[0], [1], [1], [0]], 'float32')


model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Dense(64, input_dim=2, activation='relu'))
model.add(tf.keras.layers.Dense(64, activation='relu'))
model.add(tf.keras.layers.Dense(64, activation='relu'))
model.add(tf.keras.layers.Dense(64, activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))


model.compile(loss='mean_squared_error', optimizer='adam', metrics=['binary_accuracy'])


model.fit(X, Y, batch_size=1, nb_epoch=100, verbose=0)


# inputs:  ['dense_input']
print('inputs: ', [input.op.name for input in model.inputs])


# outputs:  ['dense_4/Sigmoid']
print('outputs: ', [output.op.name for output in model.outputs])


model.save('./xor.h5')


frozen_graph = freeze_session(tf.keras.backend.get_session(), output_names=[out.op.name for out in model.outputs])
tf.train.write_graph(frozen_graph, './', 'xor.pbtxt', as_text=True)
tf.train.write_graph(frozen_graph, './', 'xor.pb', as_text=False)

Py:

import numpy as np
import tensorflow as tf


model = tf.keras.models.load_model('./xor.h5')


# 0 ^ 0 =  [[0.01974997]]
print('0 ^ 0 = ', model.predict(np.array([[0, 0]])))


# 0 ^ 1 =  [[0.99141496]]
print('0 ^ 1 = ', model.predict(np.array([[0, 1]])))


# 1 ^ 0 =  [[0.9897714]]
print('1 ^ 0 = ', model.predict(np.array([[1, 0]])))


# 1 ^ 1 =  [[0.00406971]]
print('1 ^ 1 = ', model.predict(np.array([[1, 1]])))

Opencv-predict.py :

import numpy as np
import cv2 as cv




model = cv.dnn.readNetFromTensorflow('./xor.pb')


# 0 ^ 0 =  [[0.01974997]]
model.setInput(np.array([[0, 0]]), name='dense_input')
print('0 ^ 0 = ', model.forward(outputName='dense_4/Sigmoid'))


# 0 ^ 1 =  [[0.99141496]]
model.setInput(np.array([[0, 1]]), name='dense_input')
print('0 ^ 1 = ', model.forward(outputName='dense_4/Sigmoid'))


# 1 ^ 0 =  [[0.9897714]]
model.setInput(np.array([[1, 0]]), name='dense_input')
print('1 ^ 0 = ', model.forward(outputName='dense_4/Sigmoid'))


# 1 ^ 1 =  [[0.00406971]]
model.setInput(np.array([[1, 1]]), name='dense_input')
print('1 ^ 1 = ', model.forward(outputName='dense_4/Sigmoid'))

Predict.cpp:

#include <cstdlib>
#include <iostream>
#include <opencv2/opencv.hpp>


int main(int argc, char **argv)
{
cv::dnn::Net net;


net = cv::dnn::readNetFromTensorflow("./xor.pb");


// 0 ^ 0 = [0.018541215]
float x0[] = { 0, 0 };
net.setInput(cv::Mat(1, 2, CV_32F, x0), "dense_input");
std::cout << "0 ^ 0 = " << net.forward("dense_4/Sigmoid") << std::endl;


// 0 ^ 1 = [0.98295897]
float x1[] = { 0, 1 };
net.setInput(cv::Mat(1, 2, CV_32F, x1), "dense_input");
std::cout << "0 ^ 1 = " << net.forward("dense_4/Sigmoid") << std::endl;


// 1 ^ 0 = [0.98810625]
float x2[] = { 1, 0 };
net.setInput(cv::Mat(1, 2, CV_32F, x2), "dense_input");
std::cout << "1 ^ 0 = " << net.forward("dense_4/Sigmoid") << std::endl;


// 1 ^ 1 = [0.010002014]
float x3[] = { 1, 1 };
net.setInput(cv::Mat(1, 2, CV_32F, x3), "dense_input");
std::cout << "1 ^ 1 = " << net.forward("dense_4/Sigmoid") << std::endl;


return EXIT_SUCCESS;
}

请使用 Save _ model. simple _ save,一些示例代码:

with tf.keras.backend.get_session() as sess:
tf.saved_model.simple_save(
sess,
export_path,
inputs={'input': keras_model.input},
outputs={'output': keras_model.output})

= = 更新 = = = =

你可以使用 作为保存的模型,示例代码:

saved_model_path = tf.contrib.saved_model.save_keras_model(model, "./saved_models")

有一个非常重要的点,当你想转换成张量流。如果使用辍学、批量标准化或任何其他类似的图层(它们具有不可训练但可计算的值) ,则使用 您应该更改 Keras 后端的学习阶段。这里有一个关于它的 讨论

import keras.backend as K
k.set_learning_phase(0) # 0 testing, 1 training mode

使用 Estiator.export _ savedmodel 我们可以很容易地将 h5模型转换为保存模型。 检查 doc here < a href = “ https://www.tensorflo.org/api _ docs/python/tf/Estimator”rel = “ nofollow norefrer”> https://www.tensorflow.org/api_docs/python/tf/estimator/estimator

def prepare_image(image_str_tensor):
image_contents = tf.read_file(image_str_tensor)
image = tf.image.decode_jpeg(image_contents, channels=3)
image = tf.image.resize_images(image, [224, 224])
image = tf.cast(image, tf.float32)
return preprocess_input(image)


def serving_input_receiver_fn():
input_ph = tf.placeholder(tf.string, shape=[None])
images_tensor = tf.map_fn(
prepare_image, input_ph, back_prop=False, dtype=tf.float32)
images_tensor = tf.image.convert_image_dtype(images_tensor,
dtype=tf.float32)


return tf.estimator.export.ServingInputReceiver({"input": images_tensor},
{'image_url': input_ph})


estimator = tf.keras.estimator.model_to_estimator(
keras_model_path=h5_model_path
)


estimator.export_savedmodel(saved_model_path, serving_input_receiver_fn=serving_input_receiver_fn)

这个办法对我很有效。 感谢 https://medium.com/tensorflow/training-and-serving-ml-models-with-tf-keras-fd975cc0fa27

import tensorflow as tf


# The export path contains the name and the version of the model
tf.keras.backend.set_learning_phase(0) # Ignore dropout at inference
model = tf.keras.models.load_model('./model.h5')
export_path = './PlanetModel/1'


# Fetch the Keras session and save the model
# The signature definition is defined by the input and output tensors
# And stored with the default serving key
with tf.keras.backend.get_session() as sess:
tf.saved_model.simple_save(
sess,
export_path,
inputs={'input_image': model.input},
outputs={t.name:t for t in model.outputs})

Tensorflow Save _ model api 最适合生成 pb 模型

如果您有 h5模型,那么通过 keras load _ model 加载它

from tensorflow import keras
model = keras.models.load_model("model.h5")

通过 Save _ model api 保存张量流模型,将模型保存为 pb 格式。这个模型需要元数据来通过 Google Ai 平台提供服务。所以你可以上传目录到艾平台为您的模型服务。

import tensorflow as tf
tf.saved_model.save(model, './directory-to-save-file/')

在这个时候,所有以上的旧答案都已经过时了

from tensorflow.keras.models import Model, load_model
model = load_model(MODEL_FULLPATH)
model.save(MODEL_FULLPATH_MINUS_EXTENSION)

将创建一个文件夹,其中包含“ save _ model. pb”

Tf 2.2.0

导入 tensorflow. keras 而不仅仅是 keras,因为它将把模型加载为 keras.engine. 函数。不能直接转换成张量流的序列对象。Pb 格式

#import keras
import tensorflow.keras as keras
model = keras.models.load_model(load_path)
model.save(save_path)

tensorflow 2.x: 如果您只想在 pbtxt中保存图形定义,那么使用下面的代码。

import tensorflow as tf
keras_model = ...
tf.io.write_graph(
keras_model.output.graph,
'model_dir',
'model.pbtxt',
as_text=True,
)

如果用户试图将 Mask-RCNN 模型/权重转换成一个冻结的图形,这里的大多数答案都是不够的。

这可以在保存 mrcnn/model.py文件中的模型(.h5)权重时完成。只需要做以下更改(git diff)

+    def freeze_session(self, session, keep_var_names=None, output_names=None, clear_devices=True):
+        """
+        Freezes the state of a session into a pruned computation graph.
+
+        Creates a new computation graph where variable nodes are replaced by
+        constants taking their current value in the session. The new graph will be
+        pruned so subgraphs that are not necessary to compute the requested
+        outputs are removed.
+        @param session The TensorFlow session to be frozen.
+        @param keep_var_names A list of variable names that should not be frozen,
+                              or None to freeze all the variables in the graph.
+        @param output_names Names of the relevant graph outputs.
+        @param clear_devices Remove the device directives from the graph for better portability.
+        @return The frozen graph definition.
+        """
+        graph = session.graph
+        with graph.as_default():
+            freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))
+            output_names = output_names or []
+            output_names += [v.op.name for v in tf.global_variables()]
+            input_graph_def = graph.as_graph_def()
+            if clear_devices:
+                for node in input_graph_def.node:
+                    node.device = ""
+            frozen_graph = tf.graph_util.convert_variables_to_constants(
+                session, input_graph_def, output_names, freeze_var_names)
+            return frozen_graph
+
def train(self, train_dataset, val_dataset, learning_rate, epochs, layers,
augmentation=None, custom_callbacks=None, no_augmentation_sources=None):
"""Train the model.
@@ -2373,6 +2401,12 @@ class MaskRCNN():
workers=workers,
use_multiprocessing=True,
)
+        #######using session and saving .pb file##
+        frozen_graph = self.freeze_session(K.get_session(),
+                              output_names=[out.op.name for out in self.keras_model.outputs])
+        print('\n\n\t\t******* Writing Frozen Graph in logs directory *******\n\n')
+        tf.train.write_graph(frozen_graph, self.log_dir, "my_model.pb", as_text=False)
+
self.epoch = max(self.epoch, epochs)


完整的文件可以找到 给你。有了它,我就能够转换 ResNet50和 ResNet101骨架的 coco 和图像网络权重。

在我的案例中,我试图将暗网权重转换为 TensorFlow 模型,我需要这个模型。Pb 格式。 我在这里和其他论坛上尝试了很多解决方案,但是我最终能够通过将我的 Tensorflow v2.2升级到 Tensorflow v2.3来修复它,并且我能够成功地将模型保存到。Pb 格式。

以下是参考文件:

我的进口商品:

import tensorflow as tf
import tensorflow.keras as keras

以. pb 格式保存模型的代码:

model.save("/path to directory/")

以.h5格式保存模型的代码:

tf.keras.models.save_model(model = model, filepath, modelname.h5')

注意: 只有当我将 Tensorflow 从2.2版本升级到2.3版本时,我才能让它工作