Я пытаюсь выполнить классификацию изображений с помощью API TensorFlow 2.2.0 C ++. Я обучил простой con vnet, используя пакет tenorflow 2.2 pip, установленный с помощью pip. Я сохраняю свою модель, используя функцию model.save(save_format='tf', filepath='../../graphs/test0', include_optimizer=True)
python, а затем пытаюсь загрузить и выполнить классификацию с помощью следующего кода:
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include "class_name.h"
#include "tensorflow/cc/ops/const_op.h"
#include "tensorflow/cc/ops/standard_ops.h"
#include "tensorflow/cc/saved_model/loader.h"
#include "tensorflow/cc/saved_model/tag_constants.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/graph/default_device.h"
#include "tensorflow/core/graph/graph_def_builder.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/stringpiece.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/io/path.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/init_main.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/types.h"
#include "tensorflow/core/public/session.h"
#include "tensorflow/core/util/command_line_flags.h"
using namespace tensorflow;
using tensorflow::Flag;
using tensorflow::Status;
using tensorflow::string;
using tensorflow::Tensor;
static Status ReadEntireFile(tensorflow::Env* env, const string& filename,
Tensor* output) {
tensorflow::uint64 file_size = 0;
TF_RETURN_IF_ERROR(env->GetFileSize(filename, &file_size));
string contents;
contents.resize(file_size);
std::unique_ptr<tensorflow::RandomAccessFile> file;
TF_RETURN_IF_ERROR(env->NewRandomAccessFile(filename, &file));
tensorflow::StringPiece data;
TF_RETURN_IF_ERROR(file->Read(0, file_size, &data, &(contents)[0]));
if (data.size() != file_size) {
return tensorflow::errors::DataLoss("Truncated read of '", filename,
"' expected ", file_size, " got ",
data.size());
}
output->scalar<tstring>()() = tstring(data);
return Status::OK();
}
//Read the image file, apply appropriate decoding depending on type of image
int TensorFromFile(string file_name, const int input_height, const int input_width, std::vector<Tensor>* out_tensors) {
Status status;
const float input_mean = 0.;
const float input_std = 255.;
auto root = tensorflow::Scope::NewRootScope();
using namespace ::tensorflow::ops; // NOLINT(build/namespaces)
string input_name = "file_reader";
string output_name = "normalized";
// read file_name into a tensor named input
Tensor input(tensorflow::DT_STRING, tensorflow::TensorShape());
status = (ReadEntireFile(tensorflow::Env::Default(), file_name, &input));
LOG(INFO) << status.ToString();
// use a placeholder to read input data
auto file_reader =
Placeholder(root.WithOpName("input"), tensorflow::DataType::DT_STRING);
std::vector<std::pair<string, tensorflow::Tensor>> inputs = {
{"input", input},
};
// Now try to figure out what kind of file it is and decode it.
const int wanted_channels = 3;
tensorflow::Output image_reader;
if (tensorflow::str_util::EndsWith(file_name, ".png")) {
image_reader = DecodePng(root.WithOpName("png_reader"), file_reader,
DecodePng::Channels(wanted_channels));
} else if (tensorflow::str_util::EndsWith(file_name, ".gif")) {
// gif decoder returns 4-D tensor, remove the first dim
image_reader =
Squeeze(root.WithOpName("squeeze_first_dim"),
DecodeGif(root.WithOpName("gif_reader"), file_reader));
} else if (tensorflow::str_util::EndsWith(file_name, ".bmp")) {
image_reader = DecodeBmp(root.WithOpName("bmp_reader"), file_reader);
} else {
// Assume if it's neither a PNG nor a GIF then it must be a JPEG.
image_reader = DecodeJpeg(root.WithOpName("jpeg_reader"), file_reader,
DecodeJpeg::Channels(wanted_channels));
}
// Now cast the image data to float so we can do normal math on it.
auto float_caster =
Cast(root.WithOpName("float_caster"), image_reader, tensorflow::DT_FLOAT);
// The convention for image ops in TensorFlow is that all images are expected
// to be in batches, so that they're four-dimensional arrays with indices of
// [batch, height, width, channel]. Because we only have a single image, we
// have to add a batch dimension of 1 to the start with ExpandDims().
auto dims_expander = ExpandDims(root, float_caster, 0);
// Bilinearly resize the image to fit the required dimensions.
auto resized = ResizeBilinear(
root, dims_expander,
Const(root.WithOpName("size"), {input_height, input_width}));
// Subtract the mean and divide by the scale.
Div(root.WithOpName(output_name), Sub(root, resized, {input_mean}),
{input_std});
// This runs the GraphDef network definition that we've just constructed, and
// returns the results in the output tensor.
tensorflow::GraphDef graph;
status = (root.ToGraphDef(&graph));
std::unique_ptr<tensorflow::Session> session(
tensorflow::NewSession(tensorflow::SessionOptions()));
status = (session->Create(graph));
LOG(INFO) << status.ToString();
status = (session->Run({inputs}, {output_name}, {}, out_tensors));
LOG(INFO) << status.ToString();
return 0;
}
int main(int argc, char* argv[]) {
using namespace ::tensorflow::ops;
tensorflow::Status status;
std::string delimiter = ".";
std::string ofilename;
std::vector<Tensor> inputs;
std::vector<Tensor> outputs;
std::string graph_path = "../../graphs/test1/";
std::string image_path = "../../graphs/test0.png";
std::string mdlpath(graph_path);
std::string imgpath(image_path);
int32 inputdim = 32;
//std::unique_ptr<tensorflow::Session> session(tensorflow::NewSession({}));
tensorflow::GraphDef graph;
LOG(INFO) << "OK";
tensorflow::SavedModelBundle model;
Status load_status = tensorflow::LoadSavedModel(tensorflow::SessionOptions(), tensorflow::RunOptions(), graph_path, {tensorflow::kSavedModelTagServe}, &model);
LOG(INFO) << model.meta_graph_def.has_graph_def();
LOG(INFO) << load_status.ToString() << std::endl;
graph = model.meta_graph_def.graph_def();
//add graph to scope
//status = session->Create(graph);
//if (!status.ok()) {
// std::cout << status.ToString() << "\n";
// return -1;
//}
LOG(INFO) << "OK";
//Read input image, assuming to be a sqaure image
if (TensorFromFile(imgpath, inputdim, inputdim, &inputs)) {
LOG(ERROR) << "Image reading failed"
<< "\n";
return -1;
}
LOG(INFO) << "OK L1";
LOG(INFO) << typeid(inputs).name();
std::cout << "input dimension of the image: " << inputs[0].DebugString() << std::endl;
auto shape = graph.node().Get(0).attr().at("shape").shape();
for (int i = 0; i < shape.dim_size(); i++) {
std::cout << shape.dim(i).size() << std::endl;
}
LOG(INFO) << "";
for (int i = 0; i < graph.node_size(); i++) {
LOG(INFO) << graph.node(i).name();
}
LOG(INFO) << "";
//get the appropriate input and out layer names from the graph/mode to execute
auto inputlayer = graph.node(0).name();
LOG(INFO) << "OK A1";
LOG(INFO) << inputlayer;
auto outputlayer = graph.node(graph.node_size() - 1).name();
LOG(INFO) << "OK A2";
LOG(INFO) << outputlayer;
const Tensor& resized_tensor = inputs[0];
std::vector<std::pair<string, tensorflow::Tensor>> inputsPair = {
{inputlayer, inputs[0]},
};
status = model.GetSession()->Run({{inputlayer, resized_tensor}}, {outputlayer}, {}, &outputs);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
return -1;
}
std::cout << "Output dimension of the image" << outputs[0].DebugString() << std::endl;
//create filename
ofilename.append(imgpath.substr(0, imgpath.find(delimiter)));
ofilename.append("_mask.png");
std::cout << "output filename: " << ofilename << std::endl;
//Now write this to a image file
//if (TensorToFile(ofilename, outputs, threshold)) return -1;
model.GetSession()->Close();
return 0;
}
Это дает этот журнал:
2020-06-21 00:26:13.628887: I main.cpp:149] OK
2020-06-21 00:26:13.629011: I tensorflow/cc/saved_model/reader.cc:31] Reading SavedModel from: ../../graphs/test1/
2020-06-21 00:26:13.635410: I tensorflow/cc/saved_model/reader.cc:54] Reading meta graph with tags { serve }
2020-06-21 00:26:13.635458: I tensorflow/cc/saved_model/loader.cc:295] Reading SavedModel debug info (if present) from: ../../graphs/test1/
2020-06-21 00:26:13.674455: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 1995615000 Hz
2020-06-21 00:26:13.674913: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x5559c0d43e20 initialized for platform Host (this does not guarantee that XLA will be used). Devices:
2020-06-21 00:26:13.674942: I tensorflow/compiler/xla/service/service.cc:176] StreamExecutor device (0): Host, Default Version
2020-06-21 00:26:13.709421: I tensorflow/cc/saved_model/loader.cc:234] Restoring SavedModel bundle.
2020-06-21 00:26:13.770175: I tensorflow/cc/saved_model/loader.cc:183] Running initialization op on SavedModel bundle at path: ../../graphs/test1/
2020-06-21 00:26:13.790109: I tensorflow/cc/saved_model/loader.cc:364] SavedModel load for tags { serve }; Status: success: OK. Took 161100 microseconds.
2020-06-21 00:26:13.790167: I main.cpp:154] 1
2020-06-21 00:26:13.790187: I main.cpp:155] OK
2020-06-21 00:26:13.793408: I main.cpp:166] OK
2020-06-21 00:26:13.793553: I main.cpp:71] OK
2020-06-21 00:26:13.794331: I main.cpp:123] OK
2020-06-21 00:26:13.800549: I main.cpp:125] OK
2020-06-21 00:26:13.800864: I main.cpp:175] OK L1
2020-06-21 00:26:13.800891: I main.cpp:177] St6vectorIN10tensorflow6TensorESaIS1_EE
input dimension of the image: Tensor<type: float shape: [1,32,32,3] values: [[[0.596078455 0.690196097 0.729411781]]]...>
3
3
3
32
2020-06-21 00:26:13.800986: I main.cpp:184]
2020-06-21 00:26:13.801005: I main.cpp:186] conv2d/kernel
2020-06-21 00:26:13.801022: I main.cpp:186] conv2d/kernel/Read/ReadVariableOp
2020-06-21 00:26:13.801037: I main.cpp:186] conv2d/bias
2020-06-21 00:26:13.801053: I main.cpp:186] conv2d/bias/Read/ReadVariableOp
2020-06-21 00:26:13.801068: I main.cpp:186] conv2d_1/kernel
2020-06-21 00:26:13.801084: I main.cpp:186] conv2d_1/kernel/Read/ReadVariableOp
2020-06-21 00:26:13.801100: I main.cpp:186] conv2d_1/bias
2020-06-21 00:26:13.801116: I main.cpp:186] conv2d_1/bias/Read/ReadVariableOp
2020-06-21 00:26:13.801132: I main.cpp:186] conv2d_2/kernel
2020-06-21 00:26:13.801148: I main.cpp:186] conv2d_2/kernel/Read/ReadVariableOp
2020-06-21 00:26:13.801163: I main.cpp:186] conv2d_2/bias
2020-06-21 00:26:13.801179: I main.cpp:186] conv2d_2/bias/Read/ReadVariableOp
2020-06-21 00:26:13.801195: I main.cpp:186] conv2d_3/kernel
2020-06-21 00:26:13.801210: I main.cpp:186] conv2d_3/kernel/Read/ReadVariableOp
2020-06-21 00:26:13.801224: I main.cpp:186] conv2d_3/bias
2020-06-21 00:26:13.801240: I main.cpp:186] conv2d_3/bias/Read/ReadVariableOp
2020-06-21 00:26:13.801255: I main.cpp:186] dense/kernel
2020-06-21 00:26:13.801271: I main.cpp:186] dense/kernel/Read/ReadVariableOp
2020-06-21 00:26:13.801286: I main.cpp:186] dense/bias
2020-06-21 00:26:13.801302: I main.cpp:186] dense/bias/Read/ReadVariableOp
2020-06-21 00:26:13.801317: I main.cpp:186] dense_1/kernel
2020-06-21 00:26:13.801332: I main.cpp:186] dense_1/kernel/Read/ReadVariableOp
2020-06-21 00:26:13.801348: I main.cpp:186] dense_1/bias
2020-06-21 00:26:13.801363: I main.cpp:186] dense_1/bias/Read/ReadVariableOp
2020-06-21 00:26:13.801378: I main.cpp:186] dense_2/kernel
2020-06-21 00:26:13.801394: I main.cpp:186] dense_2/kernel/Read/ReadVariableOp
2020-06-21 00:26:13.801409: I main.cpp:186] dense_2/bias
2020-06-21 00:26:13.801425: I main.cpp:186] dense_2/bias/Read/ReadVariableOp
2020-06-21 00:26:13.801440: I main.cpp:186] dense_3/kernel
2020-06-21 00:26:13.801455: I main.cpp:186] dense_3/kernel/Read/ReadVariableOp
2020-06-21 00:26:13.801471: I main.cpp:186] dense_3/bias
2020-06-21 00:26:13.801486: I main.cpp:186] dense_3/bias/Read/ReadVariableOp
2020-06-21 00:26:13.801502: I main.cpp:186] NoOp
2020-06-21 00:26:13.801517: I main.cpp:186] Const
2020-06-21 00:26:13.801532: I main.cpp:186] serving_default_input
2020-06-21 00:26:13.801548: I main.cpp:186] StatefulPartitionedCall
2020-06-21 00:26:13.801564: I main.cpp:186] saver_filename
2020-06-21 00:26:13.801580: I main.cpp:186] StatefulPartitionedCall_1
2020-06-21 00:26:13.801596: I main.cpp:186] StatefulPartitionedCall_2
2020-06-21 00:26:13.801611: I main.cpp:188]
2020-06-21 00:26:13.801625: I main.cpp:192] OK A1
2020-06-21 00:26:13.801640: I main.cpp:193] conv2d/kernel
2020-06-21 00:26:13.801655: I main.cpp:195] OK A2
2020-06-21 00:26:13.801670: I main.cpp:196] StatefulPartitionedCall_2
2020-06-21 00:26:13.813969: E main.cpp:206] Invalid argument: Expects arg[0] to be resource but float is provided
I не очень хорошо понимаю этот код ошибки. Что означает Invalid argument: Expects arg[0] to be resource but float is provided
?