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