tensorflow [C++][致命错误]在cc/tools/saved_model.h中调用FreezeSavedModel会导致protobuf::FatalException,

gpfsuwkq  于 4个月前  发布在  其他
关注(0)|答案(3)|浏览(113)

请尽快修复这个问题。一个无法正确导出训练好的模型的机器学习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, &current_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},
        &current_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_
xj3cbfub

xj3cbfub1#

你好,我想问一下你是否解决了这个问题,以及你是如何构建TF 2.4以查看该功能的。我仍然在收到链接器错误符号未找到"class tensorflow::Status __cdecl tensorflow::FreezeSavedModel",即使修改了BUILD文件以包含它。谢谢。

smdnsysy

smdnsysy2#

问题从未得到解决。我已经有一段时间没有接触这个项目了,我忘记了如何获取它。但我认为是通过大量的gdb回溯跟踪来找到文件的。

fumotvh3

fumotvh33#

你好,
感谢你打开这个问题。由于这个问题已经开放了很长时间,这个问题的代码/调试信息可能与当前代码库的状态不相关。
Tensorflow团队正在不断通过修复错误和添加新功能来改进框架。我们建议你尝试使用最新的TensorFlow version 和最新的兼容硬件配置,这可能会解决该问题。如果你仍然遇到问题,请创建一个新的GitHub问题,附上你的最新发现以及所有有助于我们调查的调试信息。
请按照 release notes 了解Tensorflow空间中最新发展的动态。

相关问题