J'ai affiné le modèle d'inception avec un nouvel ensemble de données et l'ai enregistré en tant que modèle ".h5" dans Keras. Mon objectif est maintenant d’exécuter mon modèle sur Android Tensorflow qui accepte uniquement l’extension ".pb". La question est que y at-il une bibliothèque dans Keras ou tensorflow pour faire cette conversion? J'ai vu ce billet jusqu'à présent: https://blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html mais je ne peux pas encore le comprendre.
Keras n'inclut aucun moyen d'exporter un graphique TensorFlow en tant que fichier de mémoire tampon de protocole, mais vous pouvez le faire à l'aide des utilitaires TensorFlow standard. Here est un article de blog expliquant comment procéder à l'aide du script de l'utilitaire freeze_graph.py
inclus dans TensorFlow, qui est la méthode "typique" utilisée.
Cependant, je trouve personnellement pénible de devoir créer un point de contrôle, puis d'exécuter un script externe pour obtenir un modèle, et préfère le faire à partir de mon propre code Python. J'utilise donc une fonction comme celle-ci:
_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
_
Ce qui s’inspire de la mise en oeuvre de _freeze_graph.py
_. Les paramètres sont similaires au script aussi. session
est l'objet de session TensorFlow. _keep_var_names
_ est nécessaire uniquement si vous souhaitez que certaines variables ne soient pas gelées (par exemple, pour les modèles avec état), donc généralement pas. _output_names
_ est une liste avec les noms des opérations qui produisent les sorties souhaitées. _clear_devices
_ supprime simplement les directives de périphérique pour rendre le graphique plus portable. Ainsi, pour un Keras typique model
avec une sortie, vous feriez quelque chose comme:
_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])
_
Ensuite, vous pouvez écrire le graphique dans un fichier comme d'habitude avec tf.train.write_graph
:
_tf.train.write_graph(frozen_graph, "some_directory", "my_model.pb", as_text=False)
_
La méthode freeze_session fonctionne bien. Mais, comparé à l'enregistrement dans un fichier de point de contrôle, l'utilisation de l'outil freeze_graph fourni avec TensorFlow me parait plus simple, car plus facile à gérer. Tout ce que vous devez faire est les deux étapes suivantes:
Tout d’abord, ajoutez après votre code Keras model.fit(...)
et entraînez votre modèle:
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')
Ensuite, accédez à votre répertoire racine TensorFlow, exécutez:
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
L'exemple simple suivant (exemple XOR) montre comment exporter des modèles Keras (au format h5
et au format pb
], et comment utiliser le modèle sous Python et en 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)
prévoir.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;
}
Il y a un point très important lorsque vous souhaitez convertir en tensorflow. Si vous utilisez la suppression, la normalisation par lots ou toute autre couche de ce type (qui ne peuvent pas être entraînées mais qui calculent des valeurs), , vous devez modifier la phase d'apprentissage de keras backend . Voici un discussion à ce sujet.
import keras.backend as K
k.set_learning_phase(0) # 0 testing, 1 training mode
Si vous souhaitez utiliser le modèle uniquement à des fins d'inférence, vous devez tout d'abord figer le graphique, puis l'écrire sous forme de fichier .pb
. L'extrait de code ressemble à ceci ( code emprunté d'ici ):
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)
Vous pouvez faire ce qui précède en utilisant l’outil keras_to_tensorflow : https://github.com/amir-abdi/keras_to_tensorflow
L'outil keras_to_tensorflow prend en charge les opérations ci-dessus, avec quelques fonctionnalités supplémentaires pour une solution plus diversifiée. Appelez-le simplement avec les arguments d'entrée corrects (par exemple, les indicateurs input_model
et output_model
).
Si vous souhaitez rééduquer le modèle dans tensorflow, utilisez l'outil ci-dessus avec l'indicateur output_meta_ckpt
pour exporter les points de contrôle et les méta-graphiques.
Veuillez utiliser tf.saved_model.simple_save , quelques exemples de codes:
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})
=== mettre à jour ====
Vous pouvez utiliser as_a_saved_model , exemple de codes:
saved_model_path = tf.contrib.saved_model.save_keras_model(model, "./saved_models")
en utilisant estimator.export_savedmodel, nous pouvons facilement convertir un modèle h5 en un modèle enregistré. Consultez la documentation ici 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)
Cette solution a fonctionné pour moi. Courtoisie à 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})