web-dev-qa-db-ja.com

Keras .h5をtensorflow .pbにエクスポートする方法は?

新しいデータセットでインセプションモデルを微調整し、Kerasで「.h5」モデルとして保存しました。現在、私の目標は、拡張子「.pb」のみを受け入れるAndroid Tensorflowでモデルを実行することです。質問は、この変換を行うためにKerasまたはテンソルフローにライブラリがありますか?私はこれまでにこの投稿を見ました: https://blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html しかし、まだ理解できません。

37
Solix

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)
61
jdehesa

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
22
Jeff Tang

次の簡単な例(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;
}
6
Amir Saniyan

テンソルフローに変換する場合、非常に重要なポイントがあります。ドロップアウト、バッチ正規化、またはこれらのような他のレイヤー(トレーニング可能ではないが値を計算する)を使用する場合、kerasバックエンドの学習フェーズを変更する必要があります 。ここに discussion があります。

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

モデルを推論専用にする場合は、最初にグラフをフリーズしてから、.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フラグとともに使用して、チェックポイントとメタグラフをエクスポートします。

2
AHA

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")
2
lasclocker

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)
1
Aashish Dahiya

このソリューションは私のために働いた。礼儀 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})
1
Tarik GUELZIM