CMS 3D CMS Logo

ONNXRuntime.cc
Go to the documentation of this file.
1 /*
2  * ONNXRuntime.cc
3  *
4  * Created on: Jun 28, 2019
5  * Author: hqu
6  */
7 
9 
12 #include <algorithm>
13 #include <cassert>
14 #include <functional>
15 #include <iostream>
16 #include <memory>
17 #include <numeric>
18 
19 namespace cms::Ort {
20 
21  using namespace ::Ort;
22 
23  const Env ONNXRuntime::env_(ORT_LOGGING_LEVEL_ERROR, "");
24 
25  ONNXRuntime::ONNXRuntime(const std::string& model_path, const SessionOptions* session_options) {
26  // create session
27  if (session_options) {
28  session_ = std::make_unique<Session>(env_, model_path.c_str(), *session_options);
29  } else {
30  session_ = std::make_unique<Session>(env_, model_path.c_str(), defaultSessionOptions());
31  }
32  AllocatorWithDefaultOptions allocator;
33 
34  // get input names and shapes
35  size_t num_input_nodes = session_->GetInputCount();
36  input_node_strings_.resize(num_input_nodes);
37  input_node_names_.resize(num_input_nodes);
38  input_node_dims_.clear();
39 
40  for (size_t i = 0; i < num_input_nodes; i++) {
41  // get input node names
42  std::string input_name(session_->GetInputNameAllocated(i, allocator).get());
43  input_node_strings_[i] = input_name;
45 
46  // get input shapes
47  auto type_info = session_->GetInputTypeInfo(i);
48  auto tensor_info = type_info.GetTensorTypeAndShapeInfo();
49 
50  input_node_dims_[input_name] = tensor_info.GetShape();
51  }
52 
53  size_t num_output_nodes = session_->GetOutputCount();
54  output_node_strings_.resize(num_output_nodes);
55  output_node_names_.resize(num_output_nodes);
56  output_node_dims_.clear();
57 
58  for (size_t i = 0; i < num_output_nodes; i++) {
59  // get output node names
60  std::string output_name(session_->GetOutputNameAllocated(i, allocator).get());
61  output_node_strings_[i] = output_name;
63 
64  // get output node types
65  auto type_info = session_->GetOutputTypeInfo(i);
66  auto tensor_info = type_info.GetTensorTypeAndShapeInfo();
67  output_node_dims_[output_name] = tensor_info.GetShape();
68 
69  // the 0th dim depends on the batch size
70  output_node_dims_[output_name].at(0) = -1;
71  }
72  }
73 
75 
77  SessionOptions sess_opts;
78  sess_opts.SetIntraOpNumThreads(1);
79  if (backend == Backend::cuda) {
80  // https://www.onnxruntime.ai/docs/reference/execution-providers/CUDA-ExecutionProvider.html
81  OrtCUDAProviderOptions options;
82  sess_opts.AppendExecutionProvider_CUDA(options);
83  }
84  return sess_opts;
85  }
86 
87  FloatArrays ONNXRuntime::run(const std::vector<std::string>& input_names,
88  FloatArrays& input_values,
89  const std::vector<std::vector<int64_t>>& input_shapes,
90  const std::vector<std::string>& output_names,
91  int64_t batch_size) const {
92  assert(input_names.size() == input_values.size());
93  assert(input_shapes.empty() || input_names.size() == input_shapes.size());
94  assert(batch_size > 0);
95 
96  // create input tensor objects from data values
97  std::vector<Value> input_tensors;
98  auto memory_info = MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
99  for (const auto& name : input_node_strings_) {
100  auto iter = std::find(input_names.begin(), input_names.end(), name);
101  if (iter == input_names.end()) {
102  throw cms::Exception("RuntimeError") << "Input " << name << " is not provided!";
103  }
104  auto input_pos = iter - input_names.begin();
105  auto value = input_values.begin() + input_pos;
106  std::vector<int64_t> input_dims;
107  if (input_shapes.empty()) {
108  input_dims = input_node_dims_.at(name);
109  input_dims[0] = batch_size;
110  } else {
111  input_dims = input_shapes[input_pos];
112  // rely on the given input_shapes to set the batch size
113  if (input_dims[0] != batch_size) {
114  throw cms::Exception("RuntimeError") << "The first element of `input_shapes` (" << input_dims[0]
115  << ") does not match the given `batch_size` (" << batch_size << ")";
116  }
117  }
118  auto expected_len = std::accumulate(input_dims.begin(), input_dims.end(), 1, std::multiplies<int64_t>());
119  if (expected_len != (int64_t)value->size()) {
120  throw cms::Exception("RuntimeError")
121  << "Input array " << name << " has a wrong size of " << value->size() << ", expected " << expected_len;
122  }
123  auto input_tensor =
124  Value::CreateTensor<float>(memory_info, value->data(), value->size(), input_dims.data(), input_dims.size());
125  assert(input_tensor.IsTensor());
126  input_tensors.emplace_back(std::move(input_tensor));
127  }
128 
129  // set output node names; will get all outputs if `output_names` is not provided
130  std::vector<const char*> run_output_node_names;
131  if (output_names.empty()) {
132  run_output_node_names = output_node_names_;
133  } else {
134  for (const auto& name : output_names) {
135  run_output_node_names.push_back(name.c_str());
136  }
137  }
138 
139  // run
140  auto output_tensors = session_->Run(RunOptions{nullptr},
141  input_node_names_.data(),
142  input_tensors.data(),
143  input_tensors.size(),
144  run_output_node_names.data(),
145  run_output_node_names.size());
146 
147  // convert output to floats
149  for (auto& output_tensor : output_tensors) {
150  assert(output_tensor.IsTensor());
151 
152  // get output shape
153  auto tensor_info = output_tensor.GetTensorTypeAndShapeInfo();
154  auto length = tensor_info.GetElementCount();
155 
156  auto floatarr = output_tensor.GetTensorMutableData<float>();
157  outputs.emplace_back(floatarr, floatarr + length);
158  }
159  assert(outputs.size() == run_output_node_names.size());
160 
161  return outputs;
162  }
163 
164  const std::vector<std::string>& ONNXRuntime::getOutputNames() const {
165  if (session_) {
166  return output_node_strings_;
167  } else {
168  throw cms::Exception("RuntimeError") << "Needs to call createSession() first before getting the output names!";
169  }
170  }
171 
172  const std::vector<int64_t>& ONNXRuntime::getOutputShape(const std::string& output_name) const {
173  auto iter = output_node_dims_.find(output_name);
174  if (iter == output_node_dims_.end()) {
175  throw cms::Exception("RuntimeError") << "Output name " << output_name << " is invalid!";
176  } else {
177  return iter->second;
178  }
179  }
180 
181 } /* namespace cms::Ort */
std::unique_ptr<::Ort::Session > session_
Definition: ONNXRuntime.h:62
std::map< std::string, std::vector< int64_t > > input_node_dims_
Definition: ONNXRuntime.h:66
::Ort::SessionOptions defaultSessionOptions(Backend backend=Backend::cpu)
Definition: ONNXRuntime.cc:76
std::map< std::string, std::vector< int64_t > > output_node_dims_
Definition: ONNXRuntime.h:70
static const ::Ort::Env env_
Definition: ONNXRuntime.h:61
void find(edm::Handle< EcalRecHitCollection > &hits, DetId thisDet, std::vector< EcalRecHitCollection::const_iterator > &hit, bool debug=false)
Definition: FindCaloHit.cc:19
std::vector< std::vector< float > > FloatArrays
Definition: ONNXRuntime.h:23
assert(be >=bs)
const std::vector< std::string > & getOutputNames() const
Definition: ONNXRuntime.cc:164
ONNXRuntime(const std::string &model_path, const ::Ort::SessionOptions *session_options=nullptr)
Definition: value.py:1
const std::vector< int64_t > & getOutputShape(const std::string &output_name) const
Definition: ONNXRuntime.cc:172
std::vector< const char * > output_node_names_
Definition: ONNXRuntime.h:69
std::vector< std::string > input_node_strings_
Definition: ONNXRuntime.h:64
std::vector< const char * > input_node_names_
Definition: ONNXRuntime.h:65
def move(src, dest)
Definition: eostools.py:511
std::vector< std::string > output_node_strings_
Definition: ONNXRuntime.h:68
FloatArrays run(const std::vector< std::string > &input_names, FloatArrays &input_values, const std::vector< std::vector< int64_t >> &input_shapes={}, const std::vector< std::string > &output_names={}, int64_t batch_size=1) const
Definition: ONNXRuntime.cc:87