如何将 Keras .h5 导出到 tensorflow .pb?

Posted

技术标签:

【中文标题】如何将 Keras .h5 导出到 tensorflow .pb?【英文标题】:How to export Keras .h5 to tensorflow .pb? 【发布时间】:2018-01-09 23:38:30 【问题描述】:

我已经使用新数据集微调了初始模型,并将其保存为 Keras 中的“.h5”模型。现在我的目标是在仅接受“.pb”扩展名的 android Tensorflow 上运行我的模型。问题是 Keras 或 tensorflow 中是否有任何库可以进行这种转换?到目前为止,我已经看到了这篇文章:https://blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html,但还不知道。

【问题讨论】:

【参考方案1】:

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

这可以在将模型 (.h5) 权重保存在 mrcnn/model.py 文件中时完成。只需进行以下更改 (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)

完整的文件可以在HERE找到。有了它,我能够为 coco 和 imagenet 权重转换 ResNet50 和 ResNet101 主干。

【讨论】:

【参考方案2】:

Tensorflow 2 更新

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

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

或旧的 Keras 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 共享或部署。

保存和加载 Keras 模型:https://www.tensorflow.org/guide/keras/save_and_serialize 使用 SavedModel 格式:https://www.tensorflow.org/guide/saved_model

Tensorflow 2 示例

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


train.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

predict.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 定义了一组通用运算符(机器学习和深度学习模型的构建块)和通用文件格式,使 AI 开发人员能够使用具有各种框架、工具、运行时和编译器的模型。

$ 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 将创建一个仅使用 opset 13 中可用的 ops 的 onnx 图。由于在大多数情况下,较旧的 opset 具有较少的 ops,因此某些模型可能无法在较旧的 opset 上转换。

https://onnx.ai/ https://github.com/onnx/onnx https://github.com/onnx/tensorflow-onnx

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 示例)展示了如何导出 Keras 模型(h5 格式和pb 格式),并在 Python 和 C++ 中使用该模型:


train.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)

predict.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;

【讨论】:

非常感谢您提供完整的示例。我可以请你添加一个额外的句子吗?如果人们直接使用 keras 而不是 tf.keras,他们还需要在函数调用中使用“keras.backend.get_session()”而不是“tf.keras.backend.get_session()”,否则会报错未初始化的变量。我没有意识到你之前使用了前缀“tf.keras”,而那个小差异花了我一个小时...... 这对我很有帮助。不使用 cv2 的 opencv-predict.py 的替代方案:import tensorflow as tffrom tensorflow.python.platform import gfilef = gfile.FastGFile(r'.\xor\xor.pb', 'rb')graph_def = tf.GraphDef()graph_def.ParseFromString(f.read())graph_def.ParseFromString(f.read())f.close()tfSession = tf.InteractiveSession()tfSession.graph.as_default()tf.import_graph_def(graph_def)tf.import_graph_def(graph_def)987654367@98765466跨度> @gebbissimo 我收到此错误AttributeError: module 'tensorflow.keras.backend' has no attribute 'get_session'。你有什么主意吗?。我将其用作tf.keras.backend.get_session()【参考方案3】:

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,
)

【讨论】:

【参考方案4】:

tf 2.2.0

导入 tensorflow.keras 而不仅仅是 keras,因为它会将您的模型加载为无法直接转换为 tensorflow .pb 格式的 keras.engine.sequential.Sequential 对象

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

【讨论】:

得到以下错误: ValueError: Attempted to save a function b'__inference_forward_lstm_1_layer_call_fn_14156' 它引用了一个符号张量 Tensor("dropout/mul_1:0", shape=(None, 300), dtype=float32 ) 这不是一个简单的常数。这不受支持。【参考方案5】:

目前,以上所有较旧的答案都已过时。从 TensorFlow 2.1 开始

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

将创建一个包含“saved_model.pb”的文件夹

【讨论】:

