请尽快修复这个问题。一个无法正确导出训练好的模型的机器学习API是致命的。
系统信息
- 是否编写了自定义代码(与在TensorFlow中使用的库存示例脚本相反):是
- OS平台和发行版(例如,Linux Ubuntu 16.04):Ubuntu 20.04
- 移动设备(例如iPhone 8,Pixel 2,Samsung Galaxy),如果问题发生在移动设备上:否
- 从哪里安装的TensorFlow(源代码或二进制文件):源代码
- TensorFlow版本(使用以下命令):2.4.0
- Python版本:3.8.5
- Bazel版本(如果从源代码编译):3.1.0
- GCC/编译器版本(如果从源代码编译):gcc-10
- CUDA/cuDNN版本:cuda 11已安装但未使用
- GPU型号和内存:mx150
- protobuf版本:3.9.2 / 3.10.1(尝试了两者,前者是指定的版本),3.14.0根本无法编译
- abseil版本:abseil-cpp-20200225,
absl/bas/option.h
配置为代码末尾附加的版本。
描述当前行为
每当在代码中调用FreezeSavedModel函数时,tensorflow::ClientSession都无法正常执行。
如果将FreezeSavedModel函数注解掉,一切都会正常工作。
显示错误消息。
2020-12-23 10:10:29.483878: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2020-12-23 10:10:29.502660: I tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2099940000 Hz
[libprotobuf FATAL external/com_google_protobuf/src/google/protobuf/map.h:1060] CHECK failed: it != end(): key not found: Reciprocal
terminate called after throwing an instance of 'google::protobuf::FatalException'
what(): CHECK failed: it != end(): key not found: Reciprocal
Aborted (core dumped)
描述预期行为
无错误执行。
2020-12-23 10:01:51.025954: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2020-12-23 10:01:51.046594: I tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2099940000 Hz
Tensor<type: float shape: [1,150,150,3] values: [[[0.34117648 0.313725501 0.24313727]]]...>
Tensor<type: float shape: [150,150,3] values: [[0.34117648 0.313725501 0.24313727]]...>
独立代码以重现问题
CatDogCnn的头文件
/**
* @file CatDogCnn.hpp
* @author Hung-Tien Huang (paperbus72@gmail.com)
* @brief A simplified AlexNet for classifying cats and dogs.
* @version 0.1
* @date 2020-12-16
*
* @copyright Copyright (c) 2020
*
*/
#ifndef INCLUDE_TUTORIAL_02_CAT_DOG_CNN_CAT_DOG_CNN_H_
#define INCLUDE_TUTORIAL_02_CAT_DOG_CNN_CAT_DOG_CNN_H_
#include <tensorflow/cc/client/client_session.h>
#include <tensorflow/cc/framework/gradients.h>
#include <tensorflow/cc/ops/image_ops.h>
#include <tensorflow/cc/ops/standard_ops.h>
#include <tensorflow/cc/ops/state_ops.h>
#include <tensorflow/cc/tools/freeze_saved_model.h>
#include <tensorflow/core/framework/tensor.h>
#include <tensorflow/core/lib/io/path.h>
#include <tensorflow/core/platform/types.h>
#include <tensorflow/core/public/session.h>
#include <tensorflow/core/summary/summary_file_writer.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <unordered_set>
#include <variant>
#include <vector>
namespace tensorflow_tutorial {
class CatDogCnn {
public:
/**
* @brief Construct a new Cat Dog Cnn object. Assume images are square.
*
* @param image_size the size of image
* @param num_of_channels number of channels
*/
CatDogCnn(const int image_size, const int num_of_channels);
/**
* @brief Create a Graph for Input Image. If unstack is false, the
* output_image_tensor_node_ will have shape of [height, width, channel]. If
* unstack is true, the ouput_image_tensor_node_ will have shape of [batch,
* height, width, channel]. In addition, only the first element will be stored
* to output_image_tensor_node_
*
* @param unstack unstack or not (set to true if batch mode, false otherwise)
* @return tensorflow::Status of the current scope
*/
tensorflow::Status CreateGraphForInputImage(bool unstack);
/**
* @brief Convert an image to a tensor
*
* @param path_to_image path to the jpeg file to be converted
* @param out_tensor pointer to output tensor
* @return tensorflow::Status of the current scope
*/
tensorflow::Status ConvertImageToTensor(
const std::filesystem::path& path_to_image,
tensorflow::Tensor* out_tensor);
/**
* @brief Load all the image in a directory represented by dataset_root_path.
* std::vector<std::pair<std::string, float>> folder_label is a vector of
* ["string representation of class", and float representation of the class].
* std::vector<std::tuple<tensorflow::Tensor, float, std::filesystem::path>>
* image_tensors is ["tensor representation of an image", type of class in
* float, and the path to the actual image file]; its content will get cleared
* prior to loading
*
* @param dataset_root_path path to the dataset's root folder
* @param folder_label reference to the manually assigned type and each type's
* numerical representation; label string MUST match the actual folder name
* @param image_tensors pointer to the vector which images are to be stored
* @return tensorflow::Status of the current scope
*/
tensorflow::Status LoadDataset(
const std::filesystem::path& dataset_root_path,
const std::vector<std::pair<std::string, float>>& folder_label,
std::vector<std::tuple<tensorflow::Tensor, float, std::filesystem::path>>*
image_tensors);
/**
* @brief Load entire dataset into memory and chunk them into batches.
*
* @param dataset_root_path path to the dataset's root folder
* @param folder_label reference to the manually assigned type and each type's
* numerical representation; label string MUST match the actual folder name
* @param batch_size number of elements per batch
* @param image_batch pointer to a vector of all image batches
* @param label_batch pointer to a vector of all label batches
* @return tensorflow::Status of the current scope
*/
tensorflow::Status LoadBatach(
const std::filesystem::path& dataset_root_path,
const std::vector<std::pair<std::string, float>>& folder_labels,
const size_t batch_size, std::vector<tensorflow::Tensor>* image_batch,
std::vector<tensorflow::Tensor>* label_batch);
/**
* @brief returns an input object that performs Gloron/Xavier normal
* initialization
*
* @param scope
* @param in_chan
* @param out_chan
* @param filter_side
* @return tensorflow::Input an input node that returns initialized
* parameters
*/
tensorflow::Input GlorotUniformInitializer(const tensorflow::Scope& scope,
const int input_channels,
const int output_channels,
const int filter_side = 0);
/**
* @brief Add convolution layer with relu and max pool. Weights are
* initialized with Glorot/Xavier univorm initialization.
*
* @param layer_index
* @param scope
* @param input_channels
* @param output_channels
* @param filter_side
* @param input
* @return tensorflow::Input
*/
tensorflow::Output AddConvLayer(const std::string& layer_index,
const tensorflow::Scope& scope,
const int input_channels,
const int output_channels,
const int filter_side,
const tensorflow::Input& input);
/**
* @brief Add fully connected layer with or withot activation based on bool
* need_activation variable.
*
* @param layer_index
* @param scope
* @param input_channels
* @param output_channels
* @param need_activation whether to append additional Relu at the end
* @param input
* @return tensorflow::Input
*/
tensorflow::Output AddDenseLayer(const std::string& layer_index,
const tensorflow::Scope& scope,
const int input_channels,
const int output_channels,
const bool need_activation,
const tensorflow::Input& input);
/**
* @brief Create a CNN Graph
*
* @param filter_side
* @return tensorflow::Status
*/
tensorflow::Status CreateGraphForCnn(const int filter_side);
/**
* @brief Create an Optimization(backpropagation) graph.
*
* @param learning_rate
* @return tensorflow::Status
*/
tensorflow::Status CreateGraphForOptimization(const float learning_rate);
/**
* @brief Use the image_batch and label_batch to train the neural network.
*
* @param image_batch
* @param label_batch
* @param results
* @param loss
* @return tensorflow::Status
*/
tensorflow::Status Train(const tensorflow::Tensor& image_batch,
const tensorflow::Tensor& label_batch,
std::vector<float>* results, float* loss);
/**
* @brief Use the image_batch and label_batch provided to validate the neural
* network.
*
* @param image_batch
* @param label_batch
* @param results
* @return tensorflow::Status
*/
tensorflow::Status Validate(const tensorflow::Tensor& image_batch,
const tensorflow::Tensor& label_batch,
std::vector<float>* results);
/**
* @brief Executes all the Assgin Variable nodes
*
* @return tensorflow::Status
*/
tensorflow::Status Initialize();
/**
* @brief Freeze the model
*
* @param path
* @return tensorflow::Status
*/
tensorflow::Status FreezeModel(const std::filesystem::path& path);
/**
* @brief Load a frozen model from file.
*
* @param path
* @return tensorflow::Status
*/
tensorflow::Status LoadFrozenModel(const std::filesystem::path& path);
/**
* @brief Predict from frozen model
*
* @param image
* @param result
* @return tensorflow::Status
*/
tensorflow::Status PredictFromFrozen(const tensorflow::Tensor& image,
int* result);
private:
const int kImageSize; // assume squre picture
const int kNumOfChannels; // RGB
// load image
tensorflow::Scope load_image_scope_;
tensorflow::Output input_image_filename_node_;
tensorflow::Output output_image_tensor_node_;
// networks
tensorflow::Scope network_root_scope_;
std::unique_ptr<tensorflow::ClientSession> train_session_ptr_;
std::unique_ptr<tensorflow::Session> frozen_session_ptr_;
std::map<std::string, tensorflow::Output> variables_;
std::map<std::string, tensorflow::TensorShape> variable_shapes_;
std::map<std::string, tensorflow::Output> variable_assign_nodes_;
// cnn variables
tensorflow::Output input_batch_tensor_node_;
tensorflow::Output input_labels_tensor_node_;
tensorflow::Output drop_rate_node_;
tensorflow::Output skip_drop_node_;
tensorflow::Output output_classification_;
tensorflow::Output squared_difference_node_;
tensorflow::Output output_loss_logits_node_;
const std::string kInputNodeName;
const std::string kInputDropRateNodeName;
const std::string kInputSkipDropNodeName;
const std::string kOutputClassificationNodeName;
// back propagation
std::vector<tensorflow::Operation> adam_operations;
std::vector<tensorflow::Output> weights_biases_;
/**
* @brief Shuffle the dataset randomly.
*
* @param image_tensors
*/
static void ShuffleSet(
std::vector<std::tuple<tensorflow::Tensor, float, std::filesystem::path>>*
image_tensors);
};
} // namespace tensorflow_tutorial
#endif // INCLUDE_TUTORIAL_02_CAT_DOG_CNN_CAT_DOG_CNN_H_
CatDogCnn.cpp
/**
* @file CatDogCnn.cpp
* @author Hung-Tien Huang (paperbus72@gmail.com)
* @brief
* @version 0.1
* @date 2020-12-16
*
* @copyright Copyright (c) 2020
*
*/
#include <tensorflow_tutorial/tutorial_02/CatDogCnn/CatDogCnn.hpp>
namespace tensorflow_tutorial {
CatDogCnn::CatDogCnn(const int image_size, const int num_of_channels)
: kImageSize{image_size},
kNumOfChannels{num_of_channels},
load_image_scope_{tensorflow::Scope::NewRootScope()},
network_root_scope_{tensorflow::Scope::NewRootScope()},
kInputNodeName{"input_batch"},
kInputDropRateNodeName{"drop_rate"},
kInputSkipDropNodeName{"skip_drop"},
kOutputClassificationNodeName{"output_classification"} { /*empty*/
}
tensorflow::Status CatDogCnn::CreateGraphForInputImage(bool unstack) {
input_image_filename_node_ = tensorflow::ops::Placeholder{
load_image_scope_.NewSubScope("input_file_name"),
tensorflow::DataType::DT_STRING};
tensorflow::ops::ReadFile read_file_node{
load_image_scope_.NewSubScope("read_file"), input_image_filename_node_};
tensorflow::ops::DecodeJpeg decode_image_node{
load_image_scope_.NewSubScope("decode_image"), read_file_node};
// convert each pixel to float
tensorflow::ops::Cast cast_float_node{
load_image_scope_.NewSubScope("cast_float"), decode_image_node,
tensorflow::DataType::DT_FLOAT};
// [height, width channel] -> [batch, height, width, channel]
tensorflow::ops::ExpandDims expand_batch_dim_node{
load_image_scope_.NewSubScope("exapnd_batch_dim"), cast_float_node, 0};
// resize image to square
tensorflow::ops::ResizeBilinear resize_image_node(
load_image_scope_.NewSubScope("resize"), expand_batch_dim_node,
{kImageSize, kImageSize});
// divide each pixel by 255 so that each pixel is [0, 1]
tensorflow::ops::Div normalize_image_node{
load_image_scope_.NewSubScope("normalize_image"),
resize_image_node,
{255.f}};
if (unstack) {
/**
* @todo Unstack has documentation mismach. According to documentation, it
* should be unstack along 0th axis. However, doing so cause segmentation
* fault. When unstack along 1st axis, it is the expected behaviour when
* unstack along 0th axis
*
*/
// unstack along batch axis
// array of [height, width, channel]
tensorflow::ops::Unstack unstack_image_node{
load_image_scope_.NewSubScope("unstack_image"), normalize_image_node,
1};
output_image_tensor_node_ = unstack_image_node[0];
} else {
output_image_tensor_node_ = normalize_image_node;
}
return load_image_scope_.status();
}
tensorflow::Status CatDogCnn::ConvertImageToTensor(
const std::filesystem::path& path_to_image,
tensorflow::Tensor* out_tensor) {
if (load_image_scope_.ok() == false) {
return load_image_scope_.status();
}
if (path_to_image.extension().string() != ".jpg" &&
path_to_image.extension().string() != ".jpeg") {
std::cerr << path_to_image.string() << " is NOT *.jpeg" << std::endl;
/**
* @todo cannot create status with error message. change back to
* tensorflow::errors::InvalidArgument when tensorflow fix the bug
*
*/
// return tensorflow::errors::InvalidArgument("Image must be jpeg encoded");
// return tensorflow::Status{tensorflow::errors::Code::INVALID_ARGUMENT,
// err_msg,};
return tensorflow::Status::OK();
}
std::vector<tensorflow::Tensor> out_tensors;
tensorflow::ClientSession client_session{load_image_scope_};
TF_CHECK_OK(
client_session.Run({{input_image_filename_node_, path_to_image.string()}},
{output_image_tensor_node_}, &out_tensors));
(*out_tensor) = std::move(out_tensors[0]);
return load_image_scope_.status();
}
tensorflow::Status CatDogCnn::LoadDataset(
const std::filesystem::path& dataset_root_path,
const std::vector<std::pair<std::string, float>>& folder_labels,
std::vector<std::tuple<tensorflow::Tensor, float, std::filesystem::path>>*
image_tensors) {
image_tensors->clear();
// for each class
for (const std::pair<std::string, float>& current_class : folder_labels) {
// create path to the curren class directory
const std::filesystem::path current_class_path =
dataset_root_path / current_class.first;
// if the current class directory exists
if (std::filesystem::exists(current_class_path)) {
// for each file inside the current class directory
for (const std::filesystem::directory_entry& p :
std::filesystem::directory_iterator{current_class_path}) {
const std::filesystem::path& current_image_path = p.path();
// continue if not a jpeg file
if (current_image_path.extension().string() != ".jpeg" &&
current_image_path.extension().string() != ".jpg") {
std::cerr << current_image_path << " is NOT a jpeg" << std::endl;
continue;
}
tensorflow::Tensor current_image_tensor;
TF_RETURN_IF_ERROR(
ConvertImageToTensor(current_image_path, ¤t_image_tensor));
image_tensors->emplace_back(
std::tuple<tensorflow::Tensor, float, std::filesystem::path>{
std::move(current_image_tensor), current_class.second,
std::move(current_image_path)});
}
ShuffleSet(image_tensors);
image_tensors->shrink_to_fit();
} else {
std::cerr << current_class_path << " does NOT exists" << std::endl;
/**
* @todo status is not ok!!
*
*/
return tensorflow::Status::OK();
}
}
return load_image_scope_.status();
}
void CatDogCnn::ShuffleSet(
std::vector<std::tuple<tensorflow::Tensor, float, std::filesystem::path>>*
image_tensors) {
uint_fast32_t seed = static_cast<uint_fast32_t>(
std::chrono::system_clock::now().time_since_epoch().count());
std::shuffle(image_tensors->begin(), image_tensors->end(),
std::mt19937{seed});
}
tensorflow::Status CatDogCnn::LoadBatach(
const std::filesystem::path& dataset_root_path,
const std::vector<std::pair<std::string, float>>& folder_labels,
const size_t batch_size, std::vector<tensorflow::Tensor>* image_batch,
std::vector<tensorflow::Tensor>* label_batch) {
// clear batch prior to begin
image_batch->clear();
label_batch->clear();
// load entire dataset in
std::vector<std::tuple<tensorflow::Tensor, float, std::filesystem::path>>
entire_dataset;
TF_RETURN_IF_ERROR(
LoadDataset(dataset_root_path, folder_labels, &entire_dataset));
// start to chunk batch
auto begin = entire_dataset.begin();
auto end = entire_dataset.begin() + batch_size;
size_t num_of_batches = entire_dataset.size() / batch_size;
if (num_of_batches * batch_size < entire_dataset.size()) {
num_of_batches = num_of_batches + 1;
}
for (size_t i = 0; i < num_of_batches; ++i) {
// if end exceeds the entire_dataset.end()
if (end > entire_dataset.end()) {
end = entire_dataset.end();
}
// break Tensor and label into two std::vector
std::vector<tensorflow::Input> current_batch_image_v{};
std::vector<tensorflow::Input> current_batch_label_v{};
for (auto curr = begin; curr < end; ++curr) {
current_batch_image_v.emplace_back(std::move(std::get<0>(*curr)));
tensorflow::Tensor tmp{tensorflow::DataType::DT_FLOAT, {1}};
tmp.scalar<float>()(0) = std::get<1>(*curr);
current_batch_label_v.emplace_back(std::move(tmp));
}
// convert std::vector to tensorflow::InputList
tensorflow::InputList current_batch_image{current_batch_image_v};
tensorflow::InputList current_batch_label{current_batch_label_v};
// stack all the image in current batch into one big tensor
tensorflow::Scope root = tensorflow::Scope::NewRootScope();
tensorflow::ops::Stack stack_current_image_batch_node{root,
current_batch_image};
tensorflow::ops::Stack stack_current_label_batch_node{root,
current_batch_label};
TF_CHECK_OK(root.status());
tensorflow::ClientSession client_session{root};
std::vector<tensorflow::Tensor> current_batch;
TF_CHECK_OK(client_session.Run(
{stack_current_image_batch_node, stack_current_label_batch_node},
¤t_batch));
image_batch->emplace_back(std::move(current_batch[0]));
label_batch->emplace_back(std::move(current_batch[1]));
// increment the entire_dataset being processed
begin = end;
// break if begin reaches the end
if (begin == entire_dataset.end()) {
break;
}
end = end + batch_size;
}
return tensorflow::Status::OK();
}
tensorflow::Input CatDogCnn::GlorotUniformInitializer(
const tensorflow::Scope& scope, const int input_channels,
const int output_channels, const int filter_side) {
float std = 0.0f;
tensorflow::Tensor tensor_shape;
if (filter_side == 0) { // dense
std = std::sqrt(6.0f / (input_channels + output_channels));
tensor_shape = tensorflow::Tensor{tensorflow::DT_INT64, {2}};
auto v = tensor_shape.vec<tensorflow::int64>();
v(0) = input_channels;
v(1) = output_channels;
} else { // conv
std = std::sqrt(6.0f / (filter_side * filter_side *
(input_channels + output_channels)));
tensor_shape = tensorflow::Tensor{tensorflow::DT_INT64, {4}};
auto v = tensor_shape.vec<tensorflow::int64>();
v(0) = filter_side;
v(1) = filter_side;
v(2) = input_channels;
v(3) = output_channels;
}
// std::cout << tensor_shape.DebugString() << std::endl;
// rand_node returns tensor with value [0,1]
tensorflow::ops::RandomUniform rand_node{scope, tensor_shape,
tensorflow::DT_FLOAT};
// ([0, 1] - [.5, .5]) * 2.0f = [-1, 1]
return tensorflow::ops::Multiply{
scope, tensorflow::ops::Sub{scope, rand_node, 0.5f}, std * 2.0f};
}
tensorflow::Output CatDogCnn::AddConvLayer(const std::string& layer_index,
const tensorflow::Scope& scope,
const int input_channels,
const int output_channels,
const int filter_side,
const tensorflow::Input& input) {
const std::string kCurrWeightStr{"weight_" + layer_index};
const std::string kCurrBiasStr{"bias_" + layer_index};
// conv2d weights
tensorflow::TensorShape shape{filter_side, filter_side, input_channels,
output_channels};
variables_[kCurrWeightStr] =
tensorflow::ops::Variable{scope.NewSubScope(kCurrWeightStr + "_var"),
shape, tensorflow::DataType::DT_FLOAT};
variable_shapes_[kCurrWeightStr] = shape;
variable_assign_nodes_[kCurrWeightStr] = tensorflow::ops::Assign{
scope.NewSubScope(kCurrWeightStr + "_assign"), variables_[kCurrWeightStr],
GlorotUniformInitializer(scope, input_channels, output_channels,
filter_side)};
// bias after conv2d
shape = {output_channels};
variables_[kCurrBiasStr] =
tensorflow::ops::Variable{scope.NewSubScope(kCurrBiasStr + "_var"), shape,
tensorflow::DataType::DT_FLOAT};
variable_shapes_[kCurrBiasStr] = shape;
variable_assign_nodes_[kCurrBiasStr] = tensorflow::ops::Assign{
scope.NewSubScope(kCurrBiasStr + "_assign"), variables_[kCurrBiasStr],
tensorflow::Input::Initializer{0.0f, shape}};
/**
* @todo tensorflow::StringPiece is using absl::string_view, which is being
* redirected to std::string_view and causes error.
*
*/
// here have to use assign node as input, otherwise the variables will be
// uninitialized
tensorflow::ops::Conv2D conv_2d_node{scope.WithOpName("Conv"),
input,
variables_[kCurrWeightStr],
{1, 1, 1, 1},
"SAME"};
tensorflow::ops::BiasAdd bias_add_node{
scope.WithOpName("bias_add"), conv_2d_node, variables_[kCurrBiasStr]};
tensorflow::ops::Relu relu_node{scope.WithOpName("relu"), bias_add_node};
return tensorflow::ops::MaxPoolV2{scope.WithOpName("max_pool"),
relu_node,
{1, 2, 2, 1},
{1, 2, 2, 1},
"SAME"};
}
tensorflow::Output CatDogCnn::AddDenseLayer(const std::string& layer_index,
const tensorflow::Scope& scope,
const int input_channels,
const int output_channels,
const bool need_activation,
const tensorflow::Input& input) {
const std::string kCurrWeightStr{"weight_" + layer_index};
const std::string kCurrBiasStr{"bias_" + layer_index};
tensorflow::TensorShape shape{input_channels, output_channels};
variables_[kCurrWeightStr] =
tensorflow::ops::Variable{scope.NewSubScope(kCurrWeightStr + "_var"),
shape, tensorflow::DataType::DT_FLOAT};
variable_shapes_[kCurrWeightStr] = shape;
variable_assign_nodes_[kCurrWeightStr] = tensorflow::ops::Assign{
scope.NewSubScope(kCurrWeightStr + "_assign"), variables_[kCurrWeightStr],
GlorotUniformInitializer(scope, input_channels, output_channels)};
shape = {output_channels};
variables_[kCurrBiasStr] =
tensorflow::ops::Variable{scope.NewSubScope(kCurrBiasStr + "_var"), shape,
tensorflow::DataType::DT_FLOAT};
variable_shapes_[kCurrBiasStr] = shape;
variable_assign_nodes_[kCurrBiasStr] = tensorflow::ops::Assign{
scope.NewSubScope(kCurrBiasStr + "_assign"), variables_[kCurrBiasStr],
tensorflow::Input::Initializer{0.0f, shape}};
// here have to use variable assign node as input, otherwise variables will be
// uninitialized
tensorflow::ops::MatMul multiply_weight_node{
scope.WithOpName("multiply_weight"), input, variables_[kCurrWeightStr]};
tensorflow::ops::Add add_bias_node{scope.WithOpName("add_bais"),
multiply_weight_node,
variables_[kCurrBiasStr]};
if (need_activation) {
return tensorflow::ops::Relu{scope.WithOpName("relu"), add_bias_node};
}
return add_bias_node;
}
tensorflow::Status CatDogCnn::CreateGraphForCnn(const int filter_side) {
input_batch_tensor_node_ = tensorflow::ops::Placeholder(
network_root_scope_.WithOpName(kInputNodeName),
tensorflow::DataType::DT_FLOAT);
drop_rate_node_ = tensorflow::ops::Placeholder{
network_root_scope_.WithOpName(kInputDropRateNodeName),
tensorflow::DataType::DT_FLOAT};
skip_drop_node_ = tensorflow::ops::Placeholder{
network_root_scope_.WithOpName(kInputSkipDropNodeName),
tensorflow::DataType::DT_FLOAT};
// conv 1
tensorflow::Scope scope_conv1 = network_root_scope_.NewSubScope("conv1");
int input_channels = kNumOfChannels;
int output_channels = 32;
tensorflow::Output pool1_node =
AddConvLayer("1", scope_conv1, input_channels, output_channels,
filter_side, input_batch_tensor_node_);
int new_side = std::ceil(kImageSize / 2.0f);
// conv 2
tensorflow::Scope scope_conv2 = network_root_scope_.NewSubScope("conv2");
input_channels = output_channels;
output_channels = 64;
tensorflow::Output pool2_node =
AddConvLayer("2", scope_conv2, input_channels, output_channels,
filter_side, pool1_node);
new_side = std::ceil(new_side / 2.0f);
// conv_3
tensorflow::Scope scope_conv3 = network_root_scope_.NewSubScope("conv3");
input_channels = output_channels;
output_channels = 128;
tensorflow::Output pool3_node =
AddConvLayer("3", scope_conv3, input_channels, output_channels,
filter_side, pool2_node);
new_side = std::ceil(new_side / 2.0f);
// conv_4
tensorflow::Scope scope_conv4 = network_root_scope_.NewSubScope("conv4");
input_channels = output_channels;
output_channels = 128;
tensorflow::Output pool4_node =
AddConvLayer("4", scope_conv4, input_channels, output_channels,
filter_side, pool3_node);
new_side = std::ceil(new_side / 2.0f);
// flatten
tensorflow::Scope scope_flatten = network_root_scope_.NewSubScope("flatten");
int flat_length = new_side * new_side * output_channels;
tensorflow::ops::Reshape flat_node{
scope_flatten, pool4_node, {-1, flat_length}};
// dropout
tensorflow::Scope scope_dropout = network_root_scope_.NewSubScope("dropout");
// [0,1)
tensorflow::ops::RandomUniform rand_node{
scope_dropout, tensorflow::ops::Shape{scope_dropout, flat_node},
tensorflow::DataType::DT_FLOAT};
// binary = floor(rand + (1 - drop_rate) + skip_drop)
tensorflow::ops::Floor drop_out_binary_node{
scope_dropout,
tensorflow::ops::Add{
scope_dropout, rand_node,
tensorflow::ops::Add{
scope_dropout,
tensorflow::ops::Sub{scope_dropout, 1.0f, drop_rate_node_},
skip_drop_node_}}};
// multiply elementwise
tensorflow::ops::Multiply apply_drop_out_node{
scope_dropout.WithOpName("apply_drop_out"),
tensorflow::ops::Div{scope_dropout, flat_node, drop_rate_node_},
drop_out_binary_node};
// dense1
input_channels = flat_length;
output_channels = 512;
tensorflow::Scope scope_dense1 = network_root_scope_.NewSubScope("dense1");
tensorflow::Output relu5 =
AddDenseLayer("dense1", scope_dense1, input_channels, output_channels,
false, apply_drop_out_node);
// dense2
input_channels = output_channels;
output_channels = 256;
tensorflow::Scope scope_dense2 = network_root_scope_.NewSubScope("dense2");
tensorflow::Output relu6 = AddDenseLayer(
"dense2", scope_dense2, input_channels, output_channels, false, relu5);
// desne3
input_channels = output_channels;
output_channels = 1;
tensorflow::Scope scope_dense3 = network_root_scope_.NewSubScope("dense3");
tensorflow::Output relu7 = AddDenseLayer(
"dense3", scope_dense3, input_channels, output_channels, false, relu6);
output_classification_ = tensorflow::ops::Sigmoid{
network_root_scope_.WithOpName(kOutputClassificationNodeName), relu7};
return network_root_scope_.status();
}
tensorflow::Status CatDogCnn::CreateGraphForOptimization(
const float learning_rate) {
input_labels_tensor_node_ = tensorflow::ops::Placeholder{
network_root_scope_.WithOpName("input_labels_tensor"),
tensorflow::DataType::DT_FLOAT};
tensorflow::Scope scope_loss = network_root_scope_.NewSubScope("loss");
TF_CHECK_OK(scope_loss.status());
// calculate loos
squared_difference_node_ = tensorflow::ops::SquaredDifference{
scope_loss, output_classification_, input_labels_tensor_node_};
output_loss_logits_node_ =
tensorflow::ops::Mean{scope_loss.WithOpName("output_loss_logits"),
squared_difference_node_,
{0}};
TF_CHECK_OK(scope_loss.status());
for (std::pair<std::string, tensorflow::Output> v : variables_) {
weights_biases_.push_back(v.second);
}
std::vector<tensorflow::Output> grad_outputs{};
TF_CHECK_OK(tensorflow::AddSymbolicGradients(network_root_scope_,
{output_loss_logits_node_},
weights_biases_, &grad_outputs));
int index = 0;
for (std::pair<std::string, tensorflow::Output> v : variables_) {
std::string index_str = std::to_string(index);
// m and v are described in API documentation
tensorflow::ops::Variable m_var{network_root_scope_,
variable_shapes_[v.first],
tensorflow::DataType::DT_FLOAT};
tensorflow::ops::Variable v_var{network_root_scope_,
variable_shapes_[v.first],
tensorflow::DataType::DT_FLOAT};
variable_assign_nodes_["m_" + index_str] = tensorflow::ops::Assign{
network_root_scope_, m_var,
tensorflow::Input::Initializer{0.f, variable_shapes_[v.first]}};
variable_assign_nodes_["v_" + index_str] = tensorflow::ops::Assign{
network_root_scope_, v_var,
tensorflow::Input::Initializer{0.0f, variable_shapes_[v.first]}};
tensorflow::ops::ApplyAdam apply_adam{network_root_scope_,
v.second,
m_var,
v_var,
0.0f,
0.0f,
learning_rate,
0.9f,
0.999f,
0.00000001f,
{grad_outputs[index]}};
adam_operations.emplace_back(std::move(apply_adam.operation));
++index;
}
return network_root_scope_.status();
}
tensorflow::Status CatDogCnn::Initialize() {
if (network_root_scope_.ok() == false) {
return network_root_scope_.status();
}
std::vector<tensorflow::Output> assign_operations{};
for (std::pair<std::string, tensorflow::Output> i : variable_assign_nodes_) {
assign_operations.emplace_back(i.second);
}
train_session_ptr_.reset(new tensorflow::ClientSession{network_root_scope_});
TF_CHECK_OK(train_session_ptr_->Run(assign_operations, nullptr));
return network_root_scope_.status();
}
tensorflow::Status CatDogCnn::Train(const tensorflow::Tensor& image_batch,
const tensorflow::Tensor& label_batch,
std::vector<float>* results, float* loss) {
if (network_root_scope_.ok() == false) {
return network_root_scope_.status();
}
std::vector<tensorflow::Tensor> output_tensors{};
TF_CHECK_OK(train_session_ptr_->Run(
{
{input_batch_tensor_node_, image_batch},
{input_labels_tensor_node_, label_batch},
{drop_rate_node_, 0.5f},
{skip_drop_node_, 0.0f},
},
{output_loss_logits_node_, output_classification_}, adam_operations,
&output_tensors));
*loss = output_tensors[0].scalar<float>()(0);
auto label_mat = label_batch.matrix<float>();
auto classification_mat = output_tensors[1].matrix<float>();
for (size_t i = 0; i < label_mat.dimension(0); ++i) {
results->push_back(
(std::fabs(classification_mat(i, 0) - label_mat(i, 0)) > 0.5f) ? 0 : 1);
}
return network_root_scope_.status();
}
tensorflow::Status CatDogCnn::Validate(const tensorflow::Tensor& image_batch,
const tensorflow::Tensor& label_batch,
std::vector<float>* results) {
if (network_root_scope_.ok() == false) {
return network_root_scope_.status();
}
std::vector<tensorflow::Tensor> output_tensors;
TF_CHECK_OK(train_session_ptr_->Run(
{
{input_batch_tensor_node_, image_batch},
{input_labels_tensor_node_, label_batch},
{drop_rate_node_, 1.0f},
{skip_drop_node_, 1.0f},
},
{output_classification_}, &output_tensors));
auto label_mat = label_batch.matrix<float>();
auto classification_mat = output_tensors[0].matrix<float>();
for (size_t i = 0; i < label_mat.dimension(0); ++i) {
results->push_back(
(std::fabs(classification_mat(i, 0) - label_mat(i, 0)) > 0.5f) ? 0 : 1);
}
return network_root_scope_.status();
}
tensorflow::Status CatDogCnn::FreezeModel(const std::filesystem::path& path) {
std::vector<tensorflow::Tensor> output_tensors{};
TF_CHECK_OK(train_session_ptr_->Run(weights_biases_, &output_tensors));
std::unordered_map<std::string, tensorflow::Tensor> name_variable_map;
for (size_t i = 0; i < weights_biases_.size(); ++i) {
name_variable_map[weights_biases_[i].node()->name()] = output_tensors[i];
}
tensorflow::GraphDef graph_def{};
TF_CHECK_OK(network_root_scope_.ToGraphDef(&graph_def));
tensorflow::SavedModelBundle saved_model_bundle{};
tensorflow::SignatureDef signature_def{};
(*(signature_def.mutable_inputs()))[input_batch_tensor_node_.name()].set_name(
input_batch_tensor_node_.name());
(*(signature_def.mutable_inputs()))[output_classification_.name()].set_name(
output_classification_.name());
tensorflow::MetaGraphDef* meta_graph_def =
&(saved_model_bundle.meta_graph_def);
(*(meta_graph_def->mutable_signature_def()))["signature_def"] = signature_def;
*(meta_graph_def->mutable_graph_def()) = graph_def;
tensorflow::SessionOptions options{};
saved_model_bundle.session.reset(tensorflow::NewSession(options));
tensorflow::GraphDef frozen_graph_def{};
std::unordered_set<std::string> inputs;
std::unordered_set<std::string> outputs;
// <---------------- uncomment following causes runtime exception start ---------------->
TF_CHECK_OK(tensorflow::FreezeSavedModel(saved_model_bundle, &frozen_graph_def, &inputs, &outputs));
// <--------------- uncomment following causes runtime exception end ---------------->
return tensorflow::WriteBinaryProto(tensorflow::Env::Default(), path.string(),
frozen_graph_def);
}
tensorflow::Status CatDogCnn::LoadFrozenModel(
const std::filesystem::path& path) {
// std::unique_ptr<tensorflow::GraphDef> graph_def_ptr;
// tensorflow::SessionOptions options{};
// frozen_session_ptr_.reset(tensorflow::NewSession(options));
// graph_def_ptr.reset(new tensorflow::GraphDef{});
// TF_CHECK_OK(tensorflow::ReadBinaryProto(tensorflow::Env::Default(),
// path.string(),
// graph_def_ptr.get()));
// return frozen_session_ptr_->Create(*graph_def_ptr.get());
}
tensorflow::Status CatDogCnn::PredictFromFrozen(const tensorflow::Tensor& image,
int* result) {
// std::vector<tensorflow::Tensor> output_tensors{};
// tensorflow::Tensor t{tensorflow::DataType::DT_FLOAT,
// tensorflow::TensorShape{{1}}};
// t.scalar<float>()(0) = 1.0f;
// // drop rate and skip drop rate both set to 1
// TF_RETURN_IF_ERROR(frozen_session_ptr_->Run(
// {
// {kInputNodeName, image},
// {kInputDropRateNodeName, t},
// {kInputSkipDropNodeName, t},
// },
// {kOutputClassificationNodeName}, {}, &output_tensors));
// auto mat = output_tensors[0].matrix<float>();
// *result = (mat(0, 0) > 0.5f) ? 1 : 0;
// return tensorflow::Status::OK();
}
} // namespace tensorflow_tutorial
entry point的头文件(main.hpp)
/**
* @file tutorial_02.hpp
* @author your name (you@domain.com)
* @brief
* @version 0.1
* @date 2020-12-16
*
* @copyright Copyright (c) 2020
*
*/
#ifndef INCLUDE_TENSORFLOW_TUTORIAL_TUTORIAL_02_HPP_
#define INCLUDE_TENSORFLOW_TUTORIAL_TUTORIAL_02_HPP_
#include <tensorflow/core/framework/tensor.h>
#include <tensorflow/core/summary/summary_file_writer.h>
#include <chrono>
#include <iostream>
#include <map>
#include <tensorflow_tutorial/tutorial_02/CatDogCnn/CatDogCnn.hpp>
#include <tuple>
#include <vector>
void TestCnn(const int argc, char** argv);
void TestConvertImageToTensor(const int argc, char** argv);
void TestLoadDataset(const int argc, char** argv);
void TestLoadBatch(const int argc, char** argv);
void TestAddConvLayer(const int argc, char** argv);
void TestAddDenseLayer(const int argc, char** argv);
void AssignVariable(const tensorflow::ClientSession& session,
const tensorflow::ops::Assign& variable_assign_node);
void ProperAssignVariableExample(const int argc, char** argv);
#endif // INCLUDE_TENSORFLOW_TUTORIAL_TUTORIAL_02_HPP_
导致异常的入口点(main.cpp)
/**
* @file tutorial_02.cpp
* @author Hung-Tien Huang (paperbus72@gmail.com)
* @brief
* @version 0.1
* @date 2020-12-16
*
* @copyright Copyright (c) 2020
*
*/
#include <tensorflow_tutorial/tutorial_02/tutorial_02.hpp>
int main(int argc, char** argv) {
// TestCnn(argc, argv);
TestConvertImageToTensor(argc, argv);
// TestLoadDataset(argc, argv);
// TestLoadBatch(argc, argv);
// TestAddConvLayer(argc, argv);
// TestAddDenseLayer(argc, argv);
// ProperAssignVariableExample(argc, argv);
return 0;
}
void TestCnn(const int argc, char** argv) {
if (argc != 3) {
std::cerr << "./tutorial_2 /path/to/base/folder "
"/path/to/export/filename/without/extension"
<< std::endl;
exit(EXIT_FAILURE);
}
const std::filesystem::path kBaseFolder{argv[1]};
const std::filesystem::path kTrainFolder = kBaseFolder / "train";
const std::filesystem::path kValidateFolder = kBaseFolder / "validation";
const std::filesystem::path kExportPath =
std::filesystem::path{argv[2]}.replace_extension(".pb");
std::filesystem::create_directories(kExportPath.parent_path());
const size_t kImageSide = 150;
const size_t kImageChannels = 3;
const size_t kBatchSize = 20;
const size_t kFilterSide = 3;
tensorflow_tutorial::CatDogCnn model{150, 3};
// load dataset
TF_CHECK_OK(model.CreateGraphForInputImage(true));
std::vector<tensorflow::Tensor> train_image_batch;
std::vector<tensorflow::Tensor> train_label_batch;
std::vector<tensorflow::Tensor> validate_image_batch;
std::vector<tensorflow::Tensor> validate_label_batch;
TF_CHECK_OK(model.LoadBatach(
kTrainFolder, {std::make_pair("cats", 0), std::make_pair("dogs", 1)},
kBatchSize, &train_image_batch, &train_label_batch));
TF_CHECK_OK(model.LoadBatach(
kValidateFolder, {std::make_pair("cats", 0), std::make_pair("dogs", 1)},
kBatchSize, &validate_image_batch, &validate_label_batch));
// check data length
assert(train_image_batch.size() == train_label_batch.size());
assert(validate_image_batch.size() == validate_label_batch.size());
const size_t num_epochs = 30;
const size_t kNumTrainBatches = train_label_batch.size();
const size_t kNumValidateBatches = validate_label_batch.size();
// std::cout << kNumTrainBatches << " " << kNumValidateBatches << std::endl;
// std::cout << train_image_batch[0].DebugString() << std::endl;
// std::cout << train_image_batch[20].DebugString() << std::endl;
// std::cout << train_label_batch[0].DebugString() << std::endl;
// std::cout << train_label_batch[20].DebugString() << std::endl;
// build cnn graph
TF_CHECK_OK(model.CreateGraphForCnn(kFilterSide));
TF_CHECK_OK(model.CreateGraphForOptimization(0.0002f));
TF_CHECK_OK(model.Initialize());
// Epoch / Step loops
for (int epoch = 0; epoch < num_epochs; epoch++) {
std::cout << "Epoch " << epoch + 1 << "/" << num_epochs << ":";
auto t1 = std::chrono::high_resolution_clock::now();
float loss_sum = 0;
float accuracy_sum = 0;
for (int b = 0; b < kNumTrainBatches; b++) {
std::vector<float> results;
float loss;
TF_CHECK_OK(model.Train(train_image_batch[b], train_label_batch[b],
&results, &loss));
loss_sum += loss;
accuracy_sum +=
accumulate(results.begin(), results.end(), 0.f) / results.size();
std::cout << ".";
}
std::cout << std::endl << "Validation:";
float validation_sum = 0;
for (int c = 0; c < kNumValidateBatches; c++) {
std::vector<float> results;
TF_CHECK_OK(model.Validate(validate_image_batch[c],
validate_label_batch[c], &results));
validation_sum +=
accumulate(results.begin(), results.end(), 0.f) / results.size();
std::cout << ".";
}
std::cout << std::endl;
auto t2 = std::chrono::high_resolution_clock::now();
std::cout
<< "Time: "
<< std::chrono::duration_cast<std::chrono::seconds>(t2 - t1).count()
<< " seconds ";
std::cout << "Loss: " << loss_sum / kNumTrainBatches
<< " Results accuracy: " << accuracy_sum / kNumTrainBatches
<< " Validation accuracy: "
<< validation_sum / kNumValidateBatches << std::endl;
// TF_CHECK_OK(model.FreezeModel(kExportPath.string()));
}
}
void TestConvertImageToTensor(const int argc, char** argv) {
if (argc != 2) {
std::cerr << "./tutorial_02 /path/to/image" << std::endl;
exit(EXIT_FAILURE);
}
const std::filesystem::path path_to_image{argv[1]};
tensorflow_tutorial::CatDogCnn cat_dog_model{150, 3};
tensorflow::Tensor image_tensor;
cat_dog_model.CreateGraphForInputImage(false);
cat_dog_model.ConvertImageToTensor(path_to_image, &image_tensor);
std::cout << image_tensor.DebugString() << std::endl;
cat_dog_model.CreateGraphForInputImage(true);
cat_dog_model.ConvertImageToTensor(path_to_image, &image_tensor);
std::cout << image_tensor.DebugString() << std::endl;
}
void TestLoadDataset(const int argc, char** argv) {
if (argc != 2) {
std::cerr << "./tutorial_02 /path/to/root/folder" << std::endl;
exit(EXIT_FAILURE);
}
const std::filesystem::path path_to_root_dir{argv[1]};
tensorflow_tutorial::CatDogCnn cat_dog_model{150, 3};
std::vector<std::pair<std::string, float>> label = {std::pair{"cats", 0},
std::pair{"dogs", 1}};
std::cout << "Remain Stacked:" << std::endl;
cat_dog_model.CreateGraphForInputImage(false);
std::vector<std::tuple<tensorflow::Tensor, float, std::filesystem::path>>
image_tensors;
cat_dog_model.LoadDataset(path_to_root_dir, label, &image_tensors);
std::cout << "Number of Samples: " << image_tensors.size() << std::endl;
std::cout << "Shape of 0th element: "
<< std::get<0>(image_tensors[0]).DebugString()
<< "\nclass of 0th element (float): "
<< std::get<1>(image_tensors[0])
<< "\npath to 0th element: " << std::get<2>(image_tensors[0])
<< std::endl;
std::cout << "dataset size: " << image_tensors.size() << std::endl;
std::cout << "Unstacked:" << std::endl;
image_tensors.clear();
cat_dog_model.CreateGraphForInputImage(true);
cat_dog_model.LoadDataset(path_to_root_dir, label, &image_tensors);
std::cout << "Number of Samples: " << image_tensors.size() << std::endl;
std::cout << "Shape of 0th element: "
<< std::get<0>(image_tensors[0]).DebugString()
<< "\nclass of 0th element (float): "
<< std::get<1>(image_tensors[0])
<< "\npath to 0th element: " << std::get<2>(image_tensors[0])
<< std::endl;
std::cout << "dataset size: " << image_tensors.size() << std::endl;
}
void TestLoadBatch(const int argc, char** argv) {
if (argc != 3) {
std::cerr << "./tutorial_02 /path/to/root/folder number_of_batches"
<< std::endl;
exit(EXIT_FAILURE);
}
const std::filesystem::path path_to_root_dir{argv[1]};
const size_t kNumOfBatches = std::stoul(argv[2]);
std::vector<std::pair<std::string, float>> label = {std::pair{"cats", 0},
std::pair{"dogs", 1}};
tensorflow_tutorial::CatDogCnn cat_dog_model{150, 3};
cat_dog_model.CreateGraphForInputImage(true);
std::vector<tensorflow::Tensor> image_batches;
std::vector<tensorflow::Tensor> label_batches;
cat_dog_model.LoadBatach(path_to_root_dir, label, kNumOfBatches,
&image_batches, &label_batches);
std::cout << "image_batches: \nLength : " << image_batches.size()
<< std::endl;
std::cout << image_batches[0].DebugString() << std::endl;
std::cout << "label_batches: \nLength : " << label_batches.size()
<< std::endl;
std::cout << label_batches[0].DebugString() << std::endl;
}
void TestAddConvLayer(const int argc, char** argv) {
std::cout << "test add conv layer" << std::endl;
std::vector<tensorflow::Tensor> output_tensors{};
tensorflow_tutorial::CatDogCnn cat_dog_model{150, 3};
tensorflow::Scope scope = tensorflow::Scope::NewRootScope();
tensorflow::TensorShape shape{1, 150, 150, 3};
tensorflow::Output input_variable = tensorflow::ops::Variable{
scope.NewSubScope("input"), shape, tensorflow::DataType::DT_FLOAT};
tensorflow::ops::Assign assign_input_var{
scope.NewSubScope("assign_input"), input_variable,
tensorflow::Input::Initializer{0.3f, shape}};
tensorflow::Output conv = cat_dog_model.AddConvLayer(
"conv_test", scope.NewSubScope("conv_test"), 3, 32, 3, assign_input_var);
tensorflow::ClientSession client{scope};
TF_CHECK_OK(client.Run({conv}, &output_tensors));
std::cout << output_tensors.size() << std::endl;
tensorflow::GraphDef graph;
TF_CHECK_OK(scope.ToGraphDef(&graph));
tensorflow::SummaryWriterInterface* w;
TF_CHECK_OK(tensorflow::CreateSummaryFileWriter(
1, 0, ".", ".img-graph", tensorflow::Env::Default(), &w));
TF_CHECK_OK(w->WriteGraph(0, std::make_unique<tensorflow::GraphDef>(graph)));
if (!scope.ok()) {
std::cerr << "ERROR" << scope.status().error_message() << std::endl;
exit(EXIT_FAILURE);
}
std::cout << output_tensors[0].DebugString() << std::endl;
}
void TestAddDenseLayer(const int argc, char** argv) {
std::cout << "test add dense layer" << std::endl;
// initialize input variables
tensorflow::Scope scope = tensorflow::Scope::NewRootScope();
tensorflow::TensorShape shape{1, 30};
tensorflow::ops::Variable input_variable{scope.NewSubScope("input"), shape,
tensorflow::DataType::DT_FLOAT};
tensorflow::ops::Assign assign_input_var{
scope.NewSubScope("assign_input"), input_variable,
tensorflow::Input::Initializer{0.3f, shape}};
tensorflow::ClientSession client{scope};
AssignVariable(client, assign_input_var);
// add dense layer
tensorflow_tutorial::CatDogCnn cat_dog_model{10, 3};
tensorflow::Output dense = cat_dog_model.AddDenseLayer(
"dense_layer", scope.NewSubScope("dense_layer"), 30, 5, false,
input_variable);
// execute the graph
std::vector<tensorflow::Tensor> output_tensors{};
TF_CHECK_OK(client.Run({dense}, &output_tensors));
std::cout << output_tensors.size() << std::endl;
// output graph
tensorflow::GraphDef graph;
TF_CHECK_OK(scope.ToGraphDef(&graph));
tensorflow::SummaryWriterInterface* w;
TF_CHECK_OK(tensorflow::CreateSummaryFileWriter(
1, 0, ".", ".img-graph", tensorflow::Env::Default(), &w));
TF_CHECK_OK(w->WriteGraph(0, std::make_unique<tensorflow::GraphDef>(graph)));
// print string
std::cout << output_tensors[0].DebugString() << std::endl;
}
void AssignVariable(const tensorflow::ClientSession& session,
const tensorflow::ops::Assign& variable_assign_node) {
std::vector<tensorflow::Tensor> output{};
TF_CHECK_OK(session.Run(
{}, {}, {tensorflow::Operation{variable_assign_node.node()}}, &output));
}
void ProperAssignVariableExample(const int argc, char** argv) {
std::vector<tensorflow::Tensor> output_tensors{};
tensorflow::Scope scope = tensorflow::Scope::NewRootScope();
tensorflow::TensorShape shape{1, 150, 150, 3};
tensorflow::ops::Variable input_variable{scope.NewSubScope("input"), shape,
tensorflow::DataType::DT_FLOAT};
tensorflow::ops::Assign assign_input_var{
scope.NewSubScope("assign_input"), input_variable,
tensorflow::Input::Initializer{0.3f, shape}};
// have to execute the assign input node first prior to using the variable
tensorflow::ClientSession client{scope};
TF_CHECK_OK(client.Run({}, {},
{tensorflow::Operation{assign_input_var.node()}},
&output_tensors));
std::cout << output_tensors.size() << std::endl;
TF_CHECK_OK(client.Run({input_variable}, &output_tensors));
std::cout << output_tensors.size() << std::endl;
std::cout << output_tensors[0].DebugString() << std::endl;
}
其他信息/日志 包括任何有助于诊断问题的日志或源代码。如果包括回溯,请包括完整的回溯。大型日志和文件应附加。
2020-12-23 09:32:00.026726: I tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2099940000 Hz
[libprotobuf FATAL external/com_google_protobuf/src/google/protobuf/map.h:1060] CHECK failed: it != end(): key not found: Reciprocal
terminate called after throwing an instance of 'google::protobuf::FatalException'
what(): CHECK failed: it != end(): key not found: Reciprocal
--Type <RET> for more, q to quit, c to continue without paging--
Thread 1 "main" received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) backtrace
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007fffe8603859 in __GI_abort () at abort.c:79
#2 0x00007fffe89d8951 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007fffe89e447c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007fffe89e44e7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007fffe89e4799 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00007fffeb44aed5 in google::protobuf::internal::LogMessage::Finish() [clone .cold] () from /usr/local/lib/libtensorflow_cc.so.2
#7 0x00007ffff50353cd in tensorflow::grappler::NumOutputs(tensorflow::NodeDef const&, tensorflow::GraphDef*) () from /usr/local/lib/libtensorflow_cc.so.2
#8 0x00007ffff4bd25f6 in tensorflow::grappler::ConstantFolding::RunOptimizationPass(tensorflow::grappler::Cluster*, tensorflow::grappler::GrapplerItem*, tensorflow::GraphDef*) ()
from /usr/local/lib/libtensorflow_cc.so.2
#9 0x00007ffff4bd3175 in tensorflow::grappler::ConstantFolding::Optimize(tensorflow::grappler::Cluster*, tensorflow::grappler::GrapplerItem const&, tensorflow::GraphDef*) ()
from /usr/local/lib/libtensorflow_cc.so.2
#10 0x00007ffff4aa40fa in tensorflow::grappler::MetaOptimizer::RunOptimizer(tensorflow::grappler::GraphOptimizer*, tensorflow::grappler::Cluster*, tensorflow::grappler::GrapplerItem*, tensorflow::GraphDef*, tensorflow::grappler::MetaOptimizer::GraphOptimizationResult*) () from /usr/local/lib/libtensorflow_cc.so.2
--Type <RET> for more, q to quit, c to continue without paging--
#11 0x00007ffff4aa556d in tensorflow::grappler::MetaOptimizer::OptimizeGraph(tensorflow::grappler::Cluster*, tensorflow::grappler::GrapplerItem&&, tensorflow::GraphDef*) ()
from /usr/local/lib/libtensorflow_cc.so.2
#12 0x00007ffff4aa69a9 in tensorflow::grappler::MetaOptimizer::OptimizeConsumeItem(tensorflow::grappler::Cluster*, tensorflow::grappler::GrapplerItem&&, tensorflow::GraphDef*) ()
from /usr/local/lib/libtensorflow_cc.so.2
#13 0x00007ffff4aa88eb in tensorflow::grappler::RunMetaOptimizer(tensorflow::grappler::GrapplerItem&&, tensorflow::ConfigProto const&, tensorflow::DeviceBase*, tensorflow::grappler::Cluster*, tensorflow::GraphDef*) () from /usr/local/lib/libtensorflow_cc.so.2
#14 0x00007ffff4a95b11 in tensorflow::GraphExecutionState::OptimizeGraph(tensorflow::BuildGraphOptions const&, std::unique_ptr<tensorflow::Graph, std::default_delete<tensorflow::Graph> >*, std::unique_ptr<tensorflow::FunctionLibraryDefinition, std::default_delete<tensorflow::FunctionLibraryDefinition> >*) () from /usr/local/lib/libtensorflow_cc.so.2
#15 0x00007ffff4a9675b in tensorflow::GraphExecutionState::BuildGraph(tensorflow::BuildGraphOptions const&, std::unique_ptr<tensorflow::ClientGraph, std::default_delete<tensorflow::ClientGraph> >*) () from /usr/local/lib/libtensorflow_cc.so.2
#16 0x00007ffff4a41250 in tensorflow::DirectSession::CreateGraphs(tensorflow::BuildGraphOptions const&, std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::unique_ptr<tensorflow::Graph, std::default_delete<tensorflow::Graph> >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::unique_ptr<tensorflow::Graph, std::default_delete<tensorflow::Graph> > > > >*, std::unique_ptr<tensorflow::FunctionLibraryDefinition, std::default_delete<tensorflow::FunctionLibraryDefinition> >*,--Type <RET> for more, q to quit, c to continue without paging--
tensorflow::DirectSession::RunStateArgs*, absl::lts_2020_02_25::InlinedVector<tensorflow::DataType, 4ul, std::allocator<tensorflow::DataType> >*, absl::lts_2020_02_25::InlinedVector<tensorflow::DataType, 4ul, std::allocator<tensorflow::DataType> >*, long long*) () from /usr/local/lib/libtensorflow_cc.so.2
#17 0x00007ffff4a42b53 in tensorflow::DirectSession::CreateExecutors(tensorflow::CallableOptions const&, std::unique_ptr<tensorflow::DirectSession::ExecutorsAndKeys, std::default_delete<tensorflow::DirectSession::ExecutorsAndKeys> >*, std::unique_ptr<tensorflow::DirectSession::FunctionInfo, std::default_delete<tensorflow::DirectSession::FunctionInfo> >*, tensorflow::DirectSession::RunStateArgs*) () from /usr/local/lib/libtensorflow_cc.so.2
#18 0x00007ffff4a45418 in tensorflow::DirectSession::GetOrCreateExecutors(absl::lts_2020_02_25::Span<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, absl::lts_2020_02_25::Span<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, absl::lts_2020_02_25::Span<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, tensorflow::DirectSession::ExecutorsAndKeys**, tensorflow::DirectSession::RunStateArgs*) () from /usr/local/lib/libtensorflow_cc.so.2
#19 0x00007ffff4a496d6 in tensorflow::DirectSession::Run(tensorflow::RunOptions const&, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, tensorflow::Tensor>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, tensorflow::Tensor> > > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<tensorflow::Tensor, std::allocator<tensorflow::Tensor> >*, tensorflow::RunMetadata*, tensorflow::thread::ThreadPoolOptions const&) () from /usr/local/lib/libtensorflow_cc.so.2
#20 0x00007ffff4a33d08 in tensorflow::DirectSession::Run(tensorflow::RunOptions const&, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, tenso--Type <RET> for more, q to quit, c to continue without paging--
rflow::Tensor>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, tensorflow::Tensor> > > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<tensorflow::Tensor, std::allocator<tensorflow::Tensor> >*, tensorflow::RunMetadata*) () from /usr/local/lib/libtensorflow_cc.so.2
#21 0x00007fffeb81640a in tensorflow::ClientSession::Run(tensorflow::RunOptions const&, std::unordered_map<tensorflow::Output, tensorflow::Input::Initializer, tensorflow::OutputHash, std::equal_to<tensorflow::Output>, std::allocator<std::pair<tensorflow::Output const, tensorflow::Input::Initializer> > > const&, std::vector<tensorflow::Output, std::allocator<tensorflow::Output> > const&, std::vector<tensorflow::Operation, std::allocator<tensorflow::Operation> > const&, std::vector<tensorflow::Tensor, std::allocator<tensorflow::Tensor> >*, tensorflow::RunMetadata*) const ()
from /usr/local/lib/libtensorflow_cc.so.2
#22 0x00007fffeb816547 in tensorflow::ClientSession::Run(std::unordered_map<tensorflow::Output, tensorflow::Input::Initializer, tensorflow::OutputHash, std::equal_to<tensorflow::Output>, std::allocator<std::pair<tensorflow::Output const, tensorflow::Input::Initializer> > > const&, std::vector<tensorflow::Output, std::allocator<tensorflow::Output> > const&, std::vector<tensorflow::Operation, std::allocator<tensorflow::Operation> > const&, std::vector<tensorflow::Tensor, std::allocator<tensorflow::Tensor> >*) const () from /usr/local/lib/libtensorflow_cc.so.2
#23 0x00007fffeb816788 in tensorflow::ClientSession::Run(std::unordered_map<tensorflow::Output, tensorflow::Input::Initializer, tensorflow::OutputHash, std::equal_to<tensorflow::Output>, std::allocator<std::pair<tensorflow::Output const, tensorflow::Input::Initializer> > > const&, std::vector<tensorflow::Output, std::allocator<tensorflow::Output> > const&, std::vector<tensorflow::Tensor, std::allocator<tensorflow::Tensor> >*) const () from /usr/local/lib/libtensorflow_cc.so.2
--Type <RET> for more, q to quit, c to continue without paging--
#24 0x000055555556df6a in CatDogCNN::ReadTensorFromImageFile (this=0x7fffffffd390, file_name="../../data/cats_and_dogs_small/train/cats/cat.470.jpg", outTensor=...) at ../CatDogCNN.cpp:38
#25 0x000055555556e565 in CatDogCNN::ReadFileTensors (this=0x7fffffffd390, base_folder_name="../../data/cats_and_dogs_small/train", v_folder_label=std::vector of length 2, capacity 2 = {...},
file_tensors=std::vector of length 0, capacity 0) at ../CatDogCNN.cpp:60
#26 0x000055555556e972 in CatDogCNN::ReadBatches (this=0x7fffffffd390, base_folder_name="../../data/cats_and_dogs_small/train", v_folder_label=std::vector of length 2, capacity 2 = {...},
batch_size=20, image_batches=std::vector of length 0, capacity 0, label_batches=std::vector of length 0, capacity 0) at ../CatDogCNN.cpp:79
#27 0x000055555556307a in main (argc=1, argv=0x7fffffffd7e8) at ../main.cpp:39
absl/base/option.h必须这样配置,否则将出现链接错误。绝对不是一个好主意在类名前附加版本号。轻微的版本更改将导致无尽的错误。
#ifndef ABSL_BASE_OPTIONS_H_
#define ABSL_BASE_OPTIONS_H_
// Copyright 2019 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: options.h
// -----------------------------------------------------------------------------
//
// This file contains Abseil configuration options for setting specific
// implementations instead of letting Abseil determine which implementation to
// use at compile-time. Setting these options may be useful for package or build
// managers who wish to guarantee ABI stability within binary builds (which are
// otherwise difficult to enforce).
//
// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS: It is important that
// maintainers of package managers who wish to package Abseil read and
// understand this file! ***
//
// Abseil contains a number of possible configuration endpoints, based on
// parameters such as the detected platform, language version, or command-line
// flags used to invoke the underlying binary. As is the case with all
// libraries, binaries which contain Abseil code must ensure that separate
// packages use the same compiled copy of Abseil to avoid a diamond dependency
// problem, which can occur if two packages built with different Abseil
// configuration settings are linked together. Diamond dependency problems in
// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in
// linker errors), or undefined behavior (resulting in crashes).
//
// Diamond dependency problems can be avoided if all packages utilize the same
// exact version of Abseil. Building from source code with the same compilation
// parameters is the easiest way to avoid such dependency problems. However, for
// package managers who cannot control such compilation parameters, we are
// providing the file to allow you to inject ABI (Application Binary Interface)
// stability across builds. Settings options in this file will neither change
// API nor ABI, providing a stable copy of Abseil between packages.
//
// Care must be taken to keep options within these configurations isolated
// from any other dynamic settings, such as command-line flags which could alter
// these options. This file is provided specifically to help build and package
// managers provide a stable copy of Abseil within their libraries and binaries;
// other developers should not have need to alter the contents of this file.
//
// -----------------------------------------------------------------------------
// Usage
// -----------------------------------------------------------------------------
//
// For any particular package release, set the appropriate definitions within
// this file to whatever value makes the most sense for your package(s). Note
// that, by default, most of these options, at the moment, affect the
// implementation of types; future options may affect other implementation
// details.
//
// NOTE: the defaults within this file all assume that Abseil can select the
// proper Abseil implementation at compile-time, which will not be sufficient
// to guarantee ABI stability to package managers.
// Include a standard library header to allow configuration based on the
// standard library in use.
#ifdef __cplusplus
#include <ciso646>
#endif
// -----------------------------------------------------------------------------
// Type Compatibility Options
// -----------------------------------------------------------------------------
//
// ABSL_OPTION_USE_STD_ANY
//
// This option controls whether absl::any is implemented as an alias to
// std::any, or as an independent implementation.
//
// A value of 0 means to use Abseil's implementation. This requires only C++11
// support, and is expected to work on every toolchain we support.
//
// A value of 1 means to use an alias to std::any. This requires that all code
// using Abseil is built in C++17 mode or later.
//
// A value of 2 means to detect the C++ version being used to compile Abseil,
// and use an alias only if a working std::any is available. This option is
// useful when you are building your entire program, including all of its
// dependencies, from source. It should not be used otherwise -- for example,
// if you are distributing Abseil in a binary package manager -- since in
// mode 2, absl::any will name a different type, with a different mangled name
// and binary layout, depending on the compiler flags passed by the end user.
// For more info, see https://abseil.io/about/design/dropin-types.
//
// User code should not inspect this macro. To check in the preprocessor if
// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY.
#define ABSL_OPTION_USE_STD_ANY 0
// ABSL_OPTION_USE_STD_OPTIONAL
//
// This option controls whether absl::optional is implemented as an alias to
// std::optional, or as an independent implementation.
//
// A value of 0 means to use Abseil's implementation. This requires only C++11
// support, and is expected to work on every toolchain we support.
//
// A value of 1 means to use an alias to std::optional. This requires that all
// code using Abseil is built in C++17 mode or later.
//
// A value of 2 means to detect the C++ version being used to compile Abseil,
// and use an alias only if a working std::optional is available. This option
// is useful when you are building your program from source. It should not be
// used otherwise -- for example, if you are distributing Abseil in a binary
// package manager -- since in mode 2, absl::optional will name a different
// type, with a different mangled name and binary layout, depending on the
// compiler flags passed by the end user. For more info, see
// https://abseil.io/about/design/dropin-types.
// User code should not inspect this macro. To check in the preprocessor if
// absl::optional is a typedef of std::optional, use the feature macro
// ABSL_USES_STD_OPTIONAL.
#define ABSL_OPTION_USE_STD_OPTIONAL 0
// ABSL_OPTION_USE_STD_STRING_VIEW
//
// This option controls whether absl::string_view is implemented as an alias to
// std::string_view, or as an independent implementation.
//
// A value of 0 means to use Abseil's implementation. This requires only C++11
// support, and is expected to work on every toolchain we support.
//
// A value of 1 means to use an alias to std::string_view. This requires that
// all code using Abseil is built in C++17 mode or later.
//
// A value of 2 means to detect the C++ version being used to compile Abseil,
// and use an alias only if a working std::string_view is available. This
// option is useful when you are building your program from source. It should
// not be used otherwise -- for example, if you are distributing Abseil in a
// binary package manager -- since in mode 2, absl::string_view will name a
// different type, with a different mangled name and binary layout, depending on
// the compiler flags passed by the end user. For more info, see
// https://abseil.io/about/design/dropin-types.
//
// User code should not inspect this macro. To check in the preprocessor if
// absl::string_view is a typedef of std::string_view, use the feature macro
// ABSL_USES_STD_STRING_VIEW.
#define ABSL_OPTION_USE_STD_STRING_VIEW 0
// ABSL_OPTION_USE_STD_VARIANT
//
// This option controls whether absl::variant is implemented as an alias to
// std::variant, or as an independent implementation.
//
// A value of 0 means to use Abseil's implementation. This requires only C++11
// support, and is expected to work on every toolchain we support.
//
// A value of 1 means to use an alias to std::variant. This requires that all
// code using Abseil is built in C++17 mode or later.
//
// A value of 2 means to detect the C++ version being used to compile Abseil,
// and use an alias only if a working std::variant is available. This option
// is useful when you are building your program from source. It should not be
// used otherwise -- for example, if you are distributing Abseil in a binary
// package manager -- since in mode 2, absl::variant will name a different
// type, with a different mangled name and binary layout, depending on the
// compiler flags passed by the end user. For more info, see
// https://abseil.io/about/design/dropin-types.
//
// User code should not inspect this macro. To check in the preprocessor if
// absl::variant is a typedef of std::variant, use the feature macro
// ABSL_USES_STD_VARIANT.
#define ABSL_OPTION_USE_STD_VARIANT 0
// ABSL_OPTION_USE_INLINE_NAMESPACE
// ABSL_OPTION_INLINE_NAMESPACE_NAME
//
// These options controls whether all entities in the absl namespace are
// contained within an inner inline namespace. This does not affect the
// user-visible API of Abseil, but it changes the mangled names of all symbols.
//
// This can be useful as a version tag if you are distributing Abseil in
// precompiled form. This will prevent a binary library build of Abseil with
// one inline namespace being used with headers configured with a different
// inline namespace name. Binary packagers are reminded that Abseil does not
// guarantee any ABI stability in Abseil, so any update of Abseil or
// configuration change in such a binary package should be combined with a
// new, unique value for the inline namespace name.
//
// A value of 0 means not to use inline namespaces.
//
// A value of 1 means to use an inline namespace with the given name inside
// namespace absl. If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also
// be changed to a new, unique identifier name. In particular "head" is not
// allowed.
#define ABSL_OPTION_USE_INLINE_NAMESPACE 1
#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_2020_02_25
#endif // ABSL_BASE_OPTIONS_H_
3条答案
按热度按时间xj3cbfub1#
你好,我想问一下你是否解决了这个问题,以及你是如何构建TF 2.4以查看该功能的。我仍然在收到链接器错误符号未找到"class tensorflow::Status __cdecl tensorflow::FreezeSavedModel",即使修改了BUILD文件以包含它。谢谢。
smdnsysy2#
问题从未得到解决。我已经有一段时间没有接触这个项目了,我忘记了如何获取它。但我认为是通过大量的gdb回溯跟踪来找到文件的。
fumotvh33#
你好,
感谢你打开这个问题。由于这个问题已经开放了很长时间,这个问题的代码/调试信息可能与当前代码库的状态不相关。
Tensorflow团队正在不断通过修复错误和添加新功能来改进框架。我们建议你尝试使用最新的TensorFlow version 和最新的兼容硬件配置,这可能会解决该问题。如果你仍然遇到问题,请创建一个新的GitHub问题,附上你的最新发现以及所有有助于我们调查的调试信息。
请按照 release notes 了解Tensorflow空间中最新发展的动态。