LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
keras_model.cc
Go to the documentation of this file.
1 #include "keras_model.h"
2 
3 #include "tbb/parallel_for.h"
4 
5 #include <algorithm>
6 #include <fstream>
7 #include <math.h>
8 using namespace std;
9 
10 std::vector<float> keras::read_1d_array(std::ifstream& fin, int cols)
11 {
12  vector<float> arr;
13  float tmp_float;
14  char tmp_char;
15  fin >> tmp_char; // for '['
16  for (int n = 0; n < cols; ++n) {
17  fin >> tmp_float;
18  arr.push_back(tmp_float);
19  }
20  fin >> tmp_char; // for ']'
21  return arr;
22 }
23 
24 void keras::DataChunk2D::read_from_file(const std::string& fname)
25 {
26  ifstream fin(fname.c_str());
27  fin >> m_depth >> m_rows >> m_cols;
28 
29  for (int d = 0; d < m_depth; ++d) {
30  vector<vector<float>> tmp_single_depth;
31  for (int r = 0; r < m_rows; ++r) {
32  vector<float> tmp_row = keras::read_1d_array(fin, m_cols);
33  tmp_single_depth.push_back(tmp_row);
34  }
35  data.push_back(tmp_single_depth);
36  }
37  fin.close();
38 }
39 
41 {
42  char tmp_char = ' ';
43  string tmp_str = "";
44  float tmp_float;
45  bool skip = false;
46  fin >> m_kernels_cnt >> m_depth >> m_rows >> m_cols >> m_border_mode;
47  if (m_border_mode == "[") {
48  m_border_mode = "valid";
49  skip = true;
50  }
51 
52  cout << "LayerConv2D " << m_kernels_cnt << "x" << m_depth << "x" << m_rows << "x" << m_cols
53  << " border_mode " << m_border_mode << endl;
54  // reading kernel weights
55  for (int k = 0; k < m_kernels_cnt; ++k) {
56  vector<vector<vector<float>>> tmp_depths;
57  for (int d = 0; d < m_depth; ++d) {
58  vector<vector<float>> tmp_single_depth;
59  for (int r = 0; r < m_rows; ++r) {
60  if (!skip) { fin >> tmp_char; } // for '['
61  else {
62  skip = false;
63  }
64  vector<float> tmp_row;
65  for (int c = 0; c < m_cols; ++c) {
66  fin >> tmp_float;
67  //cout << tmp_float << " ";
68  tmp_row.push_back(tmp_float);
69  }
70  fin >> tmp_char; // for ']'
71  tmp_single_depth.push_back(tmp_row);
72  }
73  tmp_depths.push_back(tmp_single_depth);
74  }
75  m_kernels.push_back(tmp_depths);
76  }
77  // reading kernel biases
78  fin >> tmp_char; // for '['
79  for (int k = 0; k < m_kernels_cnt; ++k) {
80  fin >> tmp_float;
81  m_bias.push_back(tmp_float);
82  }
83  fin >> tmp_char; // for ']'
84 }
85 
87 {
88  fin >> m_activation_type;
89  cout << "Activation type " << m_activation_type << endl;
90 }
91 
93 {
94  fin >> m_pool_x >> m_pool_y;
95  cout << "MaxPooling " << m_pool_x << "x" << m_pool_y << endl;
96 }
97 
99 {
100  fin >> m_input_cnt >> m_neurons;
101  float tmp_float;
102  char tmp_char = ' ';
103  for (int i = 0; i < m_input_cnt; ++i) {
104  vector<float> tmp_n;
105  fin >> tmp_char; // for '['
106  for (int n = 0; n < m_neurons; ++n) {
107  fin >> tmp_float;
108  tmp_n.push_back(tmp_float);
109  }
110  fin >> tmp_char; // for ']'
111  m_weights.push_back(tmp_n);
112  }
113  cout << "weights " << m_weights.size() << endl;
114  fin >> tmp_char; // for '['
115  for (int n = 0; n < m_neurons; ++n) {
116  fin >> tmp_float;
117  m_bias.push_back(tmp_float);
118  }
119  fin >> tmp_char; // for ']'
120  cout << "bias " << m_bias.size() << endl;
121 }
122 
123 keras::KerasModel::KerasModel(const string& input_fname)
124 {
125  load_weights(input_fname);
126 }
127 
129 {
130  vector<vector<vector<float>>> im = dc->get_3d();
131 
132  size_t csize = im[0].size();
133  size_t rsize = im[0][0].size();
134  size_t size = im.size() * csize * rsize;
135  keras::DataChunkFlat* out = new DataChunkFlat(size);
136  float* y_ret = out->get_1d_rw().data();
137  for (size_t i = 0, dst = 0; i < im.size(); ++i) {
138  for (size_t j = 0; j < csize; ++j) {
139  float* row = im[i][j].data();
140  for (size_t k = 0; k < rsize; ++k) {
141  y_ret[dst++] = row[k];
142  }
143  }
144  }
145 
146  return out;
147 }
148 
150 {
151  vector<vector<vector<float>>> im = dc->get_3d();
152  vector<vector<vector<float>>> y_ret;
153  for (unsigned int i = 0; i < im.size(); ++i) {
154  vector<vector<float>> tmp_y;
155  for (unsigned int j = 0; j < (unsigned int)(im[0].size() / m_pool_x); ++j) {
156  tmp_y.push_back(vector<float>((int)(im[0][0].size() / m_pool_y), 0.0));
157  }
158  y_ret.push_back(tmp_y);
159  }
160  for (unsigned int d = 0; d < y_ret.size(); ++d) {
161  for (unsigned int x = 0; x < y_ret[0].size(); ++x) {
162  unsigned int start_x = x * m_pool_x;
163  unsigned int end_x = start_x + m_pool_x;
164  for (unsigned int y = 0; y < y_ret[0][0].size(); ++y) {
165  unsigned int start_y = y * m_pool_y;
166  unsigned int end_y = start_y + m_pool_y;
167 
168  vector<float> values;
169  for (unsigned int i = start_x; i < end_x; ++i) {
170  for (unsigned int j = start_y; j < end_y; ++j) {
171  values.push_back(im[d][i][j]);
172  }
173  }
174  y_ret[d][x][y] = *max_element(values.begin(), values.end());
175  }
176  }
177  }
179  out->set_data(y_ret);
180  return out;
181 }
182 
183 void keras::missing_activation_impl(const string& act)
184 {
185  cout << "Activation " << act << " not defined!" << endl;
186  cout << "Please add its implementation before use." << endl;
187  exit(1);
188 }
189 
191 {
192 
193  if (dc->get_data_dim() == 3) {
194  vector<vector<vector<float>>> y = dc->get_3d();
195  if (m_activation_type == "relu") {
196  for (unsigned int i = 0; i < y.size(); ++i) {
197  for (unsigned int j = 0; j < y[0].size(); ++j) {
198  for (unsigned int k = 0; k < y[0][0].size(); ++k) {
199  if (y[i][j][k] < 0) y[i][j][k] = 0;
200  }
201  }
202  }
203  }
204  else if (m_activation_type == "tanh") {
205  for (unsigned int i = 0; i < y.size(); ++i) {
206  for (unsigned int j = 0; j < y[0].size(); ++j) {
207  for (unsigned int k = 0; k < y[0][0].size(); ++k) {
208  y[i][j][k] = tanh(y[i][j][k]);
209  }
210  }
211  }
212  }
213  else {
214  keras::missing_activation_impl(m_activation_type);
215  }
216 
218  out->set_data(y);
219  return out;
220  }
221  else if (dc->get_data_dim() == 1) { // flat data, use 1D
222  vector<float> y = dc->get_1d();
223  if (m_activation_type == "relu") {
224  for (unsigned int k = 0; k < y.size(); ++k) {
225  if (y[k] < 0) y[k] = 0;
226  }
227  }
228  else if (m_activation_type == "softmax") {
229  float sum = 0.0;
230  for (unsigned int k = 0; k < y.size(); ++k) {
231  y[k] = exp(y[k]);
232  sum += y[k];
233  }
234  for (unsigned int k = 0; k < y.size(); ++k) {
235  y[k] /= sum;
236  }
237  }
238  else if (m_activation_type == "tanh") {
239  for (unsigned int k = 0; k < y.size(); ++k) {
240  y[k] = tanh(y[k]);
241  }
242  }
243  else if (m_activation_type == "sigmoid") {
244  for (unsigned int k = 0; k < y.size(); ++k) {
245  y[k] = 1.0F / (1.0F + exp(-y[k]));
246  }
247  }
248  else {
249  keras::missing_activation_impl(m_activation_type);
250  }
251 
252  keras::DataChunk* out = new DataChunkFlat();
253  out->set_data(y);
254  return out;
255  }
256  else {
257  throw "data dim not supported";
258  }
259 
260  return dc;
261 }
262 
263 // with border mode = valid
264 void keras::conv_single_depth_valid(std::vector<std::vector<float>>& y, // accumulate here
265  std::vector<std::vector<float>> const& im,
266  std::vector<std::vector<float>> const& k)
267 {
268  size_t k1_size = k.size(), k2_size = k[0].size();
269  unsigned int st_x = (k1_size - 1) >> 1;
270  unsigned int st_y = (k2_size - 1) >> 1;
271 
272  for (unsigned int i = st_x; i < im.size() - st_x; ++i) {
273  for (unsigned int j = st_y; j < im[0].size() - st_y; ++j) {
274 
275  float sum = 0;
276  for (unsigned int k1 = 0; k1 < k1_size; ++k1) {
277  //const float * k_data = k[k1_size-k1-1].data();
278  //const float * im_data = im[i-st_x+k1].data();
279  for (unsigned int k2 = 0; k2 < k2_size; ++k2) {
280  //sum += k_data[k2_size-k2-1] * im_data[j-st_y+k2];
281  sum += k[k1_size - k1 - 1][k2_size - k2 - 1] * im[i - st_x + k1][j - st_y + k2];
282  }
283  }
284  y[i - st_x][j - st_y] += sum;
285  }
286  }
287 }
288 
289 // with border mode = same
290 void keras::conv_single_depth_same(std::vector<std::vector<float>>& y, // accumulate here
291  std::vector<std::vector<float>> const& im,
292  std::vector<std::vector<float>> const& k)
293 {
294  size_t k1_size = k.size(), k2_size = k[0].size();
295  unsigned int st_x = (k1_size - 1) >> 1;
296  unsigned int st_y = (k2_size - 1) >> 1;
297 
298  size_t max_imc = im.size() - 1;
299  size_t max_imr = im[0].size() - 1;
300 
301  for (unsigned int i = 0; i < im.size(); ++i) {
302  for (unsigned int j = 0; j < im[0].size(); ++j) {
303 
304  float sum = 0;
305  for (unsigned int k1 = 0; k1 < k1_size; ++k1) {
306  //const float * k_data = k[k1_size-k1-1].data();
307  //const float * im_data = im[i-st_x+k1].data();
308  for (unsigned int k2 = 0; k2 < k2_size; ++k2) {
309  if (i + k1 < st_x) continue;
310  if (i + k1 > max_imc + st_x) continue;
311  if (j + k2 < st_y) continue;
312  if (j + k2 > max_imr + st_y) continue;
313 
314  //sum += k_data[k2_size-k2-1] * im_data[j-st_y+k2];
315  sum += k[k1_size - k1 - 1][k2_size - k2 - 1] * im[i - st_x + k1][j - st_y + k2];
316  }
317  }
318  y[i][j] += sum;
319  }
320  }
321 }
322 
324 {
325  unsigned int st_x = (m_kernels[0][0].size() - 1) >> 1;
326  unsigned int st_y = (m_kernels[0][0][0].size() - 1) >> 1;
327  auto const& im = dc->get_3d();
328 
329  size_t size_x = (m_border_mode == "valid") ? im[0].size() - 2 * st_x : im[0].size();
330  size_t size_y = (m_border_mode == "valid") ? im[0][0].size() - 2 * st_y : im[0][0].size();
331 
332  // depth rows cols
333  keras::DataChunk2D* out = new keras::DataChunk2D(m_kernels.size(), size_x, size_y, 0);
334  auto& y_ret = out->get_3d_rw();
335 
336  //auto t1 = std::chrono::high_resolution_clock::now();
337 
338  // Parallelize the kernal calculation
339  tbb::parallel_for(size_t(0), size_t(m_kernels.size()), [&](size_t j) {
340  for (unsigned int m = 0; m < im.size(); ++m) { // loop over image depth
341  if (m_border_mode == "valid")
342  keras::conv_single_depth_valid(y_ret[j], im[m], m_kernels[j][m]);
343  else
344  keras::conv_single_depth_same(y_ret[j], im[m], m_kernels[j][m]);
345  }
346 
347  for (unsigned int x = 0; x < y_ret[0].size(); ++x) {
348 
349  size_t size = y_ret[0][0].size();
350  float bias = m_bias[j];
351  size_t k = 0;
352 
353  for (unsigned int y = 0; y < size / 8; ++y) {
354  y_ret[j][x][k] += bias;
355  y_ret[j][x][k + 1] += bias;
356  y_ret[j][x][k + 2] += bias;
357  y_ret[j][x][k + 3] += bias;
358  y_ret[j][x][k + 4] += bias;
359  y_ret[j][x][k + 5] += bias;
360  y_ret[j][x][k + 6] += bias;
361  y_ret[j][x][k + 7] += bias;
362  k += 8;
363  }
364  while (k < size) {
365  y_ret[j][x][k] += bias;
366  ++k;
367  }
368  }
369  });
370 
371  //auto t2 = std::chrono::high_resolution_clock::now();
372  /*
373  cout << "Parallal : "
374  << std::chrono::duration_cast<std::chrono::microseconds>(t2-t1).count()
375  << " microseconds." << endl;
376  */
377 
378  return out;
379 }
380 
382 {
383  //cout << "weights: input size " << m_weights.size() << endl;
384  //cout << "weights: neurons size " << m_weights[0].size() << endl;
385  //cout << "bias " << m_bias.size() << endl;
386  size_t size = m_weights[0].size();
387  size_t size8 = size >> 3;
388 
389  keras::DataChunkFlat* out = new DataChunkFlat(size, 0);
390  float* y_ret = out->get_1d_rw().data();
391 
392  auto const& im = dc->get_1d();
393 
394  for (size_t j = 0; j < m_weights.size(); ++j) { // iter over input
395  const float* w = m_weights[j].data();
396  float p = im[j];
397  size_t k = 0;
398  for (size_t i = 0; i < size8; ++i) { // iter over neurons
399  y_ret[k] += w[k] * p; // vectorize if you can
400  y_ret[k + 1] += w[k + 1] * p;
401  y_ret[k + 2] += w[k + 2] * p;
402  y_ret[k + 3] += w[k + 3] * p;
403  y_ret[k + 4] += w[k + 4] * p;
404  y_ret[k + 5] += w[k + 5] * p;
405  y_ret[k + 6] += w[k + 6] * p;
406  y_ret[k + 7] += w[k + 7] * p;
407  k += 8;
408  }
409  while (k < size) {
410  y_ret[k] += w[k] * p;
411  ++k;
412  }
413  }
414  for (size_t i = 0; i < size; ++i) { // add biases
415  y_ret[i] += m_bias[i];
416  }
417 
418  return out;
419 }
420 
422 {
423  //cout << endl << "KerasModel compute output" << endl;
424  //cout << "Input data size:" << endl;
425  //dc->show_name();
426 
427  keras::DataChunk* inp = dc;
428  keras::DataChunk* out = 0;
429  for (int l = 0; l < (int)m_layers.size(); ++l) {
430  //cout << "Processing layer " << m_layers[l]->get_name() << endl;
431  out = m_layers[l]->compute_output(inp);
432 
433  //cout << "Input" << endl;
434  //inp->show_name();
435  //cout << "Output" << endl;
436  //out->show_name();
437 
438  if (inp != dc) delete inp;
439  inp = 0L;
440  inp = out;
441  }
442 
443  //cout << "Output: ";
444  //out->show_values();
445 
446  std::vector<float> flat_out = out->get_1d();
447  delete out;
448 
449  return flat_out;
450 }
451 
452 void keras::KerasModel::load_weights(const string& input_fname)
453 {
454  cout << "Reading model from " << input_fname << endl;
455  ifstream fin(input_fname.c_str());
456  string layer_type = "";
457  string tmp_str = "";
458  int tmp_int = 0;
459 
460  fin >> tmp_str >> m_layers_cnt;
461  cout << "Layers " << m_layers_cnt << endl;
462 
463  for (int layer = 0; layer < m_layers_cnt; ++layer) { // iterate over layers
464  fin >> tmp_str >> tmp_int >> layer_type;
465  cout << "Layer " << tmp_int << " " << layer_type << endl;
466 
467  Layer* l = 0L;
468  if (layer_type == "Convolution2D") { l = new LayerConv2D(); }
469  else if (layer_type == "Activation") {
470  l = new LayerActivation();
471  }
472  else if (layer_type == "MaxPooling2D") {
473  l = new LayerMaxPooling();
474  }
475  else if (layer_type == "Flatten") {
476  l = new LayerFlatten();
477  }
478  else if (layer_type == "Dense") {
479  l = new LayerDense();
480  }
481  else if (layer_type == "Dropout") {
482  continue; // we dont need dropout layer in prediciton mode
483  }
484  if (l == 0L) {
485  cout << "Layer is empty, maybe it is not defined? Cannot define network." << endl;
486  return;
487  }
488  l->load_weights(fin);
489  m_layers.push_back(l);
490  }
491 
492  fin.close();
493 }
494 
496 {
497  for (int i = 0; i < (int)m_layers.size(); ++i) {
498  delete m_layers[i];
499  }
500 }
501 
503 {
504  int i = m_layers.size() - 1;
505  while ((i > 0) && (m_layers[i]->get_output_units() == 0))
506  --i;
507  return m_layers[i]->get_output_units();
508 }
Float_t x
Definition: compare.C:6
TRandom r
Definition: spectrum.C:23
TString fin
Definition: Style.C:24
void load_weights(std::ifstream &fin)
Definition: keras_model.cc:40
KerasModel(const std::string &input_fname)
Definition: keras_model.cc:123
keras::DataChunk * compute_output(keras::DataChunk *)
Definition: keras_model.cc:323
Float_t y
Definition: compare.C:6
virtual void set_data(std::vector< std::vector< std::vector< float >>> const &)
Definition: keras_model.h:53
int get_output_length() const
Definition: keras_model.cc:502
STL namespace.
void load_weights(const std::string &input_fname)
Definition: keras_model.cc:452
virtual void load_weights(std::ifstream &fin)=0
virtual std::vector< std::vector< std::vector< float > > > const & get_3d() const
Definition: keras_model.h:49
virtual size_t get_data_dim(void) const
Definition: keras_model.h:47
keras::DataChunk * compute_output(keras::DataChunk *)
Definition: keras_model.cc:381
keras::DataChunk * compute_output(keras::DataChunk *)
Definition: keras_model.cc:128
std::vector< float > compute_output(keras::DataChunk *dc)
Definition: keras_model.cc:421
void conv_single_depth_same(std::vector< std::vector< float >> &y, std::vector< std::vector< float >> const &im, std::vector< std::vector< float >> const &k)
Definition: keras_model.cc:290
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:289
void conv_single_depth_valid(std::vector< std::vector< float >> &y, std::vector< std::vector< float >> const &im, std::vector< std::vector< float >> const &k)
Definition: keras_model.cc:264
void read_from_file(const std::string &fname)
Definition: keras_model.cc:24
decltype(auto) values(Coll &&coll)
Range-for loop helper iterating across the values of the specified collection.
Float_t d
Definition: plot.C:235
keras::DataChunk * compute_output(keras::DataChunk *)
Definition: keras_model.cc:190
void missing_activation_impl(const std::string &act)
Definition: keras_model.cc:183
void load_weights(std::ifstream &fin)
Definition: keras_model.cc:98
keras::DataChunk * compute_output(keras::DataChunk *)
Definition: keras_model.cc:149
std::vector< std::vector< std::vector< float > > > & get_3d_rw()
Definition: keras_model.h:68
std::vector< float > & get_1d_rw()
Definition: keras_model.h:111
Char_t n[5]
Double_t sum
Definition: plot.C:31
void load_weights(std::ifstream &fin)
Definition: keras_model.cc:86
void load_weights(std::ifstream &fin)
Definition: keras_model.cc:92
Float_t w
Definition: plot.C:20
virtual std::vector< float > const & get_1d() const
Definition: keras_model.h:48
std::vector< float > read_1d_array(std::ifstream &fin, int cols)
Definition: keras_model.cc:10