DaNNet
dnn_layer_conv.h
Go to the documentation of this file.
1 // Copyright 2019 Claes Rolen (www.rolensystems.com)
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 namespace dnn
17 {
21 
31 class layer_conv: public layer
32 {
33 private:
34  arma::uword N_filter_size;
35  arma::uword N_pad;
36  arma::uword N_stride;
37 
38  arma::Cube<DNN_Dtype> C;
39  arma::Mat<DNN_Dtype> Cbp;
40 
41 public:
49  layer_conv(const arma::uword n_channels,
50  const arma::uword n_kernel,
51  const arma::uword n_pad = 0,
52  const arma::uword n_stride = 1 ):layer()
53  {
54  N_channels_right = n_channels;
55  N_filter_size = n_kernel;
56  N_pad = n_pad;
57  N_stride = n_stride;
58  type = "Conv";
59  id = type;
60  }
61 
66  void init(void)
67  {
68  layer::init();
69 
70  N_cols_right = (N_cols_left-N_filter_size+2*N_pad)/N_stride + 1;
71  N_rows_right = (N_rows_left-N_filter_size+2*N_pad)/N_stride + 1;
73  id = type+"("+std::to_string(N_filter_size)+"x"+std::to_string(N_filter_size)+","+
74  std::to_string(N_channels_left)+"*"+std::to_string(N_channels_right)+")";
75  train_par = true;
76 
77  W. set_size(N_filter_size,N_filter_size,N_channels_left*N_channels_right);
78  B. set_size(N_channels_right,1);
79  Cbp. set_size(N_filter_size*N_filter_size*N_channels_right,N_rows_left*N_cols_left);
80 
81  init_weights(W,N_filter_size*N_filter_size*N_channels_left,
82  N_filter_size*N_filter_size*N_channels_right,
84  B.zeros();
85  }
86 
93  void upd_buf_size(arma::uword nmb)
94  {
96  C.zeros(N_filter_size*N_filter_size*N_channels_left,N_rows_right*N_cols_right,nmb);
97  }
98 
106  arma::Mat<DNN_Dtype> rotate_180(const arma::Mat<DNN_Dtype>& M)
107  {
108  arma::Mat<DNN_Dtype> U(M);
109  return arma::fliplr(arma::flipud(U));
110  }
111 
119  arma::Cube<DNN_Dtype> rotate_180(const arma::Cube<DNN_Dtype>& C)
120  {
121  arma::Cube<DNN_Dtype> U(C);
122  for (arma::uword n=0; n<C.n_slices; n++)
123  {
124  U.slice(n) = arma::fliplr(arma::flipud(C.slice(n)));
125  }
126  return U;
127  }
128 
140  template <typename DNN_Dtype>
141  void im2col_nzp( DNN_Dtype* Dptr, const DNN_Dtype* Xptr,
142  const arma::uword XRows, const arma::uword XCols, const arma::uword XDepth,
143  const arma::uword KRows, const arma::uword KCols,
144  const arma::uword SRows, const arma::uword SCols)
145  {
146  const arma::uword XSize = XRows*XCols;
147  const arma::uword XC = XCols-KCols;
148  const arma::uword XR = XRows-KRows;
149  for (arma::uword x_col=0; x_col<=XC; x_col+=SCols) // image cols
150  {
151  for(arma::uword x_row=0; x_row<=XR; x_row+=SRows ) // image rows
152  {
153  for (arma::uword layer=0; layer<XDepth; layer++) // Depth (input layers/featuremaps)
154  {
155  const arma::uword lay_offs = layer*XSize;
156  for(arma::uword k_col=0; k_col<KCols; k_col++ ) // kernel cols
157  {
158  const arma::uword col_offs = x_col+k_col;
159  const arma::uword l_c_offs = lay_offs+col_offs*XRows;
160  for(arma::uword k_row=0; k_row<KRows; k_row++ ) // kernel rows
161  {
162  *(Dptr++)=Xptr[l_c_offs+ x_row + k_row];
163  }
164  }
165  }
166  }
167  }
168  }
169 
182  template <typename DNN_Dtype>
183  void im2col(DNN_Dtype* Dptr, const DNN_Dtype* Xptr,
184  const arma::uword XRows, const arma::uword XCols, const arma::uword XDepth,
185  const arma::uword KRows, const arma::uword KCols,
186  const arma::uword PRows, const arma::uword PCols,
187  const arma::uword SRows, const arma::uword SCols)
188  {
189  const arma::uword XSize = XRows * XCols;
190  const arma::sword XC = XCols - KCols + PCols;
191  const arma::sword XR = XRows - KRows + PRows;
192  for (arma::sword x_col = -1*PCols; x_col <= XC; x_col += SCols) // image cols
193  {
194  for (arma::sword x_row = -1*PRows; x_row <= XR; x_row += SRows) // image rows
195  {
196  for (arma::uword layer = 0; layer < XDepth; layer++) // Depth (input layers/featuremaps)
197  {
198  const arma::uword lay_offs = layer * XSize;
199  for (arma::uword k_col = 0; k_col < KCols; k_col++) // kernel cols
200  {
201  const arma::sword col_offs = x_col + k_col;
202  const bool c_ok = static_cast<unsigned int>(col_offs) < XCols;
203  if(c_ok)
204  {
205  const arma::uword l_c_offs = lay_offs + col_offs * XRows;
206  for (arma::uword k_row = 0; k_row < KRows; k_row++) // kernel rows
207  {
208  const arma::sword row_offs = x_row + k_row;
209  const bool r_ok = static_cast<unsigned int>(row_offs) < XRows;
210  if(r_ok)
211  {
212  *(Dptr++) = Xptr[l_c_offs + row_offs];
213  }
214  else
215  {
216  *(Dptr++) = 0;
217  }
218  }
219  }
220  else
221  {
222  for (arma::uword k_row = 0; k_row < KRows; k_row++) // kernel rows
223  {
224  *(Dptr++) = 0;
225  }
226  }
227  }
228  }
229  }
230  }
231  }
232 
245  template <typename DNN_Dtype>
246  void im2row(DNN_Dtype* Dptr, const DNN_Dtype* Xptr,
247  const unsigned int XRows, const unsigned int XCols, const unsigned int XDepth,
248  const unsigned int KRows, const unsigned int KCols,
249  const unsigned int PRows, const unsigned int PCols,
250  const unsigned int SRows, const unsigned int SCols)
251  {
252  const unsigned int XSize = XRows*XCols;
253  const int XC = XCols-KCols+PCols;
254  const int XR = XRows-KRows+PRows;
255  for (unsigned int layer=0; layer<XDepth; layer++) // Depth (input layers/featuremaps)
256  {
257  const unsigned int lay_offs = layer*XSize;
258  for(unsigned int k_col=0; k_col<KCols; k_col++ ) // kernel cols
259  {
260  for(unsigned int k_row=0; k_row<KRows; k_row++ ) // kernel rows
261  {
262  for (int x_col=-PCols; x_col<=XC; x_col+=SCols) // image cols
263  {
264  const int col_offs = x_col+k_col;
265  const int l_c_offs = lay_offs+col_offs*XRows;
266  for(int x_row=-PRows; x_row<=XR; x_row+=SRows ) // image rows
267  {
268  const int row_offs = x_row+k_row;
269  if((row_offs) >= 0 && static_cast<unsigned int>(row_offs) < XRows &&
270  (col_offs) >= 0 && static_cast<unsigned int>(col_offs) < XCols)
271  {
272  *(Dptr++)=Xptr[l_c_offs+row_offs];
273  }
274  else
275  {
276  *(Dptr++)=0;
277  }
278  }
279  }
280  }
281  }
282  }
283  }
284 
288  void disp(void)
289  {
290  layer::disp();
291  std::cout << "Opt algorithm: " << opt_alg->get_algorithm() << std::endl;
292  std::cout << "Input size: ["<< N_rows_left << "," << N_rows_left << "," << N_channels_left << "]" << std::endl;
293  std::cout << "Filter size: [" << N_filter_size << "," << N_filter_size << "," << N_channels_left*N_channels_right << "]" << std::endl;
294  std::cout << "Padding: " << N_pad << std::endl;
295  std::cout << "Stride: " << N_stride << std::endl;
296  std::cout << "Output size: ["<< N_rows_right << "," << N_rows_right << "," << N_channels_right << "]" << std::endl;
297  }
298 
303  arma::uword get_nrof_params(void)
304  {
305  return N_filter_size*N_filter_size*N_channels_left*N_channels_right+N_channels_right;
306  }
307 
313  void prop(void)
314  {
315  arma::Mat<DNN_Dtype> gg(W.memptr(), N_filter_size*N_filter_size*N_channels_left, N_channels_right, false, true);
316  arma::Mat<DNN_Dtype> yy(N_rows_right*N_cols_right, N_channels_right); // [RoCo,KKLi][KKLi,Lo]
317  arma::Mat<DNN_Dtype> Bmat = arma::repmat(B.t(), N_rows_right*N_cols_right, 1);
318 
319  if (N_pad)
320  {
322  }
323  else
324  {
326  }
327  yy = ((C.slice(0)).t())*gg; // [RoCo,KKLi][KKLi,Lo]
328  yy += Bmat;
329  Y1 = arma::vectorise(yy);
330  }
331 
337  void prop_mb(void)
338  {
339  arma::Mat<DNN_Dtype> gg(W.memptr(),N_filter_size*N_filter_size*N_channels_left,N_channels_right,false,true);
340  arma::Mat<DNN_Dtype> yy(N_rows_right*N_cols_right,N_channels_right); // [RoCo,KKLi][KKLi,Lo]
341  arma::Mat<DNN_Dtype> Bmat = arma::repmat(B.t(),N_rows_right*N_cols_right,1);
342  for (arma::uword n=0; n<N_batch; n++) // loop over every image in minibatch
343  {
344  if (N_pad)
345  {
347  }
348  else
349  {
351  }
352  yy=((C.slice(n)).t())*gg; // [RoCo,KKLi][KKLi,Lo]
353  yy += Bmat;
354  Y.col(n) = arma::vectorise(yy);
355  }
356  }
357 
363  void backprop(void)
364  {
365  arma::Cube<DNN_Dtype> uu = rotate_180(W);
366  arma::Mat<DNN_Dtype> gg(uu.memptr(),N_filter_size*N_filter_size*N_channels_right,N_channels_left,false,true); // [KKLo,Li]
367  arma::Mat<DNN_Dtype> ii(N_rows_left*N_cols_left,N_channels_left); // [RiCi,Li]
368 
369  DNN_Dtype* C_ptr = Cbp.memptr();
370 
371  for (arma::uword n=0; n<N_batch; n++) // loop over every image in minibatch
372  {
373  im2col(C_ptr, right->get_Dleft_colptr(n), N_rows_right, N_cols_right, N_channels_right, N_filter_size, N_filter_size, N_filter_size - 1, N_filter_size - 1, N_stride, N_stride);
374  ii = Cbp.t()*gg; // [KKLo,RiCi]'[KKLo,Li]
375  Dleft.col(n) = arma::vectorise(ii);
376  }
377  }
378 
384  void update(void)
385  {
386  if(train_par==true)
387  {
388  arma::Mat<DNN_Dtype> dG(N_filter_size*N_filter_size*N_channels_left,N_channels_right, arma::fill::zeros);
389  const DNN_Dtype nrm = (DNN_Dtype)1.0/N_batch;
390  arma::Mat<DNN_Dtype> dB(1,N_channels_right, arma::fill::zeros);
391 
392  for (arma::uword n=0; n<N_batch; n++) // loop over every image in minibatch
393  {
394  arma::Mat<DNN_Dtype> d_o(right->get_Dleft_colptr(n),N_rows_right*N_cols_right,N_channels_right); // [RoCo,Lo]
395  dG += C.slice(n) * d_o; // [KKLi,RoCo][RoCo,Lo]
396  if(add_bias) dB += arma::sum(d_o);
397  }
398  dG *=nrm;
399  if(add_bias) dB *=nrm;
400 
401  arma::Cube<DNN_Dtype> g(W.memptr(),W.n_elem,1,1,false,true);
402  arma::Cube<DNN_Dtype> dg(dG.memptr(),dG.n_elem,1,1,false,true);
403 
404  if(add_bias)
405  {
406  opt_alg->apply(g,B,dg,dB.t());
407  }
408  else
409  {
410  arma::Mat<DNN_Dtype> m={1.0};
411  opt_alg->apply(g,m,dg,m);
412  }
413  }
414  }
415 
423  arma::Mat<DNN_Dtype> weights2img(arma::uword IR)
424  {
425  arma::uword R = W.n_rows;
426  arma::uword C = W.n_cols;
427  arma::uword M = W.n_slices;
428  arma::uword IC = static_cast<arma::uword>(ceil(M/(1.0*IR)));
429  arma::Mat<DNN_Dtype> II(IR*R,IC*C,arma::fill::zeros);
430  arma::uword r=0,c=0;
431  for(arma::uword m=0; m<M; m++)
432  {
433  arma::Mat<DNN_Dtype> I = W.slice(m);
434  I+=B(m);
435  II(r,c,arma::size(R,C)) = I;
436  c+=C;
437  if(c >= IC*C)
438  {
439  r+=R;
440  c=0;
441  }
442  }
443  return II;
444  }
445 
446  #ifdef SIGPACK_H
447  void plotWeights(sp::gplot& gp, const arma::uword PR)
448  {
449  std::ostringstream tmp_s;
450  arma::uword M = W.n_slices;
451  arma::uword PC = ceil(M/(1.0*PR));
452 
453  gp.send2gp("unset key");
454  gp.send2gp("set format xy \"\" ");
455  DNN_Dtype p=arma::max(arma::abs(arma::vectorise(W)));
456  tmp_s << "set cbrange [ " << -p << ":" << p << "]";
457  gp.send2gp(tmp_s.str().c_str());
458  tmp_s.str(""); // Clear buffer
459  if(M>4)
460  {
461  gp.send2gp("unset colorbox");
462  tmp_s << "set multiplot layout " << PR << "," << PC << " margins 0.01,0.99,0.01,0.99 spacing 0.01,0.01";
463  }
464  else
465  {
466  tmp_s << "set multiplot layout " << PR << "," << PC;
467  }
468  gp.send2gp(tmp_s.str().c_str());
469 
470  for(arma::uword m=0; m<M; m++)
471  {
472  arma::Mat<DNN_Dtype> Im = W.slice(m)+B(floor(m / N_channels_left));
473  gp.image(arma::conv_to<arma::mat>::from(Im));
474  }
475  gp.send2gp("unset multiplot");
476  gp.flush_buf();
477  }
478  #endif // SIGPACK_H
479 
480 }; // End class layer_conv
482 } // End namespace dnn
opt * opt_alg
Pointer to optimizer.
std::string type
Layer type string.
arma::Mat< DNN_Dtype > Y
Output buffer mini batch [N_right,N_batch].
void prop(void)
Forward propagation though layer.
arma::uword N_rows_left
Input rows.
arma::uword get_nrof_params(void)
Get info about number of trainable parameters in layer.
layer * right
Pointer to next layer.
arma::Mat< DNN_Dtype > Cbp
Input expanded by im2col.
arma::uword N_channels_right
Output channels, number of filters.
void backprop(void)
Backpropagation of mini batch propagation though layer.
arma::uword N_stride
Stride.
arma::Mat< DNN_Dtype > Dleft
Error buffer [N_left,N_batch].
Layer base class.
arma::uword N_cols_left
Input cols.
arma::Cube< DNN_Dtype > C
Input expanded by im2col.
arma::Mat< DNN_Dtype > Y1
Output buffer [N_right,1].
void init_weights(T &w, arma::uword fan_in, arma::uword fan_out, INIT_W_ALG alg, INIT_W_DIST dist)
Initiate weights.
virtual DNN_Dtype * get_Y_colptr(const arma::uword n)
Get output buffer memory column pointer - mini batch.
virtual void init(void)
Initialize layer.
arma::Mat< DNN_Dtype > B
Bias.
arma::uword N_pad
Padding.
void prop_mb(void)
Forward mini batch propagation though layer.
virtual void disp(void)
Display info about layer.
float DNN_Dtype
Data type used in the network (float or double)
Definition: dnn.h:28
void im2col(DNN_Dtype *Dptr, const DNN_Dtype *Xptr, const arma::uword XRows, const arma::uword XCols, const arma::uword XDepth, const arma::uword KRows, const arma::uword KCols, const arma::uword PRows, const arma::uword PCols, const arma::uword SRows, const arma::uword SCols)
Im2col with zero padding.
arma::uword N_rows_right
Output rows.
virtual void upd_buf_size(arma::uword nmb)
Update layer buffer sizes.
void disp(void)
Display info about layer.
virtual void apply(arma::Cube< DNN_Dtype > &W, arma::Mat< DNN_Dtype > &B, const arma::Cube< DNN_Dtype > &Wgrad, const arma::Mat< DNN_Dtype > &Bgrad)=0
Apply the optimizer to the layer parameters.
arma::Mat< DNN_Dtype > weights2img(arma::uword IR)
Generate an image of the weights.
arma::Cube< DNN_Dtype > W
Weights.
arma::uword N_batch
Mini batch size.
bool train_par
Enable training.
void upd_buf_size(arma::uword nmb)
Updates the buffer sizes.
Definition: dnn.h:22
arma::Cube< DNN_Dtype > rotate_180(const arma::Cube< DNN_Dtype > &C)
Rotates each matrix in a cube 180 degree.
arma::uword N_filter_size
Conv. filter size.
arma::Mat< DNN_Dtype > rotate_180(const arma::Mat< DNN_Dtype > &M)
Rotates a matrix 180 degree.
void im2row(DNN_Dtype *Dptr, const DNN_Dtype *Xptr, const unsigned int XRows, const unsigned int XCols, const unsigned int XDepth, const unsigned int KRows, const unsigned int KCols, const unsigned int PRows, const unsigned int PCols, const unsigned int SRows, const unsigned int SCols)
Im2row with zero padding.
arma::uword N_channels_left
Input channels, number of filters.
virtual std::string get_algorithm(void)
Get the optimizer algorithm information.
Definition: dnn_opt.h:67
layer_conv(const arma::uword n_channels, const arma::uword n_kernel, const arma::uword n_pad=0, const arma::uword n_stride=1)
Convolution layer constructor.
arma::uword N_right
Total size right.
void im2col_nzp(DNN_Dtype *Dptr, const DNN_Dtype *Xptr, const arma::uword XRows, const arma::uword XCols, const arma::uword XDepth, const arma::uword KRows, const arma::uword KCols, const arma::uword SRows, const arma::uword SCols)
Im2col without zero padding.
virtual DNN_Dtype * get_Y1_memptr(void)
Get output buffer memory pointer.
layer * left
Pointer to previous layer.
arma::uword N_cols_right
Output cols.
void init(void)
Initialization of layer.
A convolution layer class.
virtual DNN_Dtype * get_Dleft_colptr(const arma::uword n)
Get error buffer memory column pointer - mini batch.
bool add_bias
Enable bias.
void update(void)
Updates the trainable parameters.