Я обучил некоторые модели, используя тензорный поток 1.5.1, и у меня есть контрольные точки для этих моделей (включая файлы .ckpt и .meta). Теперь я хочу сделать вывод на C ++, используя эти файлы.

В python я бы сделал следующее, чтобы сохранить и загрузить график и контрольные точки. для экономии:

    images = tf.placeholder(...) // the input layer
    //the graph def
    output = tf.nn.softmax(net) // the output layer
    tf.add_to_collection('images', images)
    tf.add_to_collection('output', output)

Для вывода я восстанавливаю график и контрольную точку, а затем восстанавливаю входные и выходные слои из коллекций следующим образом:

    meta_file = './models/last-100.meta'
    ckpt_file = './models/last-100'
    with tf.Session() as sess:
        saver = tf.train.import_meta_graph(meta_file)
        saver.restore(sess, ckpt_file)
        images = tf.get_collection('images')
        output = tf.get_collection('output')
        outputTensors = sess.run(output, feed_dict={images: np.array(an_image)})

Теперь, предполагая, что я сделал сохранение в python, как обычно, как я могу сделать вывод и восстановить в c ++ с помощью простого кода, как в python?

Я нашел примеры и учебные пособия, но для версий tensorflow 0.7 0.12 и тот же код не работает для версии 1.5. Я не нашел руководств по восстановлению моделей с помощью C ++ API на сайте tensorflow.

2
a.ewais 17 Фев 2018 в 15:37

1 ответ

Лучший ответ

Ради этой ветки. Я перефразирую свой комментарий в ответ.

Для публикации полного примера потребуется либо установка CMake, либо размещение файла в определенном каталоге для запуска bazel. Поскольку я предпочитаю первый способ, и он нарушит все ограничения в этом сообщении, чтобы охватить все части, я хотел бы перенаправить на полная реализация на C99, C ++, GO без Bazel, которую я тестировал для TF> v1.5.

Загрузка графика в C ++ не намного сложнее, чем в Python, учитывая , что вы уже скомпилировали TensorFlow из исходников.

Начните с создания MWE, который создает очень дамповый сетевой граф - это всегда хорошая идея, чтобы понять, как все работает:

import tensorflow as tf

x = tf.placeholder(tf.float32, shape=[1, 2], name='input')
output = tf.identity(tf.layers.dense(x, 1), name='output')

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    saver = tf.train.Saver(tf.global_variables())
    saver.save(sess, './exported/my_model')

Вероятно, здесь, на SO, есть множество ответов по этой части. Так что я оставил его здесь без дальнейших объяснений.

Загрузка в Python

Прежде чем делать что-то на других языках, мы можем попытаться сделать это на Python должным образом - в том смысле, что нам просто нужно переписать его на C ++. Даже восстановление в python очень просто:

import tensorflow as tf

with tf.Session() as sess:

    # load the computation graph
    loader = tf.train.import_meta_graph('./exported/my_model.meta')
    sess.run(tf.global_variables_initializer())
    loader = loader.restore(sess, './exported/my_model')

    x = tf.get_default_graph().get_tensor_by_name('input:0')
    output = tf.get_default_graph().get_tensor_by_name('output:0')

Это бесполезно, поскольку большинство этих конечных точек API не существует в C ++ API (пока?). Альтернативная версия была бы

import tensorflow as tf

with tf.Session() as sess:

    metaGraph = tf.train.import_meta_graph('./exported/my_model.meta')
    restore_op_name = metaGraph.as_saver_def().restore_op_name
    restore_op = tf.get_default_graph().get_operation_by_name(restore_op_name)
    filename_tensor_name = metaGraph.as_saver_def().filename_tensor_name
    sess.run(restore_op, {filename_tensor_name: './exported/my_model'})


    x = tf.get_default_graph().get_tensor_by_name('input:0')
    output = tf.get_default_graph().get_tensor_by_name('output:0')

Подожди. Вы всегда можете использовать print(dir(object)), чтобы получить такие свойства, как restore_op_name, .... Восстановление модели - это операция в TensorFlow, как и любая другая операция. Мы просто вызываем эту операцию и предоставляем путь (тензор строки) в качестве входных данных. Мы даже можем написать нашу собственную операцию restore

def restore(sess, metaGraph, fn):
    restore_op_name = metaGraph.as_saver_def().restore_op_name   # u'save/restore_all'
    restore_op = tf.get_default_graph().get_operation_by_name(restore_op_name)
    filename_tensor_name = metaGraph.as_saver_def().filename_tensor_name  # u'save/Const'
    sess.run(restore_op, {filename_tensor_name: fn})

Даже это выглядит странно, но теперь это очень помогает делать то же самое в C ++.

Загрузка в C ++

Начиная с обычных вещей

#include <tensorflow/core/public/session.h>
#include <tensorflow/core/public/session_options.h>
#include <tensorflow/core/protobuf/meta_graph.pb.h>
#include <string>
#include <iostream>

typedef std::vector<std::pair<std::string, tensorflow::Tensor>> tensor_dict;

int main(int argc, char const *argv[]) {

  const std::string graph_fn = "./exported/my_model.meta";
  const std::string checkpoint_fn = "./exported/my_model";

  // prepare session
  tensorflow::Session *sess;
  tensorflow::SessionOptions options;
  TF_CHECK_OK(tensorflow::NewSession(options, &sess));

  // here we will put our loading of the graph and weights

  return 0;
}

Вы должны иметь возможность скомпилировать это, либо поместив его в репозиторий TensorFlow и используя bazel, либо просто следуя инструкциям здесь использовать CMake.

Нам нужно создать такой meta_graph, созданный tf.train.import_meta_graph. Это можно сделать

tensorflow::MetaGraphDef graph_def;
TF_CHECK_OK(ReadBinaryProto(tensorflow::Env::Default(), graph_fn, &graph_def));

В C ++ чтение графика из файла не то же самое, что импорт графика в Python. Нам нужно создать этот график в сеансе с помощью

TF_CHECK_OK(sess->Create(graph_def.graph_def()));

Взглянув на странную функцию python restore выше:

restore_op_name = metaGraph.as_saver_def().restore_op_name
restore_op = tf.get_default_graph().get_operation_by_name(restore_op_name)
filename_tensor_name = metaGraph.as_saver_def().filename_tensor_name

Мы можем закодировать эквивалентную часть на C ++

const std::string restore_op_name = graph_def.saver_def().restore_op_name()
const std::string filename_tensor_name = graph_def.saver_def().filename_tensor_name()

Имея это на месте, мы просто запускаем операцию

sess->Run(feed_dict,     // inputs
          {},            // output_tensor_names (we do not need them)
          {restore_op},  // target_node_names
          nullptr)       // outputs (there are no outputs this time)

Создание feed_dict, вероятно, представляет собой отдельную публикацию, и этот ответ уже достаточно длинный. Он охватывает только самое важное. Я хотел бы перенаправить на полную реализацию на C99, C ++, GO без Bazel, которую я тестировал для TF> v1.5. Это не так уж и сложно - это может занять очень много времени в случае обычная версия C.

5
Patwie 20 Фев 2018 в 23:43