CMS 3D CMS Logo

OnlineDQMDigiAD_cmssw.cc
Go to the documentation of this file.
1 /*
2  * OnlineDQMDigiAD_cmssw.cpp
3  *
4  * Created on: Jun 10, 2023
5  * Author: Mulugeta W.Asres, UiA, Norway
6  *
7  * The implementation follows https://github.com/cms-sw/cmssw/tree/master/PhysicsTools/ONNXRuntime
8  */
9 
10 // #include "FWCore/Utilities/interface/Exception.h"
11 // #include "FWCore/Utilities/interface/thread_safety_macros.h"
12 // #include "FWCore/Framework/interface/Event.h"
16 
17 #include <algorithm>
18 #include <cassert>
19 #include <functional>
20 #include <iostream>
21 #include <memory>
22 #include <numeric>
23 #include <algorithm>
24 
26 
27 // using namespace std;
28 using namespace cms::Ort;
29 
30 // Constructor
32  const std::string &modelFilepath,
33  Backend backend) {
34  std::string instanceName{"DESMOD Digioccupancy Map AD inference"};
35 
36  /**************** Initailize Model Memory States ******************/
37  InitializeState(); // initailize model memory states to zero
38 
39  /**************** Create ORT session ******************/
40  // Set up options for session
41  auto session_options = ONNXRuntime::defaultSessionOptions(backend);
42  // Create session by loading the onnx model
43  model_path = edm::FileInPath(modelFilepath).fullPath();
44  auto uOrtSession = std::make_unique<ONNXRuntime>(model_path, &session_options);
45  ort_mSession = std::move(uOrtSession);
46 
47  // check model availability
48  hcal_subsystem_name = model_system_name;
49 
50  IsModelExist(hcal_subsystem_name); // assert model integration for the given hcal system name
51 
52  if (hcal_subsystem_name == "he") {
53  std::vector<std::vector<int64_t>> input_shapes_ = {
54  {batch_size, 64, 72, 7, 1},
55  {batch_size, 1},
56  {1, 1},
57  {batch_size, model_state_inner_dim, model_state_layer_dims[0][0]},
58  {batch_size, model_state_inner_dim, model_state_layer_dims[0][0]},
59  {batch_size, model_state_inner_dim, model_state_layer_dims[0][1]},
60  {batch_size, model_state_inner_dim, model_state_layer_dims[0][1]},
61  {batch_size, model_state_inner_dim, model_state_layer_dims[1][0]},
62  {batch_size, model_state_inner_dim, model_state_layer_dims[1][0]},
63  {batch_size, model_state_inner_dim, model_state_layer_dims[1][1]},
64  {batch_size, model_state_inner_dim, model_state_layer_dims[1][1]}}; // input dims
65  input_shapes = input_shapes_;
66  }
67 
68  else if (hcal_subsystem_name == "hb") {
69  std::vector<std::vector<int64_t>> input_shapes_ = {
70  {batch_size, 64, 72, 4, 1},
71  {batch_size, 1},
72  {1, 1},
73  {batch_size, model_state_inner_dim, model_state_layer_dims[0][0]},
74  {batch_size, model_state_inner_dim, model_state_layer_dims[0][0]},
75  {batch_size, model_state_inner_dim, model_state_layer_dims[0][1]},
76  {batch_size, model_state_inner_dim, model_state_layer_dims[0][1]},
77  {batch_size, model_state_inner_dim, model_state_layer_dims[1][0]},
78  {batch_size, model_state_inner_dim, model_state_layer_dims[1][0]},
79  {batch_size, model_state_inner_dim, model_state_layer_dims[1][1]},
80  {batch_size, model_state_inner_dim, model_state_layer_dims[1][1]}}; // input dims
81  input_shapes = input_shapes_;
82  }
83 }
84 
85 void OnlineDQMDigiAD::IsModelExist(std::string hcal_subsystem_name) {
86  if (std::find(hcal_modeled_systems.begin(), hcal_modeled_systems.end(), hcal_subsystem_name) ==
87  hcal_modeled_systems.end()) {
89  "ML for OnlineDQM is not currently supported for the selected " + hcal_subsystem_name + " system!\n";
90  throw std::invalid_argument(err);
91  }
92 }
93 
95  // model memory states vectors init, only when the runs starts or for the first LS
96  std::fill(input_model_state_memory_e_0_0.begin(),
97  input_model_state_memory_e_0_0.end(),
98  float(0.0)); // init model memory states-encoder_layer_0_state_0 to zero
99  std::fill(input_model_state_memory_e_0_1.begin(),
100  input_model_state_memory_e_0_1.end(),
101  float(0.0)); // init model memory states-encoder_layer_0_state_1 to zero
102  std::fill(input_model_state_memory_e_1_0.begin(),
103  input_model_state_memory_e_1_0.end(),
104  float(0.0)); // init model memory states-encoder_layer_1_state_0 to zero
105  std::fill(input_model_state_memory_e_1_1.begin(),
106  input_model_state_memory_e_1_1.end(),
107  float(0.0)); // init model memory states-encoder_layer_1_state_1 to zero
108  std::fill(input_model_state_memory_d_0_0.begin(),
109  input_model_state_memory_d_0_0.end(),
110  float(0.0)); // init model memory states-decoder_layer_0_state_0 to zero
111  std::fill(input_model_state_memory_d_0_1.begin(),
112  input_model_state_memory_d_0_1.end(),
113  float(0.0)); // init model memory states-decoder_layer_0_state_1 to zero
114  std::fill(input_model_state_memory_d_1_0.begin(),
115  input_model_state_memory_d_1_0.end(),
116  float(0.0)); // init model memory states-decoder_layer_1_state_0 to zero
117  std::fill(input_model_state_memory_d_1_1.begin(),
118  input_model_state_memory_d_1_1.end(),
119  float(0.0)); // init model memory states-decoder_layer_1_state_1 to zero
120 
121  // model_state_refresh_counter = 15; // counter set due to onnx double datatype handling limitation that might cause precision error to propagate.
122  model_state_refresh_counter =
123  1; // DQM multithread returns non-sequential LS. Hence, the model will not keep states (experimental)
124 }
125 
126 std::vector<float> OnlineDQMDigiAD::Serialize2DVector(const std::vector<std::vector<float>> &input_2d_vec) {
127  std::vector<float> output;
128  for (const auto &row : input_2d_vec) {
129  for (const auto &element : row) {
130  output.push_back(element);
131  }
132  }
133  return output;
134 }
135 
136 std::vector<std::vector<float>> OnlineDQMDigiAD::Map1DTo2DVector(const std::vector<float> &input_1d_vec,
137  const int numSplits) {
138  if (numSplits <= 0)
139  throw std::invalid_argument("numSplits must be greater than 0.");
140 
141  std::size_t const splitted_size = input_1d_vec.size() / numSplits;
142 
143  if (splitted_size * numSplits != input_1d_vec.size())
144  throw std::invalid_argument("Conversion is not allowed! The input vector length " +
145  std::to_string(input_1d_vec.size()) + " must be divisible by the numSplits " +
146  std::to_string(numSplits) + ".");
147 
148  std::vector<std::vector<float>> output_2d_vec;
149 
150  for (int i = 0; i < numSplits; i++) {
151  std::vector<float> chunch_vec(input_1d_vec.begin() + i * splitted_size,
152  input_1d_vec.begin() + (i + 1) * splitted_size);
153  output_2d_vec.push_back(chunch_vec);
154  }
155  return output_2d_vec;
156 }
157 
159  std::vector<std::vector<std::vector<float>>> &digiHcal2DHist_depth_all) {
160  std::vector<float> digi3DHistVector_serialized;
161 
162  for (const std::vector<std::vector<float>> &digiHcal2DHist_depth : digiHcal2DHist_depth_all) {
163  std::vector<float> digiHcalDHist_serialized_depth = Serialize2DVector(digiHcal2DHist_depth);
164  digi3DHistVector_serialized.insert(digi3DHistVector_serialized.end(),
165  digiHcalDHist_serialized_depth.begin(),
166  digiHcalDHist_serialized_depth.end());
167  }
168 
169  return digi3DHistVector_serialized;
170 }
171 
172 std::vector<std::vector<std::vector<float>>> OnlineDQMDigiAD::ONNXOutputToDQMHistMap(
173  const std::vector<std::vector<float>> &ad_model_output_vectors,
174  const int numDepth,
175  const int numDIeta,
176  const int selOutputIdx) {
177  // each output_vector is a serialized 3d hist map
178 
179  const std::vector<float> &output_vector = ad_model_output_vectors[selOutputIdx];
180  std::vector<std::vector<float>> output_2d_vec = Map1DTo2DVector(output_vector, numDepth);
181 
182  std::vector<std::vector<std::vector<float>>> digiHcal3DHist;
183  for (const std::vector<float> &output_vector_depth : output_2d_vec) {
184  std::vector<std::vector<float>> digiHcal2DHist_depth = Map1DTo2DVector(output_vector_depth, numDIeta);
185  digiHcal3DHist.push_back(digiHcal2DHist_depth);
186  }
187 
188  return digiHcal3DHist;
189 }
190 
191 // Perform inference for a given dqm map
192 std::vector<std::vector<float>> OnlineDQMDigiAD::Inference(std::vector<float> &digiHcalMapTW,
193  std::vector<float> &numEvents,
194  std::vector<float> &adThr,
195  std::vector<float> &input_model_state_memory_e_0_0,
196  std::vector<float> &input_model_state_memory_e_0_1,
197  std::vector<float> &input_model_state_memory_e_1_0,
198  std::vector<float> &input_model_state_memory_e_1_1,
199  std::vector<float> &input_model_state_memory_d_0_0,
200  std::vector<float> &input_model_state_memory_d_0_1,
201  std::vector<float> &input_model_state_memory_d_1_0,
202  std::vector<float> &input_model_state_memory_d_1_1) {
203  /**************** Preprocessing ******************/
204  // Create input tensor (including size and value) from the loaded inputs
205  // Compute the product of all input dimension
206  // Assign memory for input tensor
207  // inputTensors will be used by the Session Run for inference
208 
209  input_values.clear();
210  input_values.emplace_back(digiHcalMapTW);
211  input_values.emplace_back(numEvents);
212  input_values.emplace_back(adThr);
213  input_values.emplace_back(input_model_state_memory_e_0_0);
214  input_values.emplace_back(input_model_state_memory_e_0_1);
215  input_values.emplace_back(input_model_state_memory_e_1_0);
216  input_values.emplace_back(input_model_state_memory_e_1_1);
217  input_values.emplace_back(input_model_state_memory_d_0_0);
218  input_values.emplace_back(input_model_state_memory_d_0_1);
219  input_values.emplace_back(input_model_state_memory_d_1_0);
220  input_values.emplace_back(input_model_state_memory_d_1_1);
221 
222  /**************** Inference ******************/
223 
224  output_values = ort_mSession->run(input_names, input_values, input_shapes, output_names, batch_size);
225 
226  return output_values;
227 }
228 
229 // AD method to be called by the CMS system
230 std::vector<std::vector<float>> OnlineDQMDigiAD::Inference_CMSSW(
231  const std::vector<std::vector<float>> &digiHcal2DHist_depth_1,
232  const std::vector<std::vector<float>> &digiHcal2DHist_depth_2,
233  const std::vector<std::vector<float>> &digiHcal2DHist_depth_3,
234  const std::vector<std::vector<float>> &digiHcal2DHist_depth_4,
235  const std::vector<std::vector<float>> &digiHcal2DHist_depth_5,
236  const std::vector<std::vector<float>> &digiHcal2DHist_depth_6,
237  const std::vector<std::vector<float>> &digiHcal2DHist_depth_7,
238  const float LS_numEvents,
239  const float flagDecisionThr)
240 
241 {
242  /**************** Prepare data ******************/
243  // merging all 2d hist into one 3d depth[ieta[iphi]]
244  std::vector<std::vector<std::vector<float>>> digiHcal2DHist_depth_all;
245 
246  if (hcal_subsystem_name == "he") {
247  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_1);
248  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_2);
249  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_3);
250  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_4);
251  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_5);
252  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_6);
253  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_7);
254  }
255 
256  else if (hcal_subsystem_name == "hb") {
257  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_1);
258  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_2);
259  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_3);
260  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_4);
261  }
262 
263  // convert the 3d depth[ieta[iphi]] vector into 1d and commbined
264  std::vector<float> digiHcalMapTW = PrepareONNXDQMMapVectors(digiHcal2DHist_depth_all);
265 
266  std::vector<float> adThr{flagDecisionThr}; // AD decision threshold, increase to reduce sensitivity
267  std::vector<float> numEvents{LS_numEvents};
268 
269  // call model inference
270  /**************** Inference ******************/
271  std::vector<std::vector<float>> output_tensors = Inference(digiHcalMapTW,
272  numEvents,
273  adThr,
274  input_model_state_memory_e_0_0,
275  input_model_state_memory_e_0_1,
276  input_model_state_memory_e_1_0,
277  input_model_state_memory_e_1_1,
278  input_model_state_memory_d_0_0,
279  input_model_state_memory_d_0_1,
280  input_model_state_memory_d_1_0,
281  input_model_state_memory_d_1_1);
282 
283  // auto output_tensors = Inference(digiHcalMapTW, numEvents, adThr);
284  //std::cout << "******* model inference is success *******" << std::endl;
285 
286  /**************** Output post processing ******************/
287  // split outputs into ad output vectors and state_memory vectors
288  std::string state_output_name_tag = "rnn_hidden";
289  std::vector<std::vector<float>> ad_model_output_vectors, ad_model_state_vectors;
290  for (size_t i = 0; i < output_tensors.size(); i++) {
291  std::string output_names_startstr = output_names[i].substr(
292  2, state_output_name_tag.length()); // Extract the same number of characters as str2 from mOutputNames
293  if (output_names_startstr == state_output_name_tag) {
294  ad_model_state_vectors.emplace_back(output_tensors[i]);
295  } else {
296  ad_model_output_vectors.emplace_back(output_tensors[i]);
297  }
298  }
299 
300  if (ad_model_output_vectors.size() == num_state_vectors) {
301  input_model_state_memory_e_0_0 = ad_model_state_vectors[0];
302  input_model_state_memory_e_0_1 = ad_model_state_vectors[1];
303  input_model_state_memory_e_1_0 = ad_model_state_vectors[2];
304  input_model_state_memory_e_1_1 = ad_model_state_vectors[3];
305  input_model_state_memory_d_0_0 = ad_model_state_vectors[4];
306  input_model_state_memory_d_0_1 = ad_model_state_vectors[5];
307  input_model_state_memory_d_1_0 = ad_model_state_vectors[6];
308  input_model_state_memory_d_1_1 = ad_model_state_vectors[7];
309  } else {
310  std::cout << "Warning: the number of output state vectors does NOT equals to expected!. The states are set to "
311  "default values."
312  << std::endl;
313  InitializeState();
314  }
315 
316  // # if onnx is returning serialized 1d vectors instead of vector of 3d vectors
317  // aml score and flag are at index 5 and 7 of the vector ad_model_output_vectors: anomaly score: ad_model_output_vectors[5], anomaly flags: ad_model_output_vectors[7]
318  /*
319  selOutputIdx: index to select of the onnx output. e.g. 5 is the anomaly score and 7 is the anomaly flag (1 is with anomaly, 0 is healthy)
320  std::vector<std::vector<std::vector<float>>> digiHcal3DHist_ANOMALY_FLAG = ONNXOutputToDQMHistMap(ad_model_output_vectors, 7)
321  std::vector<std::vector<std::vector<float>>> digiHcal3DHist_ANOMALY_SCORE = ONNXOutputToDQMHistMap(ad_model_output_vectors, 5)
322  */
323 
324  // reduce counter for each ls call. due to onnx double datatype handling limitation that might cause precision error to propagate.
325  if (--model_state_refresh_counter == 0)
326  InitializeState();
327 
328  return ad_model_output_vectors;
329 }
::Ort::SessionOptions defaultSessionOptions(Backend backend=Backend::cpu)
Definition: ONNXRuntime.cc:76
std::string fullPath() const
Definition: FileInPath.cc:161
std::vector< std::vector< float > > Inference_CMSSW(const std::vector< std::vector< float >> &digiHcal2DHist_depth_1, const std::vector< std::vector< float >> &digiHcal2DHist_depth_2, const std::vector< std::vector< float >> &digiHcal2DHist_depth_3, const std::vector< std::vector< float >> &digiHcal2DHist_depth_4, const std::vector< std::vector< float >> &digiHcal2DHist_depth_5, const std::vector< std::vector< float >> &digiHcal2DHist_depth_6, const std::vector< std::vector< float >> &digiHcal2DHist_depth_7, const float LS_numEvents, const float flagDecisionThr=20)
Perform inference on a single image.
std::vector< std::vector< float > > Map1DTo2DVector(const std::vector< float > &input_1d_vec, const int numSplits)
Converts serialized 1d vectors into 2d.
std::vector< std::vector< float > > Inference(std::vector< float > &digiHcalMapTW, std::vector< float > &numEvents, std::vector< float > &adThr, std::vector< float > &input_model_state_memory_e_0_0, std::vector< float > &input_model_state_memory_e_0_1, std::vector< float > &input_model_state_memory_e_1_0, std::vector< float > &input_model_state_memory_e_1_1, std::vector< float > &input_model_state_memory_d_0_0, std::vector< float > &input_model_state_memory_d_0_1, std::vector< float > &input_model_state_memory_d_1_0, std::vector< float > &input_model_state_memory_d_1_1)
Perform inference on a single image.
void find(edm::Handle< EcalRecHitCollection > &hits, DetId thisDet, std::vector< EcalRecHitCollection::const_iterator > &hit, bool debug=false)
Definition: FindCaloHit.cc:19
static std::string to_string(const XMLCh *ch)
OnlineDQMDigiAD(const std::string model_system_name, const std::string &modelFilepath, cms::Ort::Backend backend=cms::Ort::Backend::cpu)
Constructor.
void InitializeState()
Resets ml model memory states to default and function needs to be called when new collision run start...
std::vector< float > PrepareONNXDQMMapVectors(std::vector< std::vector< std::vector< float >>> &digiHcal2DHist_depth_all)
Prepares model input serialized dqm histogram from 2D histogram inputs from the cmssw.
void IsModelExist(std::string hcal_subsystem_name)
check whether onnx model integration is added for the selected hcal system
Definition: output.py:1
std::vector< float > Serialize2DVector(const std::vector< std::vector< float >> &input_2d_vec)
Serializes 2d vectors into 1d.
def move(src, dest)
Definition: eostools.py:511
std::vector< std::vector< std::vector< float > > > ONNXOutputToDQMHistMap(const std::vector< std::vector< float >> &ad_model_output_vectors, const int numDepth, const int numDIeta=64, const int selOutputIdx=7)
Converts 1D serialized vector output of the onnx into 3d hcal-hehp vector.