我用一个新数据集微调了Inception模型,并将其保存为Keras的".h5"模型。现在我的目标是在只接受“.pb”扩展名的Android Tensorflow上运行我的模型。问题是,Keras或Tensorflow中是否有任何库可以进行此转换?到目前为止,我看到了这篇文章:https://blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html,但仍无法弄清楚。
我用一个新数据集微调了Inception模型,并将其保存为Keras的".h5"模型。现在我的目标是在只接受“.pb”扩展名的Android Tensorflow上运行我的模型。问题是,Keras或Tensorflow中是否有任何库可以进行此转换?到目前为止,我看到了这篇文章:https://blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html,但仍无法弄清楚。
freeze_graph.py
来完成这个过程,这是“典型”的方式。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)
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')
然后切换到你的 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
将所有内容保存到单个档案中,使用Tensorflow SavedModel
格式(包含saved_model.pb
文件):
最初的回答:
model = ... # Get model (Sequential, Functional Model, or Model subclass)
model.save('path/to/location')
model = ... # Get model (Sequential, Functional Model, or Model subclass)
model.save('model.h5')
SavedModel
。SavedModel
格式。加载模型的方法如下:from tensorflow import keras
model = keras.models.load_model('path/to/location')
model = keras.models.load_model('model.h5')
SavedModel
包含了一个完整的 TensorFlow 程序,包括经过训练的参数(即 tf.Variables
)和计算。它不需要原始的模型构建代码运行,这使得它对于使用 TFLite
、TensorFlow.js
、TensorFlow Serving
或 TensorFlow Hub
进行分享或部署非常有用。
以下是一个简单示例(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
”定义了一组常见的运算符-机器学习和深度学习模型的构建块-以及一种通用的文件格式,使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中可用操作的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;
}
import tensorflow as tf
from tensorflow.python.platform import gfile
f = gfile.FastGFile(r'.\xor\xor.pb', 'rb')
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
f.close()
tfSession = tf.InteractiveSession()
tfSession.graph.as_default()
tf.import_graph_def(graph_def)
out = tfSession.graph.get_tensor_by_name('import/dense_4/Sigmoid:0')
tfSession.run(out, {'import/dense_input:0': np.array([[0,0]])})
- BitPusher16AttributeError: module 'tensorflow.keras.backend' has no attribute 'get_session'
。你有什么想法吗?我是使用 tf.keras.backend.get_session()
的方式来调用的。 - S Andrew目前所有较早的答案都已过时。截至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”的文件夹
saved_model.pb
不是GraphDef。这就是为什么ReadProtoBinary无法加载它的原因。请改用LoadSavedModel
。有关更多详细信息,请查看此答案:https://dev59.com/RLzpa4cB1Zd3GeqPL3wB#63189994 - High Performance Rangsiman当您想转换为TensorFlow时,有一个非常重要的点。如果您使用了dropout、batch normalization或其他类似这些的层(它们具有可计算但不可训练的值),您应该更改Keras后端的学习阶段。这里是关于此的讨论。
import keras.backend as K
k.set_learning_phase(0) # 0 testing, 1 training mode
这个解决方案对我很有帮助。感谢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})
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")
如果你只需要模型进行推理,你应该先冻结图形,然后将其写成一个.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)
input_model
和 output_model
标志)调用即可。output_meta_ckpt
标志导出检查点和元图。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)
tf 2.2.0
导入tensorflow.keras而不是仅仅使用keras,因为它会将您的模型加载为keras.engine.sequential.Sequential对象,该对象无法直接转换为tensorflow .pb格式。
#import keras
import tensorflow.keras as keras
model = keras.models.load_model(load_path)
model.save(save_path)
frozen_graph = freeze_session(K.get_session(),output_names=[out.op.name for out in model.outputs])
时遇到了错误,错误信息为 Keras 符号输入/输出没有实现op
。您可能正在尝试将 Keras 符号输入/输出传递给未注册调度的 TF API,从而阻止 Keras 自动将 API 调用转换为功能模型中的 lambda 层。 我该如何解决这个问题?有什么建议吗? - aminvincent