Caffe2 - C++ API
A deep learning, cross platform ML framework
reduction_front_back_ops.h
1 #ifndef CAFFE2_OPERATORS_REDUCTION_FRONT_BACK_OPS_H_
2 #define CAFFE2_OPERATORS_REDUCTION_FRONT_BACK_OPS_H_
3 
4 #include "caffe2/core/context.h"
5 #include "caffe2/core/logging.h"
6 #include "caffe2/core/operator.h"
7 
8 namespace caffe2 {
9 
10 template <class Context, bool FIRSTDIMS, bool NORMALIZE>
11 class SumReduceDimsOp final : public Operator<Context> {
12  public:
13  SumReduceDimsOp(const OperatorDef& operator_def, Workspace* ws)
14  : Operator<Context>(operator_def, ws),
15  num_reduce_dims_(
16  OperatorBase::GetSingleArgument<int32_t>("num_reduce_dim", 1)) {}
17 
18  USE_OPERATOR_CONTEXT_FUNCTIONS;
19 
20  bool RunOnDevice() override {
22  this, Input(0));
23  }
24 
25  template <typename T>
26  bool DoRunWithType() {
27  auto& X = Input(0);
28  auto* Y = Output(0);
29 
30  CAFFE_ENFORCE(
31  num_reduce_dims_ >= 0 && num_reduce_dims_ <= X.dims().size(),
32  "For N-dim input tensor, support num_reduce_dims in range [0, N].");
33 
34  vector<TIndex> output_shape;
35  int start_index = FIRSTDIMS ? num_reduce_dims_ : 0;
36  int end_index =
37  FIRSTDIMS ? X.dims().size() : X.dims().size() - num_reduce_dims_;
38  for (int i = start_index; i < end_index; ++i) {
39  output_shape.push_back(X.dims()[i]);
40  }
41  Y->Resize(output_shape);
42 
43  const int rows = FIRSTDIMS ? X.size_to_dim(num_reduce_dims_)
44  : X.size_to_dim(X.ndim() - num_reduce_dims_);
45  const int cols = FIRSTDIMS ? X.size_from_dim(num_reduce_dims_)
46  : X.size_from_dim(X.ndim() - num_reduce_dims_);
47 
48  if (cols == 0 || rows == 0) {
49  return true;
50  }
51 
52  const int32_t* lengths_data = nullptr;
53  if (InputSize() > 1) {
54  const auto& lengths = Input(1);
55  lengths_data = lengths.template data<int32_t>();
56  CAFFE_ENFORCE(
57  num_reduce_dims_ == 1,
58  "Given lengths input, the number of reduce dimensions should be one.");
59  const int batch_size = FIRSTDIMS ? cols : rows;
60  CAFFE_ENFORCE(
61  lengths.size() == batch_size,
62  "The size of lengths vector doesn't match the batch size.");
63  }
64 
65  const T* in_data = X.template data<T>();
66  T* out_data = Y->template mutable_data<T>();
67  Compute(rows, cols, in_data, lengths_data, out_data);
68 
69  return true;
70  }
71 
72  private:
73  template <typename T>
74  void Compute(
75  int rows,
76  int cols,
77  const T* in_data,
78  const int32_t* lengths_data,
79  T* out_data);
80 
81  int num_reduce_dims_;
82 };
83 
84 template <class Context, bool FIRSTDIMS, bool NORMALIZE>
85 class SumReduceDimsGradientOp final : public Operator<Context> {
86  public:
87  SumReduceDimsGradientOp(const OperatorDef& operator_def, Workspace* ws)
88  : Operator<Context>(operator_def, ws),
89  num_reduce_dims_(
90  OperatorBase::GetSingleArgument<int32_t>("num_reduce_dim", 1)) {}
91 
92  USE_OPERATOR_CONTEXT_FUNCTIONS;
93 
94  bool RunOnDevice() override {
96  this, Input(0));
97  }
98 
99  template <typename T>
100  bool DoRunWithType() {
101  auto& dY = Input(0);
102  auto& input_1 = Input(1);
103  auto* dX = Output(0);
104 
105  // In previous diff we changed the semantic: Input(1) was changed from
106  // the shape of the input to the data tensor. This made the backward
107  // computation incompatible with old models. To fix this, we check
108  // the dimension and type of Input(1).
109  if (input_1.ndim() == 1 && input_1.template IsType<TIndex>()) {
110  // Input(1) is the shape of the input
111  shape_.CopyFrom(input_1);
112  // Copy first dims
113  vector<TIndex> output_shape(
114  shape_.template data<TIndex>(),
115  shape_.template data<TIndex>() + shape_.size());
116  dX->Resize(output_shape);
117  } else {
118  // Input(1) is data tensor X
119  dX->ResizeLike(input_1);
120  }
121 
122  const int rows = FIRSTDIMS ? dX->size_to_dim(num_reduce_dims_)
123  : dX->size_to_dim(dX->ndim() - num_reduce_dims_);
124  const int cols = FIRSTDIMS
125  ? dX->size_from_dim(num_reduce_dims_)
126  : dX->size_from_dim(dX->ndim() - num_reduce_dims_);
127 
128  const int32_t* lengths_data = nullptr;
129  if (InputSize() > 2) {
130  const auto& lengths = Input(2);
131  lengths_data = lengths.template data<int32_t>();
132  CAFFE_ENFORCE(
133  num_reduce_dims_ == 1,
134  "Given lengths input, the number of reduce dimensions should be one.");
135  const int batch_size = FIRSTDIMS ? cols : rows;
136  CAFFE_ENFORCE(
137  lengths.size() == batch_size,
138  "The size of lengths vector doesn't match the batch size.");
139  }
140 
141  const T* dYdata = dY.template data<T>();
142  T* dXdata = dX->template mutable_data<T>();
143  Compute<T>(rows, cols, dYdata, lengths_data, dXdata);
144  return true;
145  }
146 
147  private:
148  template <typename T>
149  void Compute(
150  int rows,
151  int cols,
152  const T* dYdata,
153  const int32_t* lengths_data,
154  T* dXdata);
155  int num_reduce_dims_;
156  // scratch space used for former version of this reducer
157  Tensor<CPUContext> shape_;
158 };
159 
160 template <typename T, class Context, bool FIRSTDIMS>
161 class MaxReduceDimsOp final : public Operator<Context> {
162  public:
163  MaxReduceDimsOp(const OperatorDef& operator_def, Workspace* ws)
164  : Operator<Context>(operator_def, ws),
165  num_reduce_dims_(
166  OperatorBase::GetSingleArgument<int32_t>("num_reduce_dim", 1)) {}
167 
168  USE_OPERATOR_CONTEXT_FUNCTIONS;
169 
170  bool RunOnDevice() {
171  auto& X = Input(0);
172  auto* Y = Output(0);
173 
174  CAFFE_ENFORCE(
175  num_reduce_dims_ >= 0 && num_reduce_dims_ <= X.dims().size(),
176  "For N-dim input tensor, support num_reduce_dims in range [0, N].");
177 
178  const int rows = FIRSTDIMS ? X.size_to_dim(num_reduce_dims_)
179  : X.size_to_dim(X.ndim() - num_reduce_dims_);
180  const int cols = FIRSTDIMS ? X.size_from_dim(num_reduce_dims_)
181  : X.size_from_dim(X.ndim() - num_reduce_dims_);
182 
183  vector<TIndex> output_shape;
184  int start_index = FIRSTDIMS ? num_reduce_dims_ : 0;
185  int end_index =
186  FIRSTDIMS ? X.dims().size() : X.dims().size() - num_reduce_dims_;
187 
188  for (int i = start_index; i < end_index; ++i) {
189  output_shape.push_back(X.dims()[i]);
190  }
191  Y->Resize(output_shape);
192 
193  if (cols == 0 || rows == 0) {
194  return true;
195  }
196 
197  const int32_t* lengths_data = nullptr;
198  if (InputSize() > 1) {
199  const auto& lengths = Input(1);
200  lengths_data = lengths.template data<int32_t>();
201  CAFFE_ENFORCE(
202  num_reduce_dims_ == 1,
203  "Given lengths input, the number of reduce dimensions should be one.");
204  const int batch_size = FIRSTDIMS ? cols : rows;
205  CAFFE_ENFORCE(
206  lengths.size() == batch_size,
207  "The size of lengths vector doesn't match the batch size.");
208  }
209 
210  const float* data = X.template data<float>();
211  float* out_data = Y->template mutable_data<float>();
212  Compute(rows, cols, data, lengths_data, out_data);
213  return true;
214  }
215 
216  protected:
217  void Compute(
218  int rows,
219  int cols,
220  const float* data,
221  const int32_t* lengths_data,
222  float* out_data);
223 
224  int num_reduce_dims_;
225 };
226 
227 template <typename T, class Context, bool FIRSTDIMS>
228 class MaxReduceDimsGradientOp final : public Operator<Context> {
229  public:
230  MaxReduceDimsGradientOp(const OperatorDef& operator_def, Workspace* ws)
231  : Operator<Context>(operator_def, ws),
232  num_reduce_dims_(
233  OperatorBase::GetSingleArgument<int32_t>("num_reduce_dim", 1)) {}
234 
235  USE_OPERATOR_CONTEXT_FUNCTIONS;
236 
237  bool RunOnDevice() override {
238  auto& dY = Input(0);
239  auto& X = Input(1);
240  auto& Y = Input(2);
241  auto* dX = Output(0);
242 
243  dX->ResizeLike(X);
244  const int rows = FIRSTDIMS ? X.size_to_dim(num_reduce_dims_)
245  : X.size_to_dim(X.ndim() - num_reduce_dims_);
246  const int cols = FIRSTDIMS ? X.size_from_dim(num_reduce_dims_)
247  : X.size_from_dim(X.ndim() - num_reduce_dims_);
248 
249  const float* dYdata = dY.template data<float>();
250  const float* Xdata = X.template data<float>();
251  const float* Ydata = Y.template data<float>();
252 
253  const int32_t* lengths_data = nullptr;
254  if (InputSize() > 3) {
255  const auto& lengths = Input(3);
256  lengths_data = lengths.template data<int32_t>();
257  CAFFE_ENFORCE(
258  num_reduce_dims_ == 1,
259  "Given lengths input, the number of reduce dimensions should be one.");
260  const int batch_size = FIRSTDIMS ? cols : rows;
261  CAFFE_ENFORCE(
262  lengths.size() == batch_size,
263  "The size of lengths vector doesn't match the batch size.");
264  }
265 
266  float* dXdata = dX->template mutable_data<float>();
267  Compute(rows, cols, dYdata, Xdata, Ydata, lengths_data, dXdata);
268  return true;
269  }
270 
271  protected:
272  void Compute(
273  int rows,
274  int cols,
275  const float* dYdata,
276  const float* Xdata,
277  const float* Ydata,
278  const int32_t* lengths_data,
279  float* dXdata);
280 
281  int num_reduce_dims_;
282 };
283 
284 } // namespace caffe2
285 
286 #endif // CAFFE2_OPERATORS_REDUCTION_FRONT_BACK_OPS_H_
Workspace is a class that holds all the related objects created during runtime: (1) all blobs...
Definition: workspace.h:47
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...