Caffe2 - C++ API
A deep learning, cross platform ML framework
reduce_ops.cc
1 #include "caffe2/operators/reduce_ops.h"
2 
3 #include <algorithm>
4 #include <functional>
5 #include <vector>
6 
7 #include "caffe2/utils/math.h"
8 
9 namespace caffe2 {
10 
11 namespace {
12 
13 template <typename T>
14 void ComputeReduceMinMaxGradient(
15  const std::vector<int>& dY_dims,
16  const std::vector<int>& dX_dims,
17  const T* dY_data,
18  const T* X_data,
19  const T* Y_data,
20  T* dX_data) {
21  const int dX_size = std::accumulate(
22  dX_dims.cbegin(), dX_dims.cend(), 1, std::multiplies<int>());
23  const int ndim = dX_dims.size();
24  std::vector<int> index(ndim, 0);
25  for (int dX_index = 0; dX_index < dX_size; ++dX_index) {
26  const int dY_index =
27  math::utils::GetIndexFromDims(ndim, dY_dims.data(), index.data());
28  dX_data[dX_index] =
29  Y_data[dY_index] == X_data[dX_index] ? dY_data[dY_index] : T(0);
30  math::utils::IncreaseIndexInDims(ndim, dX_dims.data(), index.data());
31  }
32 }
33 
34 } // namespace
35 
36 template <>
37 template <typename T>
38 bool MinReducer<CPUContext>::Backward(
39  const std::vector<int>& dY_dims,
40  const std::vector<int>& dX_dims,
41  const T* dY_data,
42  const T* X_data,
43  const T* Y_data,
44  T* dX_data,
45  CPUContext* /* context */) const {
46  ComputeReduceMinMaxGradient(
47  dY_dims, dX_dims, dY_data, X_data, Y_data, dX_data);
48  return true;
49 }
50 
51 template <>
52 template <typename T>
53 bool MaxReducer<CPUContext>::Backward(
54  const std::vector<int>& dY_dims,
55  const std::vector<int>& dX_dims,
56  const T* dY_data,
57  const T* X_data,
58  const T* Y_data,
59  T* dX_data,
60  CPUContext* /* context */) const {
61  ComputeReduceMinMaxGradient(
62  dY_dims, dX_dims, dY_data, X_data, Y_data, dX_data);
63  return true;
64 }
65 
66 REGISTER_CPU_OPERATOR(
67  ReduceMin,
68  ReduceOp<
69  TensorTypes<std::int32_t, std::int64_t, float, double>,
70  CPUContext,
71  MinReducer<CPUContext>>);
72 REGISTER_CPU_OPERATOR(
73  ReduceMinGradient,
74  ReduceGradientOp<
75  TensorTypes<std::int32_t, std::int64_t, float, double>,
76  CPUContext,
77  MinReducer<CPUContext>>);
78 
79 OPERATOR_SCHEMA(ReduceMin)
80  .NumInputs(1)
81  .NumOutputs(1)
82  .SetDoc(R"DOC(
83  Computes the min of the input tensor's element along the provided axes.
84  The resulted tensor has the same rank as the input if keepdims equal True.
85  If keepdims equal false, then the resulted tensor have the reduced dimension
86  pruned.
87 )DOC")
88  .Arg("axes", "A list of integers, along which to reduce.")
89  .Arg(
90  "keepdims",
91  "Keep the reduced dimension(s) or not, default True keeps the reduced "
92  "dimension(s).")
93  .Input(0, "data", "An input tensor.")
94  .Output(0, "reduced", "Reduced output tensor.");
95 
96 OPERATOR_SCHEMA(ReduceMinGradient).NumInputs(3).NumOutputs(1);
97 
98 REGISTER_CPU_OPERATOR(
99  ReduceMax,
100  ReduceOp<
101  TensorTypes<std::int32_t, std::int64_t, float, double>,
102  CPUContext,
103  MaxReducer<CPUContext>>);
104 REGISTER_CPU_OPERATOR(
105  ReduceMaxGradient,
106  ReduceGradientOp<
107  TensorTypes<std::int32_t, std::int64_t, float, double>,
108  CPUContext,
109  MaxReducer<CPUContext>>);
110 
111 OPERATOR_SCHEMA(ReduceMax)
112  .NumInputs(1)
113  .NumOutputs(1)
114  .SetDoc(R"DOC(
115  Computes the max of the input tensor's element along the provided axes.
116  The resulted tensor has the same rank as the input if keepdims equal True.
117  If keepdims equal false, then the resulted tensor have the reduced dimension
118  pruned.
119 )DOC")
120  .Arg("axes", "A list of integers, along which to reduce.")
121  .Arg(
122  "keepdims",
123  "Keep the reduced dimension(s) or not, default True keeps the reduced "
124  "dimension(s).")
125  .Input(0, "data", "An input tensor.")
126  .Output(0, "reduced", "Reduced output tensor.");
127 
128 OPERATOR_SCHEMA(ReduceMaxGradient).NumInputs(3).NumOutputs(1);
129 
130 REGISTER_CPU_OPERATOR(
131  ReduceSum,
132  ReduceOp<
133  TensorTypes<std::int32_t, std::int64_t, float, double>,
134  CPUContext,
135  SumReducer<CPUContext>>);
136 REGISTER_CPU_OPERATOR(
137  ReduceSumGradient,
138  ReduceGradientOp<
139  TensorTypes<std::int32_t, std::int64_t, float, double>,
140  CPUContext,
141  SumReducer<CPUContext>>);
142 
143 OPERATOR_SCHEMA(ReduceSum)
144  .NumInputs(1)
145  .NumOutputs(1)
146  .SetDoc(R"DOC(
147 Computes the **sum** of the input tensor's elements along the provided `axes`. The resulting tensor has the same rank as the input if the `keepdims` argument equals 1 (default). If `keepdims` is set to 0, then the `axes` dimensions are pruned.
148 
149 Github Links:
150 - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/reduce_ops.cc
151 
152 <details>
153 
154 <summary> <b>Example</b> </summary>
155 
156 **Code**
157 
158 ```
159 
160 workspace.ResetWorkspace()
161 
162 op = core.CreateOperator(
163  "ReduceSum",
164  ["X"],
165  ["Y"],
166  axes=(0,1),
167  keepdims=0
168 )
169 
170 workspace.FeedBlob("X", np.random.randint(10, size=(1,2,5,5)).astype(np.float32))
171 print("X:", workspace.FetchBlob("X"))
172 workspace.RunOperatorOnce(op)
173 print("Y:", workspace.FetchBlob("Y"))
174 
175 ```
176 
177 **Result**
178 
179 ```
180 
181 X:
182 [[[[5. 3. 7. 9. 5.]
183  [4. 5. 1. 8. 3.]
184  [1. 0. 9. 7. 6.]
185  [7. 5. 0. 3. 1.]
186  [6. 4. 4. 8. 3.]]
187 
188  [[8. 9. 6. 7. 7.]
189  [5. 5. 4. 7. 0.]
190  [9. 7. 6. 6. 7.]
191  [7. 5. 2. 4. 2.]
192  [4. 5. 1. 9. 4.]]]]
193 Y:
194 [[13. 12. 13. 16. 12.]
195  [ 9. 10. 5. 15. 3.]
196  [10. 7. 15. 13. 13.]
197  [14. 10. 2. 7. 3.]
198  [10. 9. 5. 17. 7.]]
199 
200 ```
201 
202 </details>
203 
204 )DOC")
205  .Arg("axes", "(*Tuple(int)*): list of axes to reduce")
206  .Arg(
207  "keepdims",
208  "(*int*): set to 1 to keep the reduced dimension(s) (default=1), else set to 0 to not keep the reduced dimension(s)")
209  .Input(0, "X", "(*Tensor`<float>`*): input tensor")
210  .Output(0, "Y", "(*Tensor`<float>`*): reduced tensor");
211 
212 OPERATOR_SCHEMA(ReduceSumGradient).NumInputs(3).NumOutputs(1);
213 
214 REGISTER_CPU_OPERATOR(
215  ReduceMean,
216  ReduceOp<TensorTypes<float>, CPUContext, MeanReducer<CPUContext>>);
217 REGISTER_CPU_OPERATOR(
218  ReduceMeanGradient,
219  ReduceGradientOp<TensorTypes<float>, CPUContext, MeanReducer<CPUContext>>);
220 
221 OPERATOR_SCHEMA(ReduceMean)
222  .NumInputs(1)
223  .NumOutputs(1)
224  .SetDoc(R"DOC(
225 Computes the **mean** of the input tensor's elements along the provided `axes`. The resulting tensor has the same rank as the input if the `keepdims` argument equals 1 (default). If `keepdims` is set to 0, then the `axes` dimensions are pruned.
226 
227 Github Links:
228 - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/reduce_ops.cc
229 
230 <details>
231 
232 <summary> <b>Example</b> </summary>
233 
234 **Code**
235 
236 ```
237 
238 workspace.ResetWorkspace()
239 
240 op = core.CreateOperator(
241  "ReduceMean",
242  ["X"],
243  ["Y"],
244  axes=(0,1),
245  keepdims=0
246 )
247 
248 workspace.FeedBlob("X", np.random.randint(10, size=(1,2,5,5)).astype(np.float32))
249 print("X:", workspace.FetchBlob("X"))
250 workspace.RunOperatorOnce(op)
251 print("Y:", workspace.FetchBlob("Y"))
252 
253 ```
254 
255 **Result**
256 
257 ```
258 
259 X:
260 [[[[9. 0. 3. 6. 0.]
261  [3. 4. 5. 0. 9.]
262  [6. 9. 1. 1. 5.]
263  [6. 2. 3. 7. 7.]
264  [3. 1. 1. 0. 1.]]
265 
266  [[4. 3. 9. 8. 1.]
267  [8. 2. 0. 4. 0.]
268  [8. 9. 9. 0. 2.]
269  [7. 2. 5. 8. 9.]
270  [5. 9. 1. 9. 0.]]]]
271 Y:
272 [[6.5 1.5 6. 7. 0.5]
273  [5.5 3. 2.5 2. 4.5]
274  [7. 9. 5. 0.5 3.5]
275  [6.5 2. 4. 7.5 8. ]
276  [4. 5. 1. 4.5 0.5]]
277 
278 ```
279 
280 </details>
281 
282 
283 )DOC")
284  .Arg("axes", "(*Tuple(int)*): list of axes to reduce")
285  .Arg(
286  "keepdims",
287  "(*int*): set to 1 to keep the reduced dimension(s) (default=1), else set to 0 to not keep the reduced dimension(s)")
288  .Input(0, "X", "(*Tensor`<float>`*): input tensor")
289  .Output(0, "Y", "(*Tensor`<float>`*): reduced tensor");
290 
291 OPERATOR_SCHEMA(ReduceMeanGradient).NumInputs(3).NumOutputs(1);
292 
293 template <>
294 template <typename T>
295 bool L1Reducer<CPUContext>::Backward(
296  const std::vector<int>& dY_dims,
297  const std::vector<int>& dX_dims,
298  const T* dY_data,
299  const T* X_data,
300  const T* /* Y_data */,
301  T* dX_data,
302  CPUContext* /* context */) const {
303  const float kEps = 1e-12f;
304  const int dX_size = std::accumulate(
305  dX_dims.cbegin(), dX_dims.cend(), 1, std::multiplies<int>());
306  const int ndim = dX_dims.size();
307  std::vector<int> index(ndim, 0);
308  for (int dX_index = 0; dX_index < dX_size; ++dX_index) {
309  const int dY_index =
310  math::utils::GetIndexFromDims(ndim, dY_dims.data(), index.data());
311  float temp = X_data[dX_index];
312  if (temp < -kEps) {
313  dX_data[dX_index] = -dY_data[dY_index];
314  } else if (temp > kEps) {
315  dX_data[dX_index] = dY_data[dY_index];
316  } else {
317  dX_data[dX_index] = T(0);
318  }
319  math::utils::IncreaseIndexInDims(ndim, dX_dims.data(), index.data());
320  }
321  return true;
322 }
323 
324 template <>
325 template <typename T>
326 bool L2Reducer<CPUContext>::Backward(
327  const std::vector<int>& dY_dims,
328  const std::vector<int>& dX_dims,
329  const T* dY_data,
330  const T* X_data,
331  const T* Y_data,
332  T* dX_data,
333  CPUContext* /* context */) const {
334  const float kEps = 1e-12f;
335  const int dX_size = std::accumulate(
336  dX_dims.cbegin(), dX_dims.cend(), 1, std::multiplies<int>());
337  const int ndim = dX_dims.size();
338  std::vector<int> index(ndim, 0);
339  for (int dX_index = 0; dX_index < dX_size; ++dX_index) {
340  const int dY_index =
341  math::utils::GetIndexFromDims(ndim, dY_dims.data(), index.data());
342  T norm = Y_data[dY_index];
343  if (norm < kEps) {
344  dX_data[dX_index] = dY_data[dY_index];
345  } else {
346  dX_data[dX_index] = dY_data[dY_index] * X_data[dX_index] / norm;
347  }
348  math::utils::IncreaseIndexInDims(ndim, dX_dims.data(), index.data());
349  }
350  return true;
351 }
352 
353 REGISTER_CPU_OPERATOR(
354  ReduceL1,
355  ReduceOp<TensorTypes<float>, CPUContext, L1Reducer<CPUContext>>);
356 REGISTER_CPU_OPERATOR(
357  ReduceL1Gradient,
358  ReduceGradientOp<TensorTypes<float>, CPUContext, L1Reducer<CPUContext>>);
359 
360 OPERATOR_SCHEMA(ReduceL1)
361  .NumInputs(1)
362  .NumOutputs(1)
363  .SetDoc(R"DOC(
364 Computes the **L1 norm** of the input tensor's elements along the provided `axes`. The resulting tensor has the same rank as the input if the `keepdims` argument equals 1 (default). If `keepdims` is set to 0, then the `axes` dimensions are pruned.
365 
366 Github Links:
367 - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/reduce_ops.cc
368 
369 <details>
370 
371 <summary> <b>Example</b> </summary>
372 
373 **Code**
374 
375 ```
376 
377 workspace.ResetWorkspace()
378 
379 op = core.CreateOperator(
380  "ReduceL1",
381  ["X"],
382  ["Y"],
383  axes=(0,1),
384  keepdims=0
385 )
386 
387 workspace.FeedBlob("X", np.random.randint(10, size=(1,2,5,5)).astype(np.float32))
388 print("X:", workspace.FetchBlob("X"))
389 workspace.RunOperatorOnce(op)
390 print("Y:", workspace.FetchBlob("Y"))
391 
392 ```
393 
394 **Result**
395 
396 ```
397 
398 X:
399 [[[[ 2. 7. 6. 4. 5.]
400  [ 2. 1. 9. 8. 7.]
401  [ 4. 9. 1. 0. 0.]
402  [ 6. 4. 0. 8. 1.]
403  [ 1. 7. 1. 0. 2.]]
404 
405  [[ 5. 8. 1. 7. 7.]
406  [ 4. 5. 6. 5. 4.]
407  [ 1. 9. 6. 6. 3.]
408  [ 6. 6. 8. 8. 4.]
409  [ 2. 3. 5. 8. 1.]]]]
410 
411 Y:
412 [[ 7. 15. 7. 11. 12.]
413  [ 6. 6. 15. 13. 11.]
414  [ 5. 18. 7. 6. 3.]
415  [ 12. 10. 8. 16. 5.]
416  [ 3. 10. 6. 8. 3.]]
417 
418 ```
419 
420 </details>
421 
422 
423 )DOC")
424  .Arg("axes", "(*Tuple(int)*): list of axes to reduce")
425  .Arg(
426  "keepdims",
427  "(*int*): set to 1 to keep the reduced dimension(s) (default=1), else set to 0 to not keep the reduced dimension(s)")
428  .Input(0, "X", "(*Tensor`<float>`*): input tensor")
429  .Output(0, "Y", "(*Tensor`<float>`*): reduced tensor");
430 
431 OPERATOR_SCHEMA(ReduceL1Gradient).NumInputs(3).NumOutputs(1);
432 
433 REGISTER_CPU_OPERATOR(
434  ReduceL2,
435  ReduceOp<TensorTypes<float>, CPUContext, L2Reducer<CPUContext>>);
436 REGISTER_CPU_OPERATOR(
437  ReduceL2Gradient,
438  ReduceGradientOp<TensorTypes<float>, CPUContext, L2Reducer<CPUContext>>);
439 
440 OPERATOR_SCHEMA(ReduceL2)
441  .NumInputs(1)
442  .NumOutputs(1)
443  .SetDoc(R"DOC(
444 Computes the **L2 norm** of the input tensor's elements along the provided `axes`. The resulting tensor has the same rank as the input if the `keepdims` argument equals 1 (default). If `keepdims` is set to 0, then the `axes` dimensions are pruned.
445 
446 Github Links:
447 - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/reduce_ops.cc
448 
449 <details>
450 
451 <summary> <b>Example</b> </summary>
452 
453 **Code**
454 
455 ```
456 
457 workspace.ResetWorkspace()
458 
459 op = core.CreateOperator(
460  "ReduceL2",
461  ["X"],
462  ["Y"],
463  axes=(0,1),
464  keepdims=0
465 )
466 
467 workspace.FeedBlob("X", np.random.randint(10, size=(1,2,5,5)).astype(np.float32))
468 print("X:", workspace.FetchBlob("X"))
469 workspace.RunOperatorOnce(op)
470 print("Y:", workspace.FetchBlob("Y"))
471 
472 ```
473 
474 **Result**
475 
476 ```
477 
478 X:
479 [[[[ 8. 0. 2. 5. 1.]
480  [ 1. 3. 0. 4. 0.]
481  [ 1. 3. 6. 7. 7.]
482  [ 6. 9. 8. 4. 6.]
483  [ 6. 1. 5. 7. 3.]]
484 
485  [[ 2. 4. 6. 2. 8.]
486  [ 1. 1. 8. 0. 8.]
487  [ 5. 9. 0. 3. 2.]
488  [ 1. 7. 3. 7. 3.]
489  [ 6. 8. 9. 8. 7.]]]]
490 
491 Y:
492 [[ 8.24621105 4. 6.3245554 5.38516474 8.06225777]
493  [ 1.41421354 3.1622777 8. 4. 8. ]
494  [ 5.09901953 9.48683262 6. 7.6157732 7.28010988]
495  [ 6.08276272 11.40175438 8.54400349 8.06225777 6.70820379]
496  [ 8.48528099 8.06225777 10.29563046 10.63014603 7.6157732 ]]
497 
498 ```
499 
500 </details>
501 
502 
503 )DOC")
504  .Arg("axes", "(*Tuple(int)*): list of axes to reduce")
505  .Arg(
506  "keepdims",
507  "(*int*): set to 1 to keep the reduced dimension(s) (default=1), else set to 0 to not keep the reduced dimension(s)")
508  .Input(0, "X", "(*Tensor`<float>`*): input tensor")
509  .Output(0, "Y", "(*Tensor`<float>`*): reduced tensor")
510  .InheritOnnxSchema("ReduceMean");
511 
512 OPERATOR_SCHEMA(ReduceL2Gradient).NumInputs(3).NumOutputs(1);
513 
514 namespace {
515 
516 class GetReduceGradient final : public GradientMakerBase {
517  using GradientMakerBase::GradientMakerBase;
518 
519  std::vector<OperatorDef> GetGradientDefs() override {
520  return SingleGradientDef(
521  def_.type() + "Gradient",
522  "",
523  std::vector<string>{GO(0), I(0), O(0)},
524  std::vector<string>{GI(0)});
525  }
526 };
527 
528 } // namespace
529 
530 REGISTER_GRADIENT(ReduceMin, GetReduceGradient);
531 REGISTER_GRADIENT(ReduceMax, GetReduceGradient);
532 REGISTER_GRADIENT(ReduceSum, GetReduceGradient);
533 REGISTER_GRADIENT(ReduceMean, GetReduceGradient);
534 REGISTER_GRADIENT(ReduceL1, GetReduceGradient);
535 REGISTER_GRADIENT(ReduceL2, GetReduceGradient);
536 
537 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13