LBANN  0.103.0
LivermoreBigArtificialNeuralNetworkToolkit
unpooling.hpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2023, Lawrence Livermore National Security, LLC.
3 // Produced at the Lawrence Livermore National Laboratory.
4 // Written by the LBANN Research Team (B. Van Essen, et al.) listed in
5 // the CONTRIBUTORS file. <lbann-dev@llnl.gov>
6 //
7 // LLNL-CODE-697807.
8 // All rights reserved.
9 //
10 // This file is part of LBANN: Livermore Big Artificial Neural Network
11 // Toolkit. For details, see http://software.llnl.gov/LBANN or
12 // https://github.com/LLNL/LBANN.
13 //
14 // Licensed under the Apache License, Version 2.0 (the "Licensee"); you
15 // may not use this file except in compliance with the License. You may
16 // obtain a copy of the License at:
17 //
18 // http://www.apache.org/licenses/LICENSE-2.0
19 //
20 // Unless required by applicable law or agreed to in writing, software
21 // distributed under the License is distributed on an "AS IS" BASIS,
22 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
23 // implied. See the License for the specific language governing
24 // permissions and limitations under the license.
26 
27 #ifndef LBANN_LAYER_UNPOOLING_HPP_INCLUDED
28 #define LBANN_LAYER_UNPOOLING_HPP_INCLUDED
29 
30 #include "lbann/layers/layer.hpp"
33 #include "lbann/utils/im2col.hpp"
34 #include <vector>
35 
36 namespace lbann {
37 
45 template <typename TensorDataType,
47  El::Device Dev = El::Device::CPU>
48 class unpooling_layer : public data_type_layer<TensorDataType>
49 {
50  static_assert(T_layout == data_layout::DATA_PARALLEL,
51  "unpooling only supports DATA_PARALLEL");
52  static_assert(Dev == El::Device::CPU, "unpooling only supports CPU");
53 
54 private:
57 
58 public:
59  unpooling_layer(lbann_comm* comm) : data_type_layer<TensorDataType>(comm) {}
60 
61  unpooling_layer* copy() const override { return new unpooling_layer(*this); }
62 
64 
66  template <typename ArchiveT>
67  void serialize(ArchiveT& ar);
68 
70 
71  std::string get_type() const override { return "unpooling"; }
72  data_layout get_data_layout() const override { return T_layout; }
73  El::Device get_device_allocation() const override { return Dev; }
74  bool can_run_inplace() const override { return false; }
75  int get_backprop_requirements() const override { return ERROR_SIGNALS; }
76 
77  void setup_pointers() override
78  {
80 
81  // Check that pooling layer is valid
82  const auto* hint_layer =
83  dynamic_cast<const PoolLayerType*>(this->get_hint_layer());
84  if (hint_layer == nullptr) {
85  LBANN_ERROR(this->get_type(),
86  " layer \"",
87  this->get_name(),
88  "\" "
89  "does not have a valid pooling layer as a hint layer");
90  }
91  if (hint_layer->m_pool_mode != pooling_mode::MAX &&
92  hint_layer->m_pool_mode != pooling_mode::MAX_DETERMINISTIC) {
93  LBANN_ERROR("unpooling layer is only supported with max pooling");
94  }
95  if (hint_layer->using_gpus()) {
96  LBANN_ERROR("unpooling layer is not supported on GPUs");
97  }
98  }
99 
100  void setup_dims() override
101  {
103 
104  // Check that input tensor is valid
105  const auto* hint_layer = this->get_hint_layer();
106  const auto& input_dims = this->get_input_dims();
107  const auto& pool_output_dims = hint_layer->get_output_dims();
108  if (input_dims != pool_output_dims) {
109  std::stringstream err;
110  err << get_type() << " layer \"" << this->get_name() << "\" "
111  << "expects input tensors with dimensions ";
112  for (size_t i = 0; i < pool_output_dims.size(); ++i) {
113  err << (i > 0 ? " x " : "") << pool_output_dims[i];
114  }
115  err << ", but parent layer "
116  << "\"" << this->get_parent_layers()[0]->get_name() << "\" "
117  << "outputs with dimensions ";
118  for (size_t i = 0; i < input_dims.size(); ++i) {
119  err << (i > 0 ? " x " : "") << input_dims[i];
120  }
121  LBANN_ERROR(err.str());
122  }
123 
124  // Initialize output tensor based on corresponding pooling layer
125  this->set_output_dims(hint_layer->get_input_dims());
126  }
127 
128 protected:
130  void write_specific_proto(lbann_data::Layer& proto) const final;
131 
132  friend class cereal::access;
134 
135  void fp_compute() override
136  {
137  if (this->using_gpus()) {
138  throw lbann_exception("unpooling_layer: GPU version not yet implemented");
139  }
140  else {
142  }
143  }
144 
145  void bp_compute() override
146  {
147  if (this->using_gpus()) {
148  throw lbann_exception("unpooling_layer: GPU version not yet implemented");
149  }
150  else {
152  }
153  }
154 
155 private:
158  {
159 
160  using DMatDT = El::Matrix<TensorDataType, Dev>;
161 
162  // Get pooling layer
163  const auto& hint_layer =
164  dynamic_cast<const PoolLayerType&>(*this->get_hint_layer());
165 
166  // Get local matrices
167  const DMatDT& prev_activations_local = this->get_local_prev_activations();
168  DMatDT& activations_local = this->get_local_activations();
169 
170  // Get parameters
171  const int local_width = prev_activations_local.Width();
172  const auto& output_dims = this->get_output_dims();
173  const int num_channels = output_dims[0];
174  const int num_per_input_channel = this->get_input_size() / num_channels;
175  const int pool_size = hint_layer.m_pool_size;
176 
177  // Initialize im2col matrix
178  DMatDT im2col_mat(pool_size * num_channels, num_per_input_channel);
179 
180  // Iterate through data samples
181  for (int sample = 0; sample < local_width; ++sample) {
182 
183  // Clear im2col matrix
184  El::Zero(im2col_mat);
185 
186  // Populate im2col matrix
187  const TensorDataType* prev_activations_buffer =
188  prev_activations_local.LockedBuffer(0, sample);
189  const int* indices_buffer =
190  &hint_layer.m_max_pool_indices[sample * this->get_input_size()];
192  for (int channel = 0; channel < num_channels; ++channel) {
193  for (int j = 0; j < num_per_input_channel; ++j) {
194  const int input_index = j + channel * num_per_input_channel;
195  const int max_index = indices_buffer[input_index];
196  TensorDataType* im2col_buffer =
197  im2col_mat.Buffer(channel * pool_size, j);
198  im2col_buffer[max_index] = prev_activations_buffer[input_index];
199  }
200  }
201 
202  // Convert im2col matrix to output matrix
203  DMatDT output_mat = El::View(activations_local, El::ALL, El::IR(sample));
204  col2im<TensorDataType>(
205  im2col_mat,
206  output_mat,
207  num_channels,
208  output_dims.size() - 1,
209  &output_dims[1],
210  hint_layer.m_pads.data(),
211  hint_layer.m_pool_dims.data(),
212  hint_layer.m_strides.data(),
213  [](TensorDataType const& a, TensorDataType const& b) {
214  return std::max(a, b);
215  });
216  }
217  }
218 
221  {
222 
223  using DMatDT = El::Matrix<TensorDataType, Dev>;
224 
225  // Get pooling layer
226  const auto& hint_layer =
227  dynamic_cast<const PoolLayerType&>(*this->get_hint_layer());
228 
229  // Get local matrices
230  const DMatDT& prev_error_signal_local =
232  DMatDT& error_signal_local = this->get_local_error_signals();
233 
234  // Get parameters
235  const int local_width = prev_error_signal_local.Width();
236  const auto& output_dims = this->get_output_dims();
237  const int num_channels = output_dims[0];
238  const int num_per_output_channel = this->get_input_size() / num_channels;
239  const int pool_size = hint_layer.m_pool_size;
240 
241  // Initialize im2col matrix
242  DMatDT im2col_mat(pool_size * num_channels, num_per_output_channel);
243 
244  // Iterate through data samples
245  for (int sample = 0; sample < local_width; ++sample) {
246 
247  // Construct im2col matrix from input
248  const DMatDT& input_mat =
249  El::LockedView(prev_error_signal_local, El::ALL, El::IR(sample));
250  im2col<TensorDataType>(input_mat,
251  im2col_mat,
252  num_channels,
253  output_dims.size() - 1,
254  &output_dims[1],
255  hint_layer.m_pads.data(),
256  hint_layer.m_pool_dims.data(),
257  hint_layer.m_strides.data());
258 
259  // Propagate error signal based on pooling layer
260  TensorDataType* output_buffer = error_signal_local.Buffer(0, sample);
261  const int* indices_buffer =
262  &hint_layer.m_max_pool_indices[sample * this->get_input_size()];
264  for (int channel = 0; channel < num_channels; ++channel) {
265  for (int j = 0; j < num_per_output_channel; ++j) {
266  const int output_index = j + channel * num_per_output_channel;
267  const int max_index = indices_buffer[output_index];
268  TensorDataType* im2col_buffer =
269  im2col_mat.Buffer(channel * pool_size, j);
270  output_buffer[output_index] = im2col_buffer[max_index];
271  }
272  }
273  }
274  }
275 };
276 
277 #ifndef LBANN_UNPOOLING_LAYER_INSTANTIATE
278 #define PROTO(T) \
279  extern template class unpooling_layer<T, \
280  data_layout::DATA_PARALLEL, \
281  El::Device::CPU>
282 
283 #define LBANN_INSTANTIATE_CPU_HALF
285 #undef PROTO
286 #undef LBANN_INSTANTIATE_CPU_HALF
287 #endif // LBANN_UNPOOLING_LAYER_INSTANTIATE
288 
289 } // namespace lbann
290 
291 #endif // LBANN_LAYER_UNPOOLING_HPP_INCLUDED
virtual void setup_dims()
Setup tensor dimensions Called by the &#39;setup&#39; function. If there are any input tensors, the base method sets all uninitialized output tensor dimensions equal to the first input tensor dimensions.
int get_backprop_requirements() const override
Returns the necessary tensors for computing backpropagation.
Definition: unpooling.hpp:75
const Layer * get_hint_layer() const
Get hint layer.
friend class cereal::access
Definition: unpooling.hpp:132
void fp_compute() override
Apply layer operation. Called by the &#39;forward_prop&#39; function. Given the input tensors, the output tensors are populated with computed values.
Definition: unpooling.hpp:135
#define LBANN_ERROR(...)
Definition: exception.hpp:37
std::vector< int > get_input_dims(size_t input_index=0) const
Get input tensor dimensions.
const InputAbsMatrixType & get_local_prev_activations(int parent_index=0) const
unpooling_layer * copy() const override
Copy function. This function dynamically allocates memory for a layer instance and instantiates a cop...
Definition: unpooling.hpp:61
const OutputAbsMatrixType & get_local_prev_error_signals(int child_index=0) const
void write_specific_proto(lbann_data::Layer &proto) const final
void bp_compute() override
Compute objective funciton gradients. Called by the &#39;back_prop&#39; function. Given the input...
Definition: unpooling.hpp:145
constexpr El::Device Device
OutputAbsMatrixType & get_local_activations(int child_index=0)
void fp_compute_im2col()
Unpooling forward propagation with im2col.
Definition: unpooling.hpp:157
int get_input_size(size_t input_index=0) const
Get input tensor size.
void set_output_dims(std::vector< int > dims, size_t output_index=0)
Set output tensor dimensions.
#define LBANN_OMP_PARALLEL_FOR
Definition: omp_pragma.hpp:67
std::vector< const Layer * > get_parent_layers() const
exception lbann_exception
Definition: exception.hpp:145
bool can_run_inplace() const override
If True, the computation can run in-place (feeding each input activations tensor as the corresponding...
Definition: unpooling.hpp:74
data_layout get_data_layout() const override
Get data layout of the data tensors. We assume that the data layouts of the previous activations...
Definition: unpooling.hpp:72
void setup_dims() override
Setup tensor dimensions Called by the &#39;setup&#39; function. If there are any input tensors, the base method sets all uninitialized output tensor dimensions equal to the first input tensor dimensions.
Definition: unpooling.hpp:100
std::string get_name() const
Get the layer instance&#39;s name.
Definition: layer.hpp:332
void serialize(ArchiveT &ar)
El::Device get_device_allocation() const override
Get the device allocation for the data tensors. We assume that the decice allocation of the previous ...
Definition: unpooling.hpp:73
data_layout
Data layout that is optimized for different modes of parallelism.
Definition: base.hpp:218
unpooling_layer(lbann_comm *comm)
Definition: unpooling.hpp:59
InputAbsMatrixType & get_local_error_signals(int parent_index=0)
Transpose of pooling layer.
bool using_gpus() const noexcept
Whether the layer is using a GPU implementation.
Definition: layer.hpp:417
std::string get_type() const override
Get the layer type&#39;s name.
Definition: unpooling.hpp:71
virtual void setup_pointers()
Setup layer pointers. Called by the &#39;setup&#39; function. Pointers to parent/child layers are assumed to ...
std::vector< int > get_output_dims(size_t output_index=0) const
Get output tensor dimensions.
void bp_compute_im2col()
Unpooling backward propagation with im2col.
Definition: unpooling.hpp:220
void setup_pointers() override
Setup layer pointers. Called by the &#39;setup&#39; function. Pointers to parent/child layers are assumed to ...
Definition: unpooling.hpp:77