LBANN  0.103.0
LivermoreBigArtificialNeuralNetworkToolkit
layers/transform/pooling.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_POOLING_HPP_INCLUDED
28 #define LBANN_LAYER_POOLING_HPP_INCLUDED
29 
31 #include "lbann/models/model.hpp"
34 #ifdef LBANN_HAS_DNN_LIB
37 #endif // LBANN_HAS_DNN_LIB
39 #include "lbann/utils/im2col.hpp"
40 
41 #include <utility>
42 #include <vector>
43 
44 #ifdef LBANN_HAS_DISTCONV
45 #include "distconv/dnn_backend/pooling.hpp"
46 #include "lbann/utils/distconv.hpp"
47 #endif // LBANN_HAS_DISTCONV
48 
49 namespace lbann {
50 
51 inline pooling_mode to_pool_mode(std::string m)
52 {
53 #ifdef LBANN_DETERMINISTIC
54  if (m == "max")
56 #else
57  if (m == "max")
58  return pooling_mode::MAX;
59 #endif // LBANN_DETERMINISTIC
60  if (m == "average")
62  if (m == "average_no_pad")
64  else {
65  LBANN_ERROR("Invalid pooling mode requested.");
66  }
67 }
68 
69 #ifdef LBANN_HAS_DISTCONV
70 
71 namespace dc {
73 using Backend = ::distconv::BackendDNNLib;
74 template <typename TensorDataType>
75 using Pooling = ::distconv::Pooling<Backend, TensorDataType>;
76 } // namespace dc
77 
78 template <typename TensorDataType,
80  El::Device Dev = El::Device::CPU>
81 class pooling_distconv_adapter
82  : public data_type_distconv_adapter<TensorDataType>
83 {
84 public:
85  using TensorDevType =
87  pooling_distconv_adapter(Layer& layer)
89  {}
90  virtual ~pooling_distconv_adapter() = default;
91  void setup_distributions(tensor_overlap_constraints& constraints) override;
92  dc::Shape get_activations_local_shape(int index = 0) const override;
93  void setup_layer(size_t workspace_capacity) override;
94  void
95  fp_compute(bool training = true); // training=true for max back-compatibility.
96  void bp_compute();
97  std::unique_ptr<dc::Pooling<TensorDataType>> m_pooling;
98 };
99 #endif // LBANN_HAS_DISTCONV
100 
101 // Forward declaration
102 template <typename TensorDataType, data_layout T_layout, El::Device Dev>
104 
105 template <typename TensorDataType,
107  El::Device Dev = El::Device::CPU>
108 class pooling_layer : public data_type_layer<TensorDataType>
109 {
110  static_assert(T_layout == data_layout::DATA_PARALLEL,
111  "pooling only supports DATA_PARALLEL");
112 
113 private:
116 
118  std::vector<int> m_pool_dims;
122  std::vector<int> m_pads;
124  std::vector<int> m_strides;
125 
131  std::vector<int> m_max_pool_indices;
132 
133 #ifdef LBANN_HAS_DNN_LIB
134 
135  dnn_lib::PoolingDescriptor m_pooling_dnn_desc;
138  m_tensors_dnn_desc;
139 #endif // LBANN_HAS_DNN_LIB
140 
141  friend class unpooling_layer<TensorDataType, T_layout, Dev>;
142 
143 public:
145  int num_data_dims,
146  int pool_dim,
147  int pad,
148  int stride,
149  pooling_mode mode)
150  : pooling_layer(comm,
151  num_data_dims,
152  std::vector<int>(num_data_dims, pool_dim),
153  std::vector<int>(num_data_dims, pad),
154  std::vector<int>(num_data_dims, stride),
155  mode)
156  {}
157 
159  int num_data_dims,
160  std::vector<int> pool_dims,
161  std::vector<int> pads,
162  std::vector<int> strides,
163  pooling_mode mode)
164  : data_type_layer<TensorDataType>(comm),
165  m_pool_mode(mode),
166  m_pool_dims(pool_dims),
167  m_pads(pads),
168  m_strides(strides)
169 #ifdef LBANN_HAS_DNN_LIB
170  ,
171  m_tensors_dnn_desc(this)
172 #endif // LBANN_HAS_DNN_LIB
173  {
174  // Initialize input dimensions and pooling parameters
175  m_pool_size = get_linear_size(m_pool_dims);
176  }
177 
179  : data_type_layer<TensorDataType>(other),
180  m_pool_mode(other.m_pool_mode),
181  m_pool_dims(other.m_pool_dims),
182  m_pool_size(other.m_pool_size),
183  m_pads(other.m_pads),
184  m_strides(other.m_strides),
185  m_max_pool_indices(other.m_max_pool_indices)
186 #ifdef LBANN_HAS_DNN_LIB
187  ,
188  m_pooling_dnn_desc(other.m_pooling_dnn_desc),
189  m_tensors_dnn_desc(other.m_tensors_dnn_desc)
190 #endif // LBANN_HAS_DNN_LIB
191  {
192 #ifdef LBANN_HAS_DNN_LIB
193  m_tensors_dnn_desc.set_layer(this);
194 #endif // LBANN_HAS_DNN_LIB
195  }
196 
198  {
200  m_pool_mode = other.m_pool_mode;
201  m_pool_dims = other.m_pool_dims;
202  m_pool_size = other.m_pool_size;
203  m_pads = other.m_pads;
204  m_strides = other.m_strides;
205  m_max_pool_indices = other.m_max_pool_indices;
206 #ifdef LBANN_HAS_DNN_LIB
207  m_pooling_dnn_desc = other.m_pooling_dnn_desc;
208  m_tensors_dnn_desc = other.m_tensors_dnn_desc;
209  m_tensors_dnn_desc.set_layer(this);
210 #endif // LBANN_HAS_DNN_LIB
211  return *this;
212  }
213 
214  ~pooling_layer() override = default;
215 
216  pooling_layer* copy() const override { return new pooling_layer(*this); }
217 
219 
221  template <typename ArchiveT>
222  void serialize(ArchiveT& ar);
223 
225 
226  std::string get_type() const override { return "pooling"; }
227  data_layout get_data_layout() const override { return T_layout; }
228  El::Device get_device_allocation() const override { return Dev; }
229  bool can_run_inplace() const override { return false; }
230  int get_backprop_requirements() const override
231  {
233  }
234 
235 #ifdef LBANN_HAS_ONNX
236  void fill_onnx_node(onnx::GraphProto& graph) const override;
237 #endif // LBANN_HAS_ONNX
238 
239  description get_description() const override
240  {
242  std::stringstream ss;
243 
244  // Pool mode
245  ss.str(std::string{});
246  ss.clear();
247  switch (m_pool_mode) {
248  case pooling_mode::MAX:
249  ss << "max";
250  break;
252  ss << "max (deterministic)";
253  break;
255  ss << "average";
256  break;
258  ss << "average (no pad)";
259  break;
260  default:
261  ss << "invalid";
262  }
263  desc.add("Pool mode", ss.str());
264 
265  // Pool dimensions
266  ss.str(std::string{});
267  ss.clear();
268  for (size_t i = 0; i < m_pool_dims.size(); ++i) {
269  ss << (i > 0 ? ", " : "") << m_pool_dims[i];
270  }
271  desc.add("Pool dimensions", ss.str());
272 
273  // Strides
274  ss.str(std::string{});
275  ss.clear();
276  for (size_t i = 0; i < m_strides.size(); ++i) {
277  ss << (i > 0 ? ", " : "") << m_strides[i];
278  }
279  desc.add("Strides", ss.str());
280 
281  // Pads
282  ss.str(std::string{});
283  ss.clear();
284  for (size_t i = 0; i < m_pads.size(); ++i) {
285  ss << (i > 0 ? ", " : "") << m_pads[i];
286  }
287  desc.add("Pads", ss.str());
288 
289  // Result
290  return desc;
291  }
292 
293 protected:
295  void write_specific_proto(lbann_data::Layer& proto) const final;
296 
297  friend class cereal::access;
298  pooling_layer() : pooling_layer(nullptr, 1, 1, 1, 1, pooling_mode::MAX) {}
299 
300  void setup_dims() override
301  {
303  const auto& input_dims = this->get_input_dims();
304  auto output_dims = input_dims;
305  for (size_t i = 0; i < output_dims.size() - 1; ++i) {
306  const int effective_dim =
307  (input_dims[i + 1] + 2 * m_pads[i] - m_pool_dims[i] + 1);
308  output_dims[i + 1] = (effective_dim + m_strides[i] - 1) / m_strides[i];
309  }
310  this->set_output_dims(output_dims);
311  }
312 
314  void setup_gpu() override
315  {
317 #ifndef LBANN_HAS_DNN_LIB
318  LBANN_ERROR("DNN library not detected");
319 #else
320 
321  // Set pooling descriptor
322  m_pooling_dnn_desc.set(m_pool_mode,
323  dnn_lib::DNN_PROPAGATE_NAN,
324  m_pool_dims.size(),
325  m_pool_dims.data(),
326  m_pads.data(),
327  m_strides.data());
328 
329 #endif // #ifndef LBANN_HAS_DNN_LIB
330  }
331 
332  void fp_compute() override;
333 
334  void bp_compute() override;
335 
336 private:
338  void fp_compute_dnn();
339 
341  void bp_compute_dnn();
342 
344  void fp_compute_im2col();
345 
347  void bp_compute_im2col();
348 
349 #ifdef LBANN_HAS_DISTCONV
350  friend class pooling_distconv_adapter<TensorDataType, T_layout, Dev>;
351 
352 protected:
353  bool is_distconv_supported() const override;
354  void setup_distconv_adapter() override
355  {
356  this->get_distconv_adapter_ptr() =
357  std::make_unique<pooling_distconv_adapter<TensorDataType, T_layout, Dev>>(
358  *this);
359  }
360  pooling_distconv_adapter<TensorDataType, T_layout, Dev>&
361  get_distconv_adapter() override;
362  const pooling_distconv_adapter<TensorDataType, T_layout, Dev>&
363  get_distconv_adapter() const override;
364 #endif // LBANN_HAS_DISTCONV
365 };
366 
367 #ifdef LBANN_HAS_ONNX
368 template <typename T, data_layout L, El::Device D>
369 void pooling_layer<T, L, D>::fill_onnx_node(onnx::GraphProto& graph) const
370 {
371  auto* pool = graph.add_node();
372 
373  // Get the attributes setup first
374  {
375  auto* kernel_shape = pool->add_attribute();
376  kernel_shape->set_name("kernel_shape");
377  kernel_shape->set_type(onnx::AttributeProto::INTS);
378  for (auto const& k : this->m_pool_dims)
379  kernel_shape->add_ints(k);
380  }
381  if (!this->m_strides.empty()) {
382  auto* strides = pool->add_attribute();
383  strides->set_name("strides");
384  strides->set_type(onnx::AttributeProto::INTS);
385  for (auto const& s : this->m_strides)
386  strides->add_ints(s);
387  }
388  if (!this->m_pads.empty()) {
389  auto* pads = pool->add_attribute();
390  pads->set_name("pads");
391  pads->set_type(onnx::AttributeProto::INTS);
392  for (auto const& p : this->m_pads) {
393  pads->add_ints(p);
394  pads->add_ints(p);
395  }
396  }
397  // FIXME: This is missing "dilations". However, they're only a valid
398  // attribute for MaxPool, not AveragePool.
399 
400  for (auto const* parent : this->get_parent_layers()) {
401  size_t idx = parent->find_child_layer_index(*this);
402  pool->add_input(parent->get_name() + "_" + std::to_string(idx));
403  }
404  for (size_t ii = 0; ii < this->num_weights(); ii++)
405  pool->add_input(this->get_weights(ii).get_name());
406  for (auto const* child : this->get_child_layers()) {
407  size_t idx = this->find_child_layer_index(*child);
408  pool->add_output(this->get_name() + "_" + std::to_string(idx));
409  }
410  pool->set_name(this->get_name());
411 
412  switch (m_pool_mode) {
413  case pooling_mode::MAX:
414  pool->set_op_type("MaxPool");
415  break;
417  pool->set_op_type("MaxPool");
418  break;
420  pool->set_op_type("AveragePool");
421  break;
423  pool->set_op_type("AveragePool");
424  break;
425  default:
426  LBANN_ERROR("pooling_layer: no ONNX implementation for pooling mode");
427  }
428 
429  pool->set_domain("");
430  pool->set_doc_string(this->get_type());
431 }
432 #endif
433 
434 #ifndef LBANN_POOLING_LAYER_INSTANTIATE
435 #define PROTO_DEVICE(T, Device) \
436  extern template class pooling_layer<T, data_layout::DATA_PARALLEL, Device>
437 
439 #undef PROTO_DEVICE
440 #endif // LBANN_POOLING_LAYER_INSTANTIATE
441 
442 } // namespace lbann
443 
444 #endif // LBANN_LAYER_POOLING_HPP_INCLUDED
int get_backprop_requirements() const override
Returns the necessary tensors for computing backpropagation.
El::Device get_device_allocation() const override
Get the device allocation for the data tensors. We assume that the decice allocation of the previous ...
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.
std::basic_string< T > pad(const std::basic_string< T > &s, typename std::basic_string< T >::size_type n, T c)
Definition: file_utils.hpp:93
auto get_linear_size(std::vector< T > const &dims)
Definition: dim_helpers.hpp:59
#define LBANN_ERROR(...)
Definition: exception.hpp:37
void setup_gpu() override
Initialize GPU objects.
pooling_layer(lbann_comm *comm, int num_data_dims, std::vector< int > pool_dims, std::vector< int > pads, std::vector< int > strides, pooling_mode mode)
void serialize(std::ostream &os, google::protobuf::Message const &msg)
Serialize the protobuf message to a stream.
Neural network tensor operation.
Definition: layer.hpp:285
Generates nicely formatted description messages.
Definition: description.hpp:49
pooling_mode to_pool_mode(std::string m)
virtual description get_description() const
Human-readable description.
constexpr El::Device Device
std::string to_string(El::Device const &d)
bool can_run_inplace() const override
If True, the computation can run in-place (feeding each input activations tensor as the corresponding...
pooling_layer(const pooling_layer &other)
virtual void setup_gpu()
Setup GPU objects. Called by the &#39;setup&#39; function if the layer is on GPUs.
Definition: layer.hpp:782
pooling_mode
Which pooling mode to use.
Definition: dnn_enums.hpp:78
data_layout get_data_layout() const override
Get data layout of the data tensors. We assume that the data layouts of the previous activations...
::distconv::tensor::Shape Shape
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.
std::vector< int > m_max_pool_indices
data_layout
Data layout that is optimized for different modes of parallelism.
Definition: base.hpp:218
pooling_layer & operator=(const pooling_layer &other)
Transpose of pooling layer.
description get_description() const override
Human-readable description.
pooling_layer * copy() const override
Copy function. This function dynamically allocates memory for a layer instance and instantiates a cop...
std::string get_type() const override
Get the layer type&#39;s name.
void set_name(const std::string name)
Set the layer instance&#39;s name. Each layer in a model should have a unique, preferably human-readable...
Definition: layer.hpp:319
data_type_layer & operator=(data_type_layer &&other)=default
dc::TensorDev< OutputTensorDataType > TensorDevType
pooling_layer(lbann_comm *comm, int num_data_dims, int pool_dim, int pad, int stride, pooling_mode mode)