新しいデータセットでインセプションモデルを微調整し、Kerasで「.h5」モデルとして保存しました。現在、私の目標は、拡張子「.pb」のみを受け入れるAndroid Tensorflowでモデルを実行することです。質問は、この変換を行うためにKerasまたはテンソルフローにライブラリがありますか?私はこれまでにこの投稿を見ました: https://blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html しかし、まだ理解できません。
Kerasには、TensorFlowグラフをプロトコルバッファーファイルとしてエクスポートする手段は含まれていませんが、通常のTensorFlowユーティリティを使用してエクスポートできます。 ここ は、ユーティリティスクリプトを使用してそれを行う方法を説明するブログ投稿です。 freeze_graph.py
TensorFlowに含まれています。これは、「典型的な」方法です。
しかし、個人的にはチェックポイントを作成してから外部スクリプトを実行してモデルを取得する必要があり、代わりに自分の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
は、デバイスディレクティブを削除して、グラフの移植性を高めます。したがって、1つの出力を持つ典型的な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ツールを使用することは、保守が容易であるため、私には簡単に思えます。必要なのは、次の2つのステップだけです。
まず、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ルートディレクトリにcdして、次を実行します。
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
次の簡単な例(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バックエンドの学習フェーズを変更する必要があります 。ここに discussion があります。
import keras.backend as K
k.set_learning_phase(0) # 0 testing, 1 training mode
モデルを推論専用にする場合は、最初にグラフをフリーズしてから、.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_model
およびoutput_model
フラグ)で呼び出します。
Tensorflowでモデルを再トレーニングする場合は、上記のツールをoutput_meta_ckpt
フラグとともに使用して、チェックポイントとメタグラフをエクスポートします。
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")
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)
このソリューションは私のために働いた。礼儀 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})