/home/docs/checkouts/readthedocs.org/user_builds/ratpac/checkouts/latest/src/external/cppflow/include/cppflow/model.h Source File

Ratpac-two: /home/docs/checkouts/readthedocs.org/user_builds/ratpac/checkouts/latest/src/external/cppflow/include/cppflow/model.h Source File
Ratpac-two
model.h
Go to the documentation of this file.
1 // MIT License
2 //
3 // Copyright (c) 2020 Sergio Izquierdo
4 // Copyright (c) 2020 liufeng27
5 // Copyright (c) 2020 Jiannan Liu
6 // Copyright (c) 2021 Paolo Galeone
7 // Copyright (c) 2021 Paul
8 // Copyright (c) 2022 Tim Upthegrove
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 // SOFTWARE.
27 
39 #ifndef INCLUDE_CPPFLOW_MODEL_H_
40 #define INCLUDE_CPPFLOW_MODEL_H_
41 
42 // C headers
43 #include <tensorflow/c/c_api.h>
44 
45 // C++ headers
46 #include <fstream>
47 #include <iostream>
48 #include <memory>
49 #include <string>
50 #include <tuple>
51 #include <vector>
52 
53 // CppFlow headers
54 #include "cppflow/context.h"
55 #include "cppflow/defer.h"
56 #include "cppflow/tensor.h"
57 
58 namespace cppflow {
59 
60 class model {
61  public:
62  enum TYPE {
63  SAVED_MODEL,
64  FROZEN_GRAPH,
65  }; // enum TYPE
66 
67  explicit model(const std::string& filename, const TYPE type = TYPE::SAVED_MODEL);
68  model(const model& model) = default;
69  model(model&& model) = default;
70 
71  ~model() = default;
72 
73  model& operator=(const model& other) = default;
74  model& operator=(model&& other) = default;
75  std::vector<tensor> operator()(std::vector<std::tuple<std::string, tensor>> inputs, std::vector<std::string> outputs);
76  tensor operator()(const tensor& input);
77 
78  std::vector<std::string> get_operations() const;
79  std::vector<int64_t> get_operation_shape(const std::string& operation) const;
80 
81  private:
82  TF_Buffer* readGraph(const std::string& filename);
83 
84  std::shared_ptr<TF_Status> status;
85  std::shared_ptr<TF_Graph> graph;
86  std::shared_ptr<TF_Session> session;
87 }; // Class model
88 
89 } // namespace cppflow
90 
91 namespace cppflow {
92 
93 inline model::model(const std::string& filename, const TYPE type) {
94  this->status = {TF_NewStatus(), &TF_DeleteStatus};
95  this->graph = {TF_NewGraph(), TF_DeleteGraph};
96 
97  // Create the session.
98  std::unique_ptr<TF_SessionOptions, decltype(&TF_DeleteSessionOptions)> session_options = {TF_NewSessionOptions(),
99  TF_DeleteSessionOptions};
100 
101  auto session_deleter = [this](TF_Session* sess) {
102  TF_DeleteSession(sess, this->status.get());
103  status_check(this->status.get());
104  };
105 
106  if (type == TYPE::SAVED_MODEL) {
107  std::unique_ptr<TF_Buffer, decltype(&TF_DeleteBuffer)> run_options = {TF_NewBufferFromString("", 0),
108  TF_DeleteBuffer};
109  std::unique_ptr<TF_Buffer, decltype(&TF_DeleteBuffer)> meta_graph = {TF_NewBuffer(), TF_DeleteBuffer};
110 
111  int tag_len = 1;
112  const char* tag = "serve";
113  this->session = {TF_LoadSessionFromSavedModel(session_options.get(), run_options.get(), filename.c_str(), &tag,
114  tag_len, this->graph.get(), meta_graph.get(), this->status.get()),
115  session_deleter};
116  } else if (type == TYPE::FROZEN_GRAPH) {
117  this->session = {TF_NewSession(this->graph.get(), session_options.get(), this->status.get()), session_deleter};
118  status_check(this->status.get());
119 
120  // Import the graph definition
121  TF_Buffer* def = readGraph(filename);
122  if (def == nullptr) {
123  throw std::runtime_error("Failed to import graph def from file");
124  }
125 
126  std::unique_ptr<TF_ImportGraphDefOptions, decltype(&TF_DeleteImportGraphDefOptions)> graph_opts = {
127  TF_NewImportGraphDefOptions(), TF_DeleteImportGraphDefOptions};
128  TF_GraphImportGraphDef(this->graph.get(), def, graph_opts.get(), this->status.get());
129  TF_DeleteBuffer(def);
130  } else {
131  throw std::runtime_error("Model type unknown");
132  }
133 
134  status_check(this->status.get());
135 }
136 
137 inline std::vector<std::string> model::get_operations() const {
138  std::vector<std::string> result;
139  size_t pos = 0;
140  TF_Operation* oper;
141 
142  // Iterate through the operations of a graph
143  while ((oper = TF_GraphNextOperation(this->graph.get(), &pos)) != nullptr) {
144  result.emplace_back(TF_OperationName(oper));
145  }
146  return result;
147 }
148 
149 inline std::vector<int64_t> model::get_operation_shape(const std::string& operation) const {
150  // Get operation by the name
151  TF_Output out_op;
152  out_op.oper = TF_GraphOperationByName(this->graph.get(), operation.c_str());
153  out_op.index = 0;
154 
155  std::vector<int64_t> shape;
156 
157  // Operation does not exist
158  if (!out_op.oper) throw std::runtime_error("No operation named \"" + operation + "\" exists");
159 
160  if (operation == "NoOp") throw std::runtime_error("NoOp doesn't have a shape");
161 
162  // DIMENSIONS
163 
164  // Get number of dimensions
165  int n_dims = TF_GraphGetTensorNumDims(this->graph.get(), out_op, this->status.get());
166 
167  // If is not a scalar
168  if (n_dims > 0) {
169  // Get dimensions
170  auto* dims = new int64_t[n_dims];
171  TF_GraphGetTensorShape(this->graph.get(), out_op, dims, n_dims, this->status.get());
172 
173  // Check error on Model Status
174  status_check(this->status.get());
175 
176  shape = std::vector<int64_t>(dims, dims + n_dims);
177 
178  delete[] dims;
179  }
180 
181  return shape;
182 }
183 
184 inline std::tuple<std::string, int> parse_name(const std::string& name) {
185  auto idx = name.find(':');
186  return (idx == std::string::npos ? std::make_tuple(name, 0)
187  : std::make_tuple(name.substr(0, idx), std::stoi(name.substr(idx + 1))));
188 }
189 
190 inline std::vector<tensor> model::operator()(std::vector<std::tuple<std::string, tensor>> inputs,
191  std::vector<std::string> outputs) {
192  std::vector<TF_Output> inp_ops(inputs.size());
193  std::vector<TF_Tensor*> inp_val(inputs.size(), nullptr);
194 
195  for (decltype(inputs.size()) i = 0; i < inputs.size(); i++) {
196  // Operations
197  const auto [op_name, op_idx] = parse_name(std::get<0>(inputs[i]));
198  inp_ops[i].oper = TF_GraphOperationByName(this->graph.get(), op_name.c_str());
199  inp_ops[i].index = op_idx;
200 
201  if (!inp_ops[i].oper) throw std::runtime_error("No operation named \"" + op_name + "\" exists");
202 
203  // Values
204  inp_val[i] = std::get<1>(inputs[i]).get_tensor().get();
205  }
206 
207  std::vector<TF_Output> out_ops(outputs.size());
208  auto out_val = std::make_unique<TF_Tensor*[]>(outputs.size());
209  for (decltype(outputs.size()) i = 0; i < outputs.size(); i++) {
210  const auto [op_name, op_idx] = parse_name(outputs[i]);
211  out_ops[i].oper = TF_GraphOperationByName(this->graph.get(), op_name.c_str());
212  out_ops[i].index = op_idx;
213 
214  if (!out_ops[i].oper) throw std::runtime_error("No operation named \"" + op_name + "\" exists");
215  }
216 
217  TF_SessionRun(this->session.get(), /*run_options*/ NULL, inp_ops.data(), inp_val.data(),
218  static_cast<int>(inputs.size()), out_ops.data(), out_val.get(), static_cast<int>(outputs.size()),
219  /*targets*/ NULL, /*ntargets*/ 0, /*run_metadata*/ NULL, this->status.get());
220  status_check(this->status.get());
221 
222  std::vector<tensor> result;
223  result.reserve(outputs.size());
224  for (decltype(outputs.size()) i = 0; i < outputs.size(); i++) {
225  result.emplace_back(tensor(out_val[i]));
226  }
227 
228  return result;
229 }
230 
231 inline tensor model::operator()(const tensor& input) {
232  return (*this)({{"serving_default_input_1", input}}, {"StatefulPartitionedCall"})[0];
233 }
234 
235 inline TF_Buffer* model::readGraph(const std::string& filename) {
236  std::ifstream file(filename, std::ios::binary | std::ios::ate);
237 
238  // Error opening the file
239  if (!file.is_open()) {
240  std::cerr << "Unable to open file: " << filename << std::endl;
241  return nullptr;
242  }
243 
244  // Cursor is at the end to get size
245  auto size = file.tellg();
246  // Move cursor to the beginning
247  file.seekg(0, std::ios::beg);
248 
249  // Read
250  auto data = std::make_unique<char[]>(size);
251  file.seekg(0, std::ios::beg);
252  file.read(data.get(), size);
253 
254  // Error reading the file
255  if (!file) {
256  std::cerr << "Unable to read the full file: " << filename << std::endl;
257  return nullptr;
258  }
259 
260  // Create tensorflow buffer from read data
261  TF_Buffer* buffer = TF_NewBufferFromString(data.get(), size);
262 
263  // Close file and remove data
264  file.close();
265 
266  return buffer;
267 }
268 
269 } // namespace cppflow
270 
271 #endif // INCLUDE_CPPFLOW_MODEL_H_
Definition: model.h:60
A TensorFlow eager tensor wrapper.
Definition: tensor.h:61