tensorflow_cpp 1.0.6
Loading...
Searching...
No Matches
model.h
Go to the documentation of this file.
1/*
2==============================================================================
3MIT License
4Copyright 2022 Institute for Automotive Engineering of RWTH Aachen University.
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11The above copyright notice and this permission notice shall be included in all
12copies or substantial portions of the Software.
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19SOFTWARE.
20==============================================================================
21*/
22
28#pragma once
29
30#include <stdexcept>
31#include <string>
32#include <unordered_map>
33#include <utility>
34#include <vector>
35
36#include <tensorflow/core/platform/env.h>
37#include <tensorflow/core/public/session.h>
41
45namespace tensorflow_cpp {
46
47
51class Model {
52
53 public:
57 Model() {}
58
69 Model(const std::string& model_path, const bool warmup = false,
70 const bool allow_growth = true,
71 const double per_process_gpu_memory_fraction = 0,
72 const std::string& visible_device_list = "") {
73
74 loadModel(model_path, warmup, allow_growth, per_process_gpu_memory_fraction,
75 visible_device_list);
76 }
77
91 void loadModel(const std::string& model_path, const bool warmup = false,
92 const bool allow_growth = true,
93 const double per_process_gpu_memory_fraction = 0,
94 const std::string& visible_device_list = "") {
95
96 is_frozen_graph_ = (model_path.substr(model_path.size() - 3) == ".pb");
98
99 // load model
100 if (is_frozen_graph_) {
101 graph_def_ = loadFrozenGraph(model_path);
102 session_ = createSession(allow_growth, per_process_gpu_memory_fraction,
103 visible_device_list);
105 } else {
107 loadSavedModel(model_path, allow_growth,
108 per_process_gpu_memory_fraction, visible_device_list);
109 session_ = saved_model_.GetSession();
110 }
111
112 // automatically find inputs and outputs
113 if (is_frozen_graph_) {
116 } else {
119 const auto input_nodes_ = getSavedModelInputNames(saved_model_, false);
120 const auto output_nodes_ = getSavedModelOutputNames(saved_model_, false);
121 for (int k = 0; k < input_names_.size(); k++) {
122 saved_model_node2layer_[input_nodes_[k]] = input_names_[k];
123 saved_model_layer2node_[input_names_[k]] = input_nodes_[k];
124 }
125 for (int k = 0; k < output_names_.size(); k++) {
126 saved_model_node2layer_[output_nodes_[k]] = output_names_[k];
127 saved_model_layer2node_[output_names_[k]] = output_nodes_[k];
128 }
129 }
130 n_inputs_ = input_names_.size();
131 n_outputs_ = output_names_.size();
132
133 // run dummy inference to warm-up
134 if (warmup) dummyCall();
135 }
136
143 bool isLoaded() const {
144
145 bool is_loaded = bool(session_);
146
147 return is_loaded;
148 }
149
163 std::unordered_map<std::string, tf::Tensor> operator()(
164 const std::vector<std::pair<std::string, tf::Tensor>>& inputs,
165 const std::vector<std::string>& output_names) const{
166
167 // properly set input/output names for session->Run()
168 std::vector<std::pair<std::string, tf::Tensor>> input_nodes;
169 std::vector<std::string> output_node_names;
170 if (is_saved_model_) {
171 for (const auto& input : inputs)
172 input_nodes.push_back(
173 {saved_model_layer2node_.find(input.first)->second, input.second});
174 for (const auto& name : output_names)
175 output_node_names.push_back(saved_model_layer2node_.find(name)->second);
176 } else if (is_frozen_graph_) {
177 input_nodes = inputs;
178 output_node_names = output_names;
179 } else {
180 return {};
181 }
182
183 // run model
184 tf::Status status;
185 std::vector<tf::Tensor> output_tensors;
186 status = session_->Run(input_nodes, output_node_names, {}, &output_tensors);
187
188 // build outputs
189 std::unordered_map<std::string, tf::Tensor> outputs;
190 if (status.ok()) {
191 for (int k = 0; k < output_tensors.size(); k++)
192 outputs[output_names[k]] = output_tensors[k];
193 } else {
194 throw std::runtime_error("Failed to run model: " + status.ToString());
195 }
196
197 return outputs;
198 }
199
210 tf::Tensor operator()(const tf::Tensor& input_tensor) const {
211
212 if (n_inputs_ != 1 || n_outputs_ != 1) {
213 throw std::runtime_error(
214 "'tf::Tensor tensorflow_cpp::Model::operator()(const tf::Tensor&)' is "
215 "only available for single-input/single-output models. Found " +
216 std::to_string(n_inputs_) + " inputs and " +
217 std::to_string(n_outputs_) + " outputs.");
218 }
219
220 // run model
221 auto outputs =
222 (*this)({{input_names_[0], input_tensor}}, {output_names_[0]});
223
224 return outputs[output_names_[0]];
225 }
226
237 std::vector<tf::Tensor> operator()(
238 const std::vector<tf::Tensor>& input_tensors) const {
239
240 if (input_tensors.size() != n_inputs_) {
241 throw std::runtime_error(
242 "Model has " + std::to_string(n_inputs_) + " inputs, but " +
243 std::to_string(input_tensors.size()) + " input tensors were given");
244 }
245
246 // assign inputs in default order
247 std::vector<std::pair<std::string, tf::Tensor>> inputs;
248 for (int k = 0; k < n_inputs_; k++)
249 inputs.push_back({input_names_[k], input_tensors[k]});
250
251 // run model
252 auto outputs = (*this)(inputs, output_names_);
253
254 // return output tensors in default order
255 std::vector<tf::Tensor> output_tensors;
256 for (const auto& name : output_names_)
257 output_tensors.push_back(outputs[name]);
258
259 return output_tensors;
260 }
261
269 std::vector<int> getNodeShape(const std::string& name) {
270
271 if (is_saved_model_) {
272 return getSavedModelNodeShape(saved_model_,
273 saved_model_layer2node_[name]);
274 } else if (is_frozen_graph_) {
275 return getGraphNodeShape(graph_def_, name);
276 } else {
277 return {};
278 }
279 }
280
289 std::vector<int> getInputShape() {
290
291 if (n_inputs_ != 1) {
292 throw std::runtime_error(
293 "std::vector<int> tensorflow_cpp::Model::getInputShape()' is only "
294 "available for single-input models. Found " +
295 std::to_string(n_inputs_) + " inputs.");
296 }
297
298 return getNodeShape(input_names_[0]);
299 }
300
309 std::vector<int> getOutputShape() {
310
311 if (n_outputs_ != 1) {
312 throw std::runtime_error(
313 "std::vector<int> tensorflow_cpp::Model::getOutputShape()' is only "
314 "available for single-output models. Found " +
315 std::to_string(n_outputs_) + " outputs.");
316 }
317
318 return getNodeShape(output_names_[0]);
319 }
320
326 std::vector<std::vector<int>> getInputShapes() {
327
328 std::vector<std::vector<int>> shapes;
329 for (const auto& name : input_names_) shapes.push_back(getNodeShape(name));
330
331 return shapes;
332 }
333
339 std::vector<std::vector<int>> getOutputShapes() {
340
341 std::vector<std::vector<int>> shapes;
342 for (const auto& name : output_names_) shapes.push_back(getNodeShape(name));
343
344 return shapes;
345 }
346
354 tf::DataType getNodeType(const std::string& name) {
355
356 if (is_saved_model_) {
357 return getSavedModelNodeType(saved_model_, saved_model_layer2node_[name]);
358 } else if (is_frozen_graph_) {
359 return getGraphNodeType(graph_def_, name);
360 } else {
361 return tf::DataType();
362 }
363 }
364
373 tf::DataType getInputType() {
374
375 if (n_inputs_ != 1) {
376 throw std::runtime_error(
377 "'tf::DataType tensorflow_cpp::Model::getInputType()' is only "
378 "available for single-input models. Found " +
379 std::to_string(n_inputs_) + " inputs.");
380 }
381
382 return getNodeType(input_names_[0]);
383 }
384
393 tf::DataType getOutputType() {
394
395 if (n_outputs_ != 1) {
396 throw std::runtime_error(
397 "'tf::DataType tensorflow_cpp::Model::getOutputType()' is only "
398 "available for single-output models. Found " +
399 std::to_string(n_outputs_) + " outputs.");
400 }
401
402 return getNodeType(output_names_[0]);
403 }
404
410 std::vector<tf::DataType> getInputTypes() {
411
412 std::vector<tf::DataType> types;
413 for (const auto& name : input_names_) types.push_back(getNodeType(name));
414
415 return types;
416 }
417
423 std::vector<tf::DataType> getOutputTypes() {
424
425 std::vector<tf::DataType> types;
426 for (const auto& name : output_names_) types.push_back(getNodeType(name));
427
428 return types;
429 }
430
439 std::string getInfoString() {
440
441 if (is_saved_model_) {
442 return getSavedModelInfoString(saved_model_);
443 } else if (is_frozen_graph_) {
444 return getGraphInfoString(graph_def_);
445 } else {
446 return "";
447 }
448 }
449
455 tf::Session* session() const {
456 return session_;
457 }
458
464 const tf::SavedModelBundleLite& savedModel() const {
465 return saved_model_;
466 }
467
473 const tf::GraphDef& frozenGraph() const {
474 return graph_def_;
475 }
476
483 bool isSavedModel() const {
484 return is_saved_model_;
485 }
486
493 bool isFrozenGraph() const {
494 return is_frozen_graph_;
495 }
496
502 int nInputs() const {
503 return n_inputs_;
504 }
505
511 int nOutputs() const {
512 return n_outputs_;
513 }
514
520 std::vector<std::string> inputNames() const {
521 return input_names_;
522 }
523
529 std::vector<std::string> outputNames() const {
530 return output_names_;
531 }
532
533 protected:
537 void dummyCall() {
538
539 // infer input shapes/types to create dummy input tensors
540 auto input_shapes = getInputShapes();
541 auto input_types = getInputTypes();
542 std::vector<tf::Tensor> input_dummies;
543 for (int k = 0; k < n_inputs_; k++) {
544 std::vector<long int> dummy_shape(input_shapes[k].begin(),
545 input_shapes[k].end());
546 // Replace -1 (batch size dimension, None in python) with 1
547 std::replace(dummy_shape.begin(), dummy_shape.end(), -1l, 1l);
548 auto dummy_tensor_shape =
549 tf::TensorShape(tf::gtl::ArraySlice<long int>(dummy_shape));
550 tf::Tensor dummy(input_types[k], dummy_tensor_shape);
551 // init to zero, based on type
552 switch (input_types[k]) {
553 case tf::DT_FLOAT:
554 dummy.flat<float>().setZero();
555 break;
556 case tf::DT_DOUBLE:
557 dummy.flat<double>().setZero();
558 case tf::DT_INT32:
559 dummy.flat<tf::int32>().setZero();
560 break;
561 case tf::DT_UINT32:
562 dummy.flat<tf::uint32>().setZero();
563 break;
564 case tf::DT_UINT8:
565 dummy.flat<tf::uint8>().setZero();
566 break;
567 case tf::DT_UINT16:
568 dummy.flat<tf::uint16>().setZero();
569 break;
570 case tf::DT_INT16:
571 dummy.flat<tf::int16>().setZero();
572 break;
573 case tf::DT_INT8:
574 dummy.flat<tf::int8>().setZero();
575 break;
576 case tf::DT_STRING:
577 dummy.flat<tf::tstring>().setZero();
578 break;
579 case tf::DT_COMPLEX64:
580 dummy.flat<tf::complex64>().setZero();
581 break;
582 case tf::DT_COMPLEX128:
583 dummy.flat<tf::complex128>().setZero();
584 break;
585 case tf::DT_INT64:
586 dummy.flat<tf::int64>().setZero();
587 break;
588 case tf::DT_UINT64:
589 dummy.flat<tf::uint64>().setZero();
590 break;
591 case tf::DT_BOOL:
592 dummy.flat<bool>().setZero();
593 break;
594 case tf::DT_QINT8:
595 dummy.flat<tf::qint8>().setZero();
596 break;
597 case tf::DT_QUINT8:
598 dummy.flat<tf::quint8>().setZero();
599 break;
600 case tf::DT_QUINT16:
601 dummy.flat<tf::quint16>().setZero();
602 break;
603 case tf::DT_QINT16:
604 dummy.flat<tf::qint16>().setZero();
605 break;
606 case tf::DT_QINT32:
607 dummy.flat<tf::qint32>().setZero();
608 break;
609 case tf::DT_BFLOAT16:
610 dummy.flat<tf::bfloat16>().setZero();
611 break;
612 case tf::DT_HALF:
613 dummy.flat<Eigen::half>().setZero();
614 break;
615 }
616 input_dummies.push_back(dummy);
617 }
618
619 // run dummy inference
620 volatile auto output_dummies = (*this)(input_dummies);
621 }
622
623 protected:
627 tf::Session* session_ = nullptr;
628
632 tf::SavedModelBundleLite saved_model_;
633
637 tf::GraphDef graph_def_;
638
642 bool is_saved_model_ = false;
643
647 bool is_frozen_graph_ = false;
648
653
658
662 std::vector<std::string> input_names_;
663
667 std::vector<std::string> output_names_;
668
672 std::unordered_map<std::string, std::string> saved_model_node2layer_;
673
677 std::unordered_map<std::string, std::string> saved_model_layer2node_;
678};
679
680
681} // namespace tensorflow_cpp
Wrapper class for running TensorFlow SavedModels or FrozenGraphs.
Definition model.h:51
bool is_saved_model_
whether loaded model is from SavedModel
Definition model.h:642
int nInputs() const
Returns number of model inputs.
Definition model.h:502
tf::DataType getNodeType(const std::string &name)
Determines the datatype of a model node.
Definition model.h:354
std::unordered_map< std::string, tf::Tensor > operator()(const std::vector< std::pair< std::string, tf::Tensor > > &inputs, const std::vector< std::string > &output_names) const
Runs the model.
Definition model.h:163
std::vector< tf::DataType > getOutputTypes()
Determines the datatype of the model outputs.
Definition model.h:423
std::vector< int > getNodeShape(const std::string &name)
Determines the shape of a model node.
Definition model.h:269
int n_outputs_
number of model outputs
Definition model.h:657
tf::Tensor operator()(const tf::Tensor &input_tensor) const
Runs the model.
Definition model.h:210
std::vector< int > getInputShape()
Determines the shape of the model input.
Definition model.h:289
tf::Session * session_
underlying TensorFlow session
Definition model.h:627
std::string getInfoString()
Returns information about the model.
Definition model.h:439
std::unordered_map< std::string, std::string > saved_model_layer2node_
mapping between SavedModel layer and node input/output names
Definition model.h:677
tf::GraphDef graph_def_
underlying FrozenGraph GraphDef
Definition model.h:637
bool isFrozenGraph() const
Returns whether loaded model is from FrozenGraph.
Definition model.h:493
std::vector< tf::DataType > getInputTypes()
Determines the datatype of the model inputs.
Definition model.h:410
std::vector< std::vector< int > > getInputShapes()
Determines the shape of the model inputs.
Definition model.h:326
int nOutputs() const
Returns number of model outputs.
Definition model.h:511
bool isSavedModel() const
Returns whether loaded model is from SavedModel.
Definition model.h:483
std::vector< std::string > input_names_
(layer) names of model inputs
Definition model.h:662
tf::DataType getInputType()
Determines the datatype of the model input.
Definition model.h:373
bool is_frozen_graph_
whether loaded model is from FrozenGraph
Definition model.h:647
std::vector< tf::Tensor > operator()(const std::vector< tf::Tensor > &input_tensors) const
Runs the model.
Definition model.h:237
int n_inputs_
number of model inputs
Definition model.h:652
std::vector< std::string > output_names_
(layer) names of model outputs
Definition model.h:667
const tf::GraphDef & frozenGraph() const
Returns the underlying FrozenGraph GraphDef.
Definition model.h:473
tf::DataType getOutputType()
Determines the datatype of the model output.
Definition model.h:393
void dummyCall()
Runs the model once with dummy input to speed-up first inference.
Definition model.h:537
bool isLoaded() const
Checks whether the model is loaded already.
Definition model.h:143
tf::Session * session() const
Returns the underlying TensorFlow session.
Definition model.h:455
const tf::SavedModelBundleLite & savedModel() const
Returns the underlying SavedModel.
Definition model.h:464
Model()
Creates an uninitialized model.
Definition model.h:57
tf::SavedModelBundleLite saved_model_
underlying SavedModel
Definition model.h:632
Model(const std::string &model_path, const bool warmup=false, const bool allow_growth=true, const double per_process_gpu_memory_fraction=0, const std::string &visible_device_list="")
Creates a model by loading it from disk.
Definition model.h:69
std::unordered_map< std::string, std::string > saved_model_node2layer_
mapping between SavedModel node and layer input/output names
Definition model.h:672
std::vector< int > getOutputShape()
Determines the shape of the model output.
Definition model.h:309
void loadModel(const std::string &model_path, const bool warmup=false, const bool allow_growth=true, const double per_process_gpu_memory_fraction=0, const std::string &visible_device_list="")
Loads a SavedModel or FrozenGraph model from disk.
Definition model.h:91
std::vector< std::vector< int > > getOutputShapes()
Determines the shape of the model outputs.
Definition model.h:339
std::vector< std::string > outputNames() const
Returns names of model outputs.
Definition model.h:529
std::vector< std::string > inputNames() const
Returns names of model inputs.
Definition model.h:520
Utility functions for FrozenGraphs.
Namespace for tensorflow_cpp library.
Definition graph_utils.h:40
std::vector< std::string > getGraphOutputNames(const tf::GraphDef &graph_def)
Determines the names of all graph output nodes.
std::string getGraphInfoString(const tf::GraphDef &graph_def)
tf::SavedModelBundleLite loadSavedModel(const std::string &dir, const bool allow_growth=true, const double per_process_gpu_memory_fraction=0, const std::string &visible_device_list="")
Loads a TensorFlow SavedModel from a directory into a new session.
tf::GraphDef loadFrozenGraph(const std::string &file)
Loads a TensorFlow graph from a frozen graph file.
Definition graph_utils.h:53
std::vector< std::string > getSavedModelInputNames(const tf::SavedModelBundleLite &saved_model, const bool layer_names=false, const std::string &signature="serving_default")
Determines the names of the SavedModel input nodes.
std::vector< int > getGraphNodeShape(const tf::GraphDef &graph_def, const std::string &node_name)
Determines the shape of a given graph node.
std::vector< std::string > getGraphInputNames(const tf::GraphDef &graph_def)
Determines the names of all graph input nodes.
std::string getSavedModelInfoString(const tf::SavedModelBundleLite &saved_model)
bool loadGraphIntoSession(tf::Session *session, const tf::GraphDef &graph_def)
Loads a TensorFlow graph into an existing session.
Definition graph_utils.h:74
tf::DataType getSavedModelNodeType(const tf::SavedModelBundleLite &saved_model, const std::string &node_name, const std::string &signature="serving_default")
Determines the datatype of a given SavedModel node.
std::vector< std::string > getSavedModelOutputNames(const tf::SavedModelBundleLite &saved_model, const bool layer_names=false, const std::string &signature="serving_default")
Determines the names of the SavedModel output nodes.
tf::DataType getGraphNodeType(const tf::GraphDef &graph_def, const std::string &node_name)
Determines the datatype of a given graph node.
tf::Session * createSession(const bool allow_growth=true, const double per_process_gpu_memory_fraction=0, const std::string &visible_device_list="")
Creates a new TensorFlow session.
Definition utils.h:78
std::vector< int > getSavedModelNodeShape(const tf::SavedModelBundleLite &saved_model, const std::string &node_name, const std::string &signature="serving_default")
Determines the shape of a given SavedModel node.
Utility functions for SavedModels.
Utility functions for TensorFlow backend.