Tensorflow Различные способы экспорта и запуска графика в С++

Для импорта вашей обученной сети на С++ вам нужно экспортировать свою сеть, чтобы это сделать. После многого поиска и поиска почти никакой информации об этом, выяснилось, что мы должны использовать freeze_graph(), чтобы это сделать.

Благодаря новой версии Tensorflow версии 0.7 они добавили документацию.

Изучив документацию, я обнаружил, что существует несколько подобных методов, можете ли вы сказать, в чем разница между freeze_graph() и:   tf.train.export_meta_graph, поскольку он имеет схожие параметры, но, похоже, он также может использоваться для импорта моделей на С++ (я просто думаю, что разница в том, что для использования вывода файла этим методом вы можете использовать только import_graph_def() или что-то еще? )

Также один вопрос о том, как использовать write_graph(): В документах graph_def задается sess.graph_def, но в примерах в freeze_graph() это sess.graph.as_graph_def(). В чем разница между этими двумя?

Этот вопрос связан с этой проблемой.

Спасибо!

Ответы

Ответ 1

Здесь мое решение, использующее контрольные точки V2, введенные в TF 0.12.

Нет необходимости преобразовывать все переменные в константы или заморозить график.

Просто для ясности контрольная точка V2 выглядит так в моем каталоге models:

checkpoint  # some information on the name of the files in the checkpoint
my-model.data-00000-of-00001  # the saved weights
my-model.index  # probably definition of data layout in the previous file
my-model.meta  # protobuf of the graph (nodes and topology info)

Часть (сохранение) Python

with tf.Session() as sess:
    tf.train.Saver(tf.trainable_variables()).save(sess, 'models/my-model')

Если вы создаете Saver с помощью tf.trainable_variables(), вы можете сэкономить себе головную боль и пространство для хранения. Но, возможно, некоторым более сложным моделям нужны все данные для сохранения, а затем удалите этот аргумент в Saver, просто убедитесь, что вы создаете Saver после, ваш график создан. Также очень важно дать всем переменным/слоям уникальные имена, иначе вы можете запустить различные проблемы.

Часть (вывод) Python

with tf.Session() as sess:
    saver = tf.train.import_meta_graph('models/my-model.meta')
    saver.restore(sess, tf.train.latest_checkpoint('models/'))
    outputTensors = sess.run(outputOps, feed_dict=feedDict)

Часть С++ (вывод)

Обратите внимание, что checkpointPath не путь к любому из существующих файлов, просто их общий префикс. Если вы по ошибке поместите туда путь к файлу .index, TF не скажет вам, что это было неправильно, но он умрет во время вывода из-за неинициализированных переменных.

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

using namespace std;
using namespace tensorflow;

...
// set up your input paths
const string pathToGraph = "models/my-model.meta"
const string checkpointPath = "models/my-model";
...

auto session = NewSession(SessionOptions());
if (session == nullptr) {
    throw runtime_error("Could not create Tensorflow session.");
}

Status status;

// Read in the protobuf graph we exported
MetaGraphDef graph_def;
status = ReadBinaryProto(Env::Default(), pathToGraph, &graph_def);
if (!status.ok()) {
    throw runtime_error("Error reading graph definition from " + pathToGraph + ": " + status.ToString());
}

// Add the graph to the session
status = session->Create(graph_def.graph_def());
if (!status.ok()) {
    throw runtime_error("Error creating graph: " + status.ToString());
}

// Read weights from the saved checkpoint
Tensor checkpointPathTensor(DT_STRING, TensorShape());
checkpointPathTensor.scalar<std::string>()() = checkpointPath;
status = session->Run(
        {{ graph_def.saver_def().filename_tensor_name(), checkpointPathTensor },},
        {},
        {graph_def.saver_def().restore_op_name()},
        nullptr);
if (!status.ok()) {
    throw runtime_error("Error loading checkpoint from " + checkpointPath + ": " + status.ToString());
}

// and run the inference to your liking
auto feedDict = ...
auto outputOps = ...
std::vector<tensorflow::Tensor> outputTensors;
status = session->Run(feedDict, outputOps, {}, &outputTensors);

Ответ 2

Для прогнозирования (и любых других операций) вы можете сделать что-то вроде этого:

Прежде всего, в python вы должны называть ваши переменные или операции для будущего использования

self.init = tf.initialize_variables(tf.all_variables(), name="nInit")

После обучения расчеты так.., когда у вас есть назначенные переменные, проходят через все их и сохраняют в качестве констант на вашем графике. (почти то же самое можно сделать с помощью этого инструмента замораживания, но я обычно делаю это самостоятельно, проверяю "name = nWeights" в py и cpp ниже)

def save(self, filename):
    for variable in tf.trainable_variables():
        tensor = tf.constant(variable.eval())
        tf.assign(variable, tensor, name="nWeights")

    tf.train.write_graph(self.sess.graph_def, 'graph/', 'my_graph.pb', as_text=False)

Теперь перейдите на С++ и загрузите наш график и загрузите переменные из сохраненных констант:

void load(std::string my_model) {
        auto load_graph_status =
                ReadBinaryProto(tensorflow::Env::Default(), my_model, &graph_def);

        auto session_status = session->Create(graph_def);

        std::vector<tensorflow::Tensor> out;
        std::vector<string> vNames;

        int node_count = graph_def.node_size();
        for (int i = 0; i < node_count; i++) {
            auto n = graph_def.node(i);

            if (n.name().find("nWeights") != std::string::npos) {
                vNames.push_back(n.name());
            }
        }

        session->Run({}, vNames, {}, &out);

Теперь у вас есть все ваши веса нейронной сети или другие загруженные переменные.

Аналогично, вы можете выполнять другие операции (помните о именах?); создавать тензоры ввода и вывода соответствующего размера, заполнять тензор ввода данными и выполнять сеанс следующим образом:

auto operationStatus = session->Run(input, {"put_your_operation_here"}, {}, &out);