Как экспортировать Keras.h5 в tensorflow.pb?

У меня есть настраиваемая начальная модель с новым набором данных и сохранена в качестве модели ".h5" в Keras. теперь моя цель - запустить мою модель на Android Tensorflow, которая принимает только расширение ".pb". вопрос в том, что есть ли в Keras или library для создания этого преобразования библиотека? Я видел этот пост до сих пор: https://blog.keras.io/keras-as-a-simplified-interface-to-tensorflow-tutorial.html, но пока не могу понять.

Ответ 1

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 - это список с именами операций, которые выдают output_names результаты. clear_devices просто удаляет любые директивы устройства, чтобы сделать граф более переносимым. Итак, для типичной model Keras с одним выходом вы должны сделать что-то вроде:

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)

Ответ 2

Метод freeze_session работает нормально. Но по сравнению с сохранением файла контрольной точки, использование инструмента freeze_graph, поставляемого вместе с TensorFlow, кажется мне более простым, так как его легче поддерживать. Все, что вам нужно сделать, это следующие два шага:

Сначала добавьте после кода 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

Ответ 3

В следующем простом примере (пример 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;
}

Ответ 4

Есть очень важный момент, когда вы хотите преобразовать в тензорный поток. Если вы используете выпадение, пакетную нормализацию или любые другие подобные слои (которые имеют не обучаемые, но вычисляемые значения), вам следует изменить фазу обучения бэкэнда керас. Вот обсуждение этого.

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

Ответ 5

Если вы хотите, чтобы модель была только для вывода, вы должны сначала заморозить график, а затем записать его как .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).

Если вы хотите переустановить модель в тензорном потоке, используйте вышеуказанный инструмент с флагом output_meta_ckpt для экспорта контрольных точек и output_meta_ckpt.

Ответ 6

Пожалуйста, используйте 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")

Ответ 7

используя 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)

Ответ 8

Это решение сработало для меня. Предоставлено 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})

Ответ 9

Я получил эту ошибку, и не могли бы вы дать мне несколько советов cv2.error: OpenCV (3.4.3) C:\projects\opencv-python\opencv\modules\dnn\src\dnn.cpp: 412: ошибка: ([CN00 ]: Ошибка не указана) Невозможно создать слой "lambda_1/truediv" типа "RealDiv" в функции "cv :: dnn :: экспериментальный_dnn_34_v7 :: LayerData :: getLayerInstance"

Ответ 10

frozen_graph = freeze_session (K.get_session(),                             output_names = [out.op.name для out в model.keras_model.output])

Я получил ошибку:

ValueError Traceback (последний вызов был последним) в       1 frozen_graph = freeze_session (K.get_session(), → 2 output_names = [out.op.name для out в model.keras_model.output])

 в freeze_session (сеанс, keep_var_names, output_names, clear_devices)      26 node.device = ""      27 frozen_graph = tf.graph_util.convert_variables_to_constants ( ---> 28 сессий, input_graph_def, output_names, freeze_var_names)      29 вернуть замороженный_граф

~/Anaconda3/envs/env_name/Library/python3.6/сайт-пакеты/tensorflow/питон /Util/deprecation.py в new_func (* args, ** kwargs)     322 "в будущей версии", если дата "Нет" ("после% s"% date),     323 инструкции) → 324 return func (* args, ** kwargs)     325 return tf_decorator.make_decorator (     326 func, new_func, "не рекомендуется",

~/Anaconda3/envs/env_name/Library/python3.6/сайт-пакеты/tensorflow/питон/рамки /graph_util_impl.py в convert_variables_to_constants (sess, input_graph_def, output_node_names, variable_names_whitelist, variable_names_blacklist)     300 source_op_name = get_input_name (map_name_to_node [source_op_name])     301, если map_name_to_node [source_op_name].op! = "VarHandleOp": → 302 повысить ValueError ("Не удается найти переменную, которая является входом"     303 "для ReadVariableOp.")     304

ValueError: Не удается найти переменную, которая является входом для ReadVariableOp.

Что вызывает это? Не уверен, если это из-за версии TF (я нахожусь на версии 1.14 для этого)