它对我有用,但是当我尝试在 openCV 中使用它时,它给了我以下错误:FAILED: ReadProtoFromBinaryFile(param_file, param)。解析GraphDef文件失败:... 不知道。到目前为止生成的 .pb 是有效的。您使用的 Tensorflow 版本和您使用的 OpenCV 版本之间可能存在一些新的不兼容。我会做一个最小的例子并在 OpenCV 中创建一个问题 @GefilteFish SavedModel 将模型导出到一个目录中,saved_model.pb 没有 GraphDef。这就是 ReadProtoBinary 无法加载它的原因。请改用LoadSavedModel。检查此答案以获取更多详细信息:***.com/a/63189994/6596684【参考方案6】:

Tensorflow tf.saved_model api最适合生成pb模型

如果你有 h5 模型然后通过 keras load_model 加载它

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

通过saved_model api保存tensorflow模型,它会将模型保存为pb格式。此模型需要元数据才能通过 Google Ai 平台提供服务。因此,您可以将目录上传到 Ai 平台,以便为您的模型提供服务。

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

【讨论】:

请注意,这会将模型保存为 SavedModel 格式,这与冻结图形的 .pb 文件不同。【参考方案7】:

这个解决方案对我有用。 致谢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)

【讨论】:

【参考方案8】:

Keras 本身不包含任何将 TensorFlow 图导出为协议缓冲区文件的方法,但您可以使用常规的 TensorFlow 实用程序来完成。 Here 是一篇博文,解释了如何使用 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 只是删除了任何设备指令以使图形更便携。因此,对于具有一个输出的典型 Keras 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)

【讨论】:

这似乎对我有用。但是,输出 .pb 文件没有相同的输入节点名称。那正确吗?输入节点名称为 input_1 但冻结后,我不知道新名称。你能告诉我新名字是什么吗? 我在 model.output.op.name 中遇到错误。你能告诉我如何解决这个问题吗? 在类似的帖子中,我见过以前使用过“keras.backend.set_learning_phase(0)”。这里也有必要吗? @gebbissimo 好吧,这会“修剪”图形,因此只有计算给定输出所需的操作和变量才会被实际导出(因此除非您实际请求训练,否则不应导出与训练相关的变量操作)。但是,是的,您链接的帖子是另一个有效的解决方案,实际上几乎是等效的,因为两者都基于 convert_variables_to_constants(它“冻结”了变量并修剪了图表)。 为什么我建立了一个简单的模型,却把它保存到一个文件很大的pb文件中?【参考方案9】:

使用 estimator.export_savedmodel 我们可以轻松地将 h5 模型转换为保存的模型。 在此处查看文档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)

【讨论】:

【参考方案10】:

要转成tensorflow有一个很重要的一点。如果您使用 dropout、batch normalization 或任何其他类似的层(它们没有可训练但计算值),您应该更改 keras 后端的学习阶段。这是关于它的discussion。

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

【讨论】:

【参考方案11】:

请使用tf.saved_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)

===更新====

您可以使用as_a_saved_model,示例代码:

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

【讨论】:

看起来 simple_save 在 Tensorflow 2.0 中已被弃用。 @scribu,对,在TF2.0中,我们可以参考export_to_savedmodel【参考方案12】:

如果您只希望模型用于推理,则应先冻结图形,然后将其写入.pb 文件。代码 sn-p 看起来像这样(code borrowed from here):

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 标志)调用它。

如果您想在 tensorflow 中重新训练模型,请使用上面带有 output_meta_ckpt 标志的工具来导出检查点和元图。

【讨论】:

【参考方案13】:

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

首先,在您的 Keras 代码后添加 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

【讨论】:

我必须在保存检查点之前设置 K.set_learning_phase(0)。否则我在 android 上运行它时遇到错误Keras error “You must feed a value for placeholder tensor 'keras_learning_phase' with dtype bool”。我设置 0 是因为我只需要模型进行推理。 K.set_learning_phase 必须在加载模型之前调用。

以上是关于如何将 Keras .h5 导出到 tensorflow .pb?的主要内容,如果未能解决你的问题,请参考以下文章

Tensorflow (.pb) 格式到 Keras (.h5)

如何在 Tensorflow-2.0 中绘制 tf.keras 模型?

如何将保存的模型转换或加载到 TensorFlow 或 Keras?

Keras h5 到 2019 年服务于 Tensorflow?

.h5 keras 模型到 coreml 的分类转换在 IOS 中不起作用

如何将 keras 模型文件加载到 OpenCV 代码中?