MiniDNN
MaxPooling.h
1 #ifndef LAYER_MAXPOOLING_H_
2 #define LAYER_MAXPOOLING_H_
3 
4 #include <Eigen/Core>
5 #include <vector>
6 #include <stdexcept>
7 #include "../Config.h"
8 #include "../Layer.h"
9 #include "../Utils/FindMax.h"
10 
11 namespace MiniDNN {
12 
13 
21 template <typename Activation>
22 class MaxPooling: public Layer
23 {
24 private:
25  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
26  typedef Eigen::MatrixXi IntMatrix;
27 
28  const int m_channel_rows;
29  const int m_channel_cols;
30  const int m_in_channels;
31  const int m_pool_rows;
32  const int m_pool_cols;
33 
34  const int m_out_rows;
35  const int m_out_cols;
36 
37  IntMatrix m_loc; // Record the locations of maximums
38  Matrix m_z; // Max pooling results
39  Matrix m_a; // Output of this layer, a = act(z)
40  Matrix m_din; // Derivative of the input of this layer.
41  // Note that input of this layer is also the output of previous layer
42 
43 public:
44  // Currently we only implement the "valid" rule
45  // https://stackoverflow.com/q/37674306
55  MaxPooling(const int in_width, const int in_height, const int in_channels,
56  const int pooling_width, const int pooling_height) :
57  Layer(in_width * in_height * in_channels,
58  (in_width / pooling_width) * (in_height / pooling_height) * in_channels),
59  m_channel_rows(in_height), m_channel_cols(in_width), m_in_channels(in_channels),
60  m_pool_rows(pooling_height), m_pool_cols(pooling_width),
61  m_out_rows(m_channel_rows / m_pool_rows), m_out_cols(m_channel_cols / m_pool_cols)
62  {}
63 
64  void init(const Scalar& mu, const Scalar& sigma, RNG& rng) {}
65 
66  void forward(const Matrix& prev_layer_data)
67  {
68  // Each column is an observation
69  const int nobs = prev_layer_data.cols();
70  m_loc.resize(this->m_out_size, nobs);
71  m_z.resize(this->m_out_size, nobs);
72 
73  // Use m_loc to store the address of each pooling block relative to the beginning of the data
74  int* loc_data = m_loc.data();
75  const int channel_end = prev_layer_data.size();
76  const int channel_stride = m_channel_rows * m_channel_cols;
77  const int col_end_gap = m_channel_rows * m_pool_cols * m_out_cols;
78  const int col_stride = m_channel_rows * m_pool_cols;
79  const int row_end_gap = m_out_rows * m_pool_rows;
80  for(int channel_start = 0; channel_start < channel_end; channel_start += channel_stride)
81  {
82  const int col_end = channel_start + col_end_gap;
83  for(int col_start = channel_start; col_start < col_end; col_start += col_stride)
84  {
85  const int row_end = col_start + row_end_gap;
86  for(int row_start = col_start; row_start < row_end; row_start += m_pool_rows, loc_data++)
87  *loc_data = row_start;
88  }
89  }
90 
91  // Find the location of the max value in each block
92  loc_data = m_loc.data();
93  const int* const loc_end = loc_data + m_loc.size();
94  Scalar* z_data = m_z.data();
95  const Scalar* src = prev_layer_data.data();
96  for(; loc_data < loc_end; loc_data++, z_data++)
97  {
98  const int offset = *loc_data;
99  *z_data = internal::find_block_max(src + offset, m_pool_rows, m_pool_cols, m_channel_rows, *loc_data);
100  *loc_data += offset;
101  }
102 
103  // Apply activation function
104  m_a.resize(this->m_out_size, nobs);
105  Activation::activate(m_z, m_a);
106  }
107 
108  const Matrix& output() const { return m_a; }
109 
110  // prev_layer_data: in_size x nobs
111  // next_layer_data: out_size x nobs
112  void backprop(const Matrix& prev_layer_data, const Matrix& next_layer_data)
113  {
114  const int nobs = prev_layer_data.cols();
115 
116  // After forward stage, m_z contains z = max_pooling(in)
117  // Now we need to calculate d(L) / d(z) = [d(a) / d(z)] * [d(L) / d(a)]
118  // d(L) / d(z) is computed in the next layer, contained in next_layer_data
119  // The Jacobian matrix J = d(a) / d(z) is determined by the activation function
120  Matrix& dLz = m_z;
121  Activation::apply_jacobian(m_z, m_a, next_layer_data, dLz);
122 
123  // d(L) / d(in_i) = sum_j{ [d(z_j) / d(in_i)] * [d(L) / d(z_j)] }
124  // d(z_j) / d(in_i) = 1 if in_i is used to compute z_j and is the maximum
125  // = 0 otherwise
126  m_din.resize(this->m_in_size, nobs);
127  m_din.setZero();
128  const int dLz_size = dLz.size();
129 
130  const Scalar* dLz_data = dLz.data();
131  const int* loc_data = m_loc.data();
132  Scalar* din_data = m_din.data();
133  for(int i = 0; i < dLz_size; i++)
134  din_data[loc_data[i]] += dLz_data[i];
135  }
136 
137  const Matrix& backprop_data() const { return m_din; }
138 
139  void update(Optimizer& opt) {}
140 
141  std::vector<Scalar> get_parameters() const { return std::vector<Scalar>(); }
142 
143  void set_parameters(const std::vector<Scalar>& param) {}
144 
145  std::vector<Scalar> get_derivatives() const { return std::vector<Scalar>(); }
146 };
147 
148 
149 } // namespace MiniDNN
150 
151 
152 #endif /* LAYER_MAXPOOLING_H_ */
void update(Optimizer &opt)
Definition: MaxPooling.h:139
const Matrix & backprop_data() const
Definition: MaxPooling.h:137
MaxPooling(const int in_width, const int in_height, const int in_channels, const int pooling_width, const int pooling_height)
Definition: MaxPooling.h:55
std::vector< Scalar > get_derivatives() const
Definition: MaxPooling.h:145
void init(const Scalar &mu, const Scalar &sigma, RNG &rng)
Definition: MaxPooling.h:64
void set_parameters(const std::vector< Scalar > &param)
Definition: MaxPooling.h:143
std::vector< Scalar > get_parameters() const
Definition: MaxPooling.h:141
const Matrix & output() const
Definition: MaxPooling.h:108
void forward(const Matrix &prev_layer_data)
Definition: MaxPooling.h:66
void backprop(const Matrix &prev_layer_data, const Matrix &next_layer_data)
Definition: MaxPooling.h:112