LBANN  0.103.0
LivermoreBigArtificialNeuralNetworkToolkit
crop.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_CROP_HPP_INCLUDED
28 #define LBANN_LAYER_CROP_HPP_INCLUDED
29 
30 #pragma GCC diagnostic push
31 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
32 
35 #include "lbann/proto/layers.pb.h"
37 #include "lbann/utils/protobuf.hpp"
38 
39 namespace lbann {
40 
49 template <typename TensorDataType,
51  El::Device Dev = El::Device::CPU>
52 class crop_layer : public data_type_layer<TensorDataType>
53 {
54  static_assert(T_layout == data_layout::DATA_PARALLEL,
55  "crop layer only supports DATA_PARALLEL");
56 #ifdef LBANN_HAS_GPU_FP16
57  using CompareType =
58  typename std::conditional<std::is_same<TensorDataType, fp16>::value,
59  float,
60  TensorDataType>::type;
61 #else
62  using CompareType = TensorDataType;
63 #endif
64 public:
66 
69  using AbsDistMatrixType = El::AbstractDistMatrix<TensorDataType>;
70 
72 
73 public:
74  crop_layer(lbann_comm* comm, std::vector<int> dims)
75  : data_type_layer<TensorDataType>(nullptr)
76  {
77  this->set_output_dims(dims);
79  }
80 
81  crop_layer(const crop_layer& other)
82  : data_type_layer<TensorDataType>(other),
83  m_input_v(other.m_input_v ? other.m_input_v->Copy() : nullptr),
84  m_output_v(other.m_output_v ? other.m_output_v->Copy() : nullptr),
85  m_crop_pos_v(other.m_crop_pos_v ? other.m_crop_pos_v->Copy() : nullptr)
86  {}
88  {
90  m_input_v.reset(other.m_input_v ? other.m_input_v->Copy() : nullptr);
91  m_output_v.reset(other.m_output_v ? other.m_output_v->Copy() : nullptr);
92  m_crop_pos_v.reset(other.m_crop_pos_v ? other.m_crop_pos_v->Copy()
93  : nullptr);
94  return *this;
95  }
96 
97  crop_layer* copy() const override { return new crop_layer(*this); }
98 
100 
102  template <typename ArchiveT>
103  void serialize(ArchiveT& ar);
104 
106 
107  std::string get_type() const override { return "crop"; }
108  data_layout get_data_layout() const override { return T_layout; }
109  El::Device get_device_allocation() const override { return Dev; }
110  bool can_run_inplace() const override { return false; }
111  int get_backprop_requirements() const override { return ERROR_SIGNALS; }
112 
113  void setup_data(size_t max_mini_batch_size) override
114  {
115  data_type_layer<TensorDataType>::setup_data(max_mini_batch_size);
116  const auto& input = this->get_prev_activations();
117  const auto& dist = input.DistData();
118  m_input_v.reset(input.Construct(input.Grid(), input.Root()));
119  m_output_v.reset(input.Construct(input.Grid(), input.Root()));
120 
122  m_crop_pos_v.reset(AbsDistMatrixType::Instantiate(
123  *dist.grid,
124  dist.root,
125  El::STAR,
126  dist.rowDist,
127  (dist.blockWidth == 1 ? El::ELEMENT : El::BLOCK),
128  El::Device::CPU));
129  }
130 
131  void setup_dims() override
132  {
134  std::stringstream err;
135 
136  // Make sure input tensors have valid dimensions
137  const auto& input_dims = this->get_input_dims(0);
138  const auto& loc_dims = this->get_input_dims(1);
139  const auto& output_dims = this->get_output_dims();
140  if (input_dims.size() != output_dims.size()) {
141  err << get_type() << " layer \"" << this->get_name() << "\" "
142  << "expects a crop input tensor with " << output_dims.size()
143  << " dimensions, "
144  << "but parent layer "
145  << "\"" << this->get_parent_layers()[0]->get_name() << "\" "
146  << "outputs a tensor with " << input_dims.size() << " dimensions";
147  LBANN_ERROR(err.str());
148  }
149  if (loc_dims.size() != 1 || loc_dims[0] != (int)input_dims.size()) {
150  err << get_type() << " layer \"" << this->get_name() << "\" "
151  << "expects a 1D crop position tensor with " << output_dims.size()
152  << " entries, "
153  << "but parent layer "
154  << "\"" << this->get_parent_layers()[1]->get_name() << "\" "
155  << "outputs a tensor with dimensions ";
156  for (size_t i = 0; i < loc_dims.size(); ++i) {
157  err << (i > 0 ? " x " : "") << loc_dims[i];
158  }
159  LBANN_ERROR(err.str());
160  }
161  }
162 
163 protected:
165  void write_specific_proto(lbann_data::Layer& proto) const final;
166 
167  friend class cereal::access;
168  crop_layer() : crop_layer(nullptr, {1}) {}
169 
170  void fp_compute() override
171  {
172  switch (this->get_input_dims().size()) {
173  case 3:
174  fp_compute_3d();
175  break;
176  default:
177  fp_compute_nd();
178  }
179  }
180 
181  void bp_compute() override
182  {
183  switch (this->get_input_dims().size()) {
184  case 3:
185  bp_compute_3d();
186  break;
187  default:
188  bp_compute_nd();
189  }
190  }
191 
192 private:
194  std::unique_ptr<AbsDistMatrixType> m_input_v;
196  std::unique_ptr<AbsDistMatrixType> m_output_v;
198  std::unique_ptr<AbsDistMatrixType> m_crop_pos_v;
199 
202  {
203 
204  // Input and output tensors
205  const auto& input = this->get_prev_activations(0);
206  auto& output = this->get_activations();
207 
208  // Tensor dimensions
209  const auto& input_dims = this->get_input_dims(0);
210  const auto& output_dims = this->get_output_dims();
211  const El::Int num_dims = output_dims.size();
212  const auto& local_width = input.LocalWidth();
213  const auto& region_size = output_dims.back();
214 
215  // Get crop position
216  m_crop_pos_v->Empty(false);
217  m_crop_pos_v->AlignWith(input);
218  const auto& input1 = this->get_prev_activations(1);
219  if (m_crop_pos_v->DistData() == input1.DistData()) {
220  El::LockedView(*m_crop_pos_v, input1);
221  }
222  else {
223  El::Copy(input1, *m_crop_pos_v);
224  }
225  const auto& local_crop_pos = m_crop_pos_v->LockedMatrix();
226 
227  // Crop each local mini-batch sample
228  // BVE_FIXME LBANN_OMP_PARALLEL_FOR
229  for (El::Int local_col = 0; local_col < local_width; ++local_col) {
230  const auto& col = input.GlobalCol(local_col);
231 
232  // Determine crop position
233  std::vector<El::Int> crop_offsets;
234  for (El::Int d = 0; d < num_dims; ++d) {
235  const auto& pos = local_crop_pos(d, local_col);
236  if (CompareType(pos) < CompareType(0.0) ||
237  CompareType(pos) > CompareType(1.0)) {
238  std::stringstream err;
239  err << "crop position not in range [0,1] (pos=(";
240  for (El::Int i = 0; i < local_crop_pos.Height(); ++i) {
241  err << (i > 0 ? "," : "") << local_crop_pos(i, local_col);
242  }
243  err << "))";
244  LBANN_ERROR(err.str());
245  }
246  const El::Int num_offsets = input_dims[d] - output_dims[d] + 1;
247  crop_offsets.push_back(
248  std::min(El::Int(static_cast<CompareType>(pos) * num_offsets),
249  num_offsets - 1));
250  }
251 
252  // Copy contiguous regions from input tensor to output tensor
253  std::vector<El::Int> output_pos(num_dims, 0);
254  while (output_pos[0] < output_dims[0]) {
255 
256  // Copy region from input tensor to output tensor
257  auto input_index = output_pos[0] + crop_offsets[0];
258  auto output_index = output_pos[0];
259  for (El::Int d = 1; d < num_dims; ++d) {
260  input_index =
261  (input_dims[d] * input_index + output_pos[d] + crop_offsets[d]);
262  output_index = output_dims[d] * output_index + output_pos[d];
263  }
264  El::LockedView(*m_input_v,
265  input,
266  El::IR(input_index, input_index + region_size),
267  El::IR(col));
268  El::View(*m_output_v,
269  output,
270  El::IR(output_index, output_index + region_size),
271  El::IR(col));
272  El::Copy(*m_input_v, *m_output_v);
273 
274  // Move to next contiguous region
275  output_pos.back() += region_size;
276  for (El::Int d = num_dims - 1; d >= 1; --d) {
277  if (output_pos[d] >= output_dims[d]) {
278  output_pos[d] = 0;
279  ++output_pos[d - 1];
280  }
281  }
282  }
283  }
284  }
285 
288  {
289 
290  // Clear error signals
291  El::Zero(this->get_error_signals(0));
292  El::Zero(this->get_error_signals(1));
293 
294  // Input and gradient tensors
295  const auto& gradient_wrt_output = this->get_prev_error_signals();
296  auto& gradient_wrt_input = this->get_error_signals(0);
297  const auto& local_crop_pos = m_crop_pos_v->LockedMatrix();
298 
299  // Tensor dimensions
300  const auto& input_dims = this->get_input_dims(0);
301  const auto& output_dims = this->get_output_dims();
302  const El::Int num_dims = output_dims.size();
303  const auto& local_width = gradient_wrt_input.LocalWidth();
304  const auto& region_size = output_dims.back();
305 
306  // Populate error signal for each local mini-batch sample
307  // BVE_FIXME LBANN_OMP_PARALLEL_FOR
308  for (El::Int local_col = 0; local_col < local_width; ++local_col) {
309  const auto& col = gradient_wrt_input.GlobalCol(local_col);
310 
311  // Determine crop position
312  std::vector<El::Int> crop_offsets;
313  for (El::Int d = 0; d < num_dims; ++d) {
314  const auto& pos = local_crop_pos(d, local_col);
315  if (CompareType(pos) < CompareType(0.0) ||
316  CompareType(pos) > CompareType(1.0)) {
317  std::stringstream err;
318  err << "crop position not in range [0,1] (pos=(";
319  for (El::Int i = 0; i < local_crop_pos.Height(); ++i) {
320  err << (i > 0 ? "," : "") << local_crop_pos(i, local_col);
321  }
322  err << "))";
323  LBANN_ERROR(err.str());
324  }
325  const El::Int num_offsets = input_dims[d] - output_dims[d] + 1;
326  crop_offsets.push_back(
327  std::min(El::Int(static_cast<CompareType>(pos) * num_offsets),
328  num_offsets - 1));
329  }
330 
331  // Populate contiguous regions in gradient w.r.t. input tensor
332  std::vector<El::Int> output_pos(num_dims, 0);
333  while (output_pos[0] < output_dims[0]) {
334 
335  // Copy region
336  auto input_index = output_pos[0] + crop_offsets[0];
337  auto output_index = output_pos[0];
338  for (El::Int d = 1; d < num_dims; ++d) {
339  input_index =
340  (input_dims[d] * input_index + output_pos[d] + crop_offsets[d]);
341  output_index = output_dims[d] * output_index + output_pos[d];
342  }
343  El::LockedView(*m_output_v,
344  gradient_wrt_output,
345  El::IR(output_index, output_index + region_size),
346  El::IR(col));
347  El::View(*m_input_v,
348  gradient_wrt_input,
349  El::IR(input_index, input_index + region_size),
350  El::IR(col));
351  El::Copy(*m_output_v, *m_input_v);
352 
353  // Move to next contiguous region
354  output_pos.back() += region_size;
355  for (El::Int d = num_dims - 1; d >= 1; --d) {
356  if (output_pos[d] >= output_dims[d]) {
357  output_pos[d] = 0;
358  ++output_pos[d - 1];
359  }
360  }
361  }
362  }
363  }
364 
368  void fp_compute_3d();
372  void bp_compute_3d();
373 };
374 
375 template <typename T, data_layout L, El::Device D>
376 void crop_layer<T, L, D>::write_specific_proto(lbann_data::Layer& proto) const
377 {
378  proto.set_datatype(proto::ProtoDataType<T>);
379  auto* msg = proto.mutable_crop();
380  protobuf::assign_to_repeated(*msg->mutable_dims(), this->get_output_dims());
381 }
382 
383 #ifndef LBANN_CROP_LAYER_INSTANTIATE
384 #define PROTO_DEVICE(T, Device) \
385  extern template class crop_layer<T, data_layout::DATA_PARALLEL, Device>
386 
388 #undef PROTO_DEVICE
389 #endif // LBANN_CROP_LAYER_INSTANTIATE
390 
391 } // namespace lbann
392 
393 #pragma GCC diagnostic pop
394 #endif // LBANN_LAYER_CROP_HPP_INCLUDED
std::unique_ptr< AbsDistMatrixType > m_input_v
Definition: crop.hpp:194
crop_layer & operator=(const crop_layer &other)
Definition: crop.hpp:87
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.
void bp_compute_nd()
Definition: crop.hpp:287
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: crop.hpp:170
int get_backprop_requirements() const override
Returns the necessary tensors for computing backpropagation.
Definition: crop.hpp:111
friend class cereal::access
Definition: crop.hpp:167
#define LBANN_ERROR(...)
Definition: exception.hpp:37
std::vector< int > get_input_dims(size_t input_index=0) const
Get input tensor dimensions.
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: crop.hpp:131
std::string get_type() const override
Get the layer type&#39;s name.
Definition: crop.hpp:107
std::unique_ptr< AbsDistMatrixType > m_output_v
Definition: crop.hpp:196
El::AbstractDistMatrix< TensorDataType > AbsDistMatrixType
The tensor type expected in this object.
Definition: crop.hpp:69
void bp_compute() override
Compute objective funciton gradients. Called by the &#39;back_prop&#39; function. Given the input...
Definition: crop.hpp:181
void write_specific_proto(lbann_data::Layer &proto) const final
Definition: crop.hpp:376
constexpr El::Device Device
OutputAbsDistMatrixType & get_prev_error_signals(int child_index=0)
InputAbsDistMatrixType & get_prev_activations(int parent_index=0)
std::unique_ptr< AbsDistMatrixType > m_crop_pos_v
Definition: crop.hpp:198
const OutputAbsDistMatrixType & get_activations(const Layer &child) const override
void set_output_dims(std::vector< int > dims, size_t output_index=0)
Set output tensor dimensions.
void assign_to_repeated(google::protobuf::RepeatedField< T > &field, ContainerT const &values)
Assign a range of values to a repeated protobuf field.
Definition: impl.hpp:125
void serialize(ArchiveT &ar)
Extract crop from tensor at a position.
Definition: crop.hpp:52
TensorDataType CompareType
Definition: crop.hpp:62
std::vector< const Layer * > get_parent_layers() const
std::string get_name() const
Get the layer instance&#39;s name.
Definition: layer.hpp:332
crop_layer * copy() const override
Copy function. This function dynamically allocates memory for a layer instance and instantiates a cop...
Definition: crop.hpp:97
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: crop.hpp:109
data_layout
Data layout that is optimized for different modes of parallelism.
Definition: base.hpp:218
crop_layer(const crop_layer &other)
Definition: crop.hpp:81
crop_layer(lbann_comm *comm, std::vector< int > dims)
Definition: crop.hpp:74
void setup_data(size_t max_mini_batch_size) override
Setup layer data. Called by the &#39;setup&#39; function. Memory is allocated for distributed matrices...
Definition: crop.hpp:113
void setup_data(size_t max_mini_batch_size) override
std::vector< int > get_output_dims(size_t output_index=0) const
Get output tensor dimensions.
bool can_run_inplace() const override
If True, the computation can run in-place (feeding each input activations tensor as the corresponding...
Definition: crop.hpp:110
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: crop.hpp:108
int m_expected_num_parent_layers
Definition: layer.hpp:838
data_type_layer & operator=(data_type_layer &&other)=default
const InputAbsDistMatrixType & get_error_signals(const Layer &parent) const override
void fp_compute_nd()
Definition: crop.hpp:201