CMS 3D CMS Logo

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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  std::size_t const splitted_size = input_1d_vec.size() / numSplits;
140  // check splitted_size*numSplits == input_1d_vec.size()
141  std::vector<std::vector<float>> output_2d_vec;
142 
143  for (size_t i = 0; i < input_1d_vec.size(); i += numSplits - 1) {
144  std::vector<float> chunch_vec(input_1d_vec.begin() + i, input_1d_vec.begin() + i + splitted_size);
145  output_2d_vec.push_back(chunch_vec);
146  }
147  return output_2d_vec;
148 }
149 
151  std::vector<std::vector<std::vector<float>>> &digiHcal2DHist_depth_all) {
152  std::vector<float> digi3DHistVector_serialized;
153 
154  for (const std::vector<std::vector<float>> &digiHcal2DHist_depth : digiHcal2DHist_depth_all) {
155  std::vector<float> digiHcalDHist_serialized_depth = Serialize2DVector(digiHcal2DHist_depth);
156  digi3DHistVector_serialized.insert(digi3DHistVector_serialized.end(),
157  digiHcalDHist_serialized_depth.begin(),
158  digiHcalDHist_serialized_depth.end());
159  }
160 
161  return digi3DHistVector_serialized;
162 }
163 
164 std::vector<std::vector<std::vector<float>>> OnlineDQMDigiAD::ONNXOutputToDQMHistMap(
165  const std::vector<std::vector<float>> &ad_model_output_vectors, const int selOutputIdx) {
166  // each output_vector is a serialized 3d hist map
167  const unsigned short numDepth = 7;
168  const unsigned short numDIeta = 64;
169 
170  const std::vector<float> &output_vector = ad_model_output_vectors[selOutputIdx];
171  std::vector<std::vector<float>> output_2d_vec = Map1DTo2DVector(output_vector, numDepth);
172 
173  std::vector<std::vector<std::vector<float>>> digiHcal3DHist;
174  for (const std::vector<float> &output_vector_depth : output_2d_vec) {
175  std::vector<std::vector<float>> digiHcal2DHist_depth = Map1DTo2DVector(output_vector_depth, numDIeta);
176  digiHcal3DHist.push_back(digiHcal2DHist_depth);
177  }
178 
179  return digiHcal3DHist;
180 }
181 
182 // Perform inference for a given dqm map
183 std::vector<std::vector<float>> OnlineDQMDigiAD::Inference(std::vector<float> &digiHcalMapTW,
184  std::vector<float> &numEvents,
185  std::vector<float> &adThr,
186  std::vector<float> &input_model_state_memory_e_0_0,
187  std::vector<float> &input_model_state_memory_e_0_1,
188  std::vector<float> &input_model_state_memory_e_1_0,
189  std::vector<float> &input_model_state_memory_e_1_1,
190  std::vector<float> &input_model_state_memory_d_0_0,
191  std::vector<float> &input_model_state_memory_d_0_1,
192  std::vector<float> &input_model_state_memory_d_1_0,
193  std::vector<float> &input_model_state_memory_d_1_1) {
194  /**************** Preprocessing ******************/
195  // Create input tensor (including size and value) from the loaded inputs
196  // Compute the product of all input dimension
197  // Assign memory for input tensor
198  // inputTensors will be used by the Session Run for inference
199 
200  input_values.clear();
201  input_values.emplace_back(digiHcalMapTW);
202  input_values.emplace_back(numEvents);
203  input_values.emplace_back(adThr);
204  input_values.emplace_back(input_model_state_memory_e_0_0);
205  input_values.emplace_back(input_model_state_memory_e_0_1);
206  input_values.emplace_back(input_model_state_memory_e_1_0);
207  input_values.emplace_back(input_model_state_memory_e_1_1);
208  input_values.emplace_back(input_model_state_memory_d_0_0);
209  input_values.emplace_back(input_model_state_memory_d_0_1);
210  input_values.emplace_back(input_model_state_memory_d_1_0);
211  input_values.emplace_back(input_model_state_memory_d_1_1);
212 
213  /**************** Inference ******************/
214 
215  output_values = ort_mSession->run(input_names, input_values, input_shapes, output_names, batch_size);
216 
217  return output_values;
218 }
219 
220 // AD method to be called by the CMS system
221 std::vector<std::vector<float>> OnlineDQMDigiAD::Inference_CMSSW(
222  const std::vector<std::vector<float>> &digiHcal2DHist_depth_1,
223  const std::vector<std::vector<float>> &digiHcal2DHist_depth_2,
224  const std::vector<std::vector<float>> &digiHcal2DHist_depth_3,
225  const std::vector<std::vector<float>> &digiHcal2DHist_depth_4,
226  const std::vector<std::vector<float>> &digiHcal2DHist_depth_5,
227  const std::vector<std::vector<float>> &digiHcal2DHist_depth_6,
228  const std::vector<std::vector<float>> &digiHcal2DHist_depth_7,
229  const float LS_numEvents,
230  const float flagDecisionThr)
231 
232 {
233  /**************** Prepare data ******************/
234  // merging all 2d hist into one 3d depth[ieta[iphi]]
235  std::vector<std::vector<std::vector<float>>> digiHcal2DHist_depth_all;
236 
237  if (hcal_subsystem_name == "he") {
238  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_1);
239  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_2);
240  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_3);
241  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_4);
242  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_5);
243  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_6);
244  digiHcal2DHist_depth_all.push_back(digiHcal2DHist_depth_7);
245  }
246 
247  else if (hcal_subsystem_name == "hb") {
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  }
253 
254  // convert the 3d depth[ieta[iphi]] vector into 1d and commbined
255  std::vector<float> digiHcalMapTW = PrepareONNXDQMMapVectors(digiHcal2DHist_depth_all);
256 
257  std::vector<float> adThr{flagDecisionThr}; // AD decision threshold, increase to reduce sensitivity
258  std::vector<float> numEvents{LS_numEvents};
259 
260  // call model inference
261  /**************** Inference ******************/
262  std::vector<std::vector<float>> output_tensors = Inference(digiHcalMapTW,
263  numEvents,
264  adThr,
265  input_model_state_memory_e_0_0,
266  input_model_state_memory_e_0_1,
267  input_model_state_memory_e_1_0,
268  input_model_state_memory_e_1_1,
269  input_model_state_memory_d_0_0,
270  input_model_state_memory_d_0_1,
271  input_model_state_memory_d_1_0,
272  input_model_state_memory_d_1_1);
273 
274  // auto output_tensors = Inference(digiHcalMapTW, numEvents, adThr);
275  //std::cout << "******* model inference is success *******" << std::endl;
276 
277  /**************** Output post processing ******************/
278  // split outputs into ad output vectors and state_memory vectors
279  std::string state_output_name_tag = "rnn_hidden";
280  std::vector<std::vector<float>> ad_model_output_vectors, ad_model_state_vectors;
281  for (size_t i = 0; i < output_tensors.size(); i++) {
282  std::string output_names_startstr = output_names[i].substr(
283  2, state_output_name_tag.length()); // Extract the same number of characters as str2 from mOutputNames
284  if (output_names_startstr == state_output_name_tag) {
285  ad_model_state_vectors.emplace_back(output_tensors[i]);
286  } else {
287  ad_model_output_vectors.emplace_back(output_tensors[i]);
288  }
289  }
290 
291  if (ad_model_output_vectors.size() == num_state_vectors) {
292  input_model_state_memory_e_0_0 = ad_model_state_vectors[0];
293  input_model_state_memory_e_0_1 = ad_model_state_vectors[1];
294  input_model_state_memory_e_1_0 = ad_model_state_vectors[2];
295  input_model_state_memory_e_1_1 = ad_model_state_vectors[3];
296  input_model_state_memory_d_0_0 = ad_model_state_vectors[4];
297  input_model_state_memory_d_0_1 = ad_model_state_vectors[5];
298  input_model_state_memory_d_1_0 = ad_model_state_vectors[6];
299  input_model_state_memory_d_1_1 = ad_model_state_vectors[7];
300  } else {
301  std::cout << "Warning: the number of output state vectors does NOT equals to expected!. The states are set to "
302  "default values."
303  << std::endl;
304  InitializeState();
305  }
306 
307  // # if onnx is returning serialized 1d vectors instead of vector of 3d vectors
308  // 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]
309  /*
310  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)
311  std::vector<std::vector<std::vector<float>>> digiHcal3DHist_ANOMALY_FLAG = ONNXOutputToDQMHistMap(ad_model_output_vectors, 7)
312  std::vector<std::vector<std::vector<float>>> digiHcal3DHist_ANOMALY_SCORE = ONNXOutputToDQMHistMap(ad_model_output_vectors, 5)
313  */
314 
315  // reduce counter for each ls call. due to onnx double datatype handling limitation that might cause precision error to propagate.
316  if (--model_state_refresh_counter == 0)
317  InitializeState();
318 
319  return ad_model_output_vectors;
320 }
std::vector< std::vector< std::vector< float > > > ONNXOutputToDQMHistMap(const std::vector< std::vector< float >> &ad_model_output_vectors, const int selOutputIdx=7)
Converts 1D serialized vector output of the onnx into 3d hcal-hehp vector.
::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
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
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