Caffe2 - C++ API
A deep learning, cross platform ML framework
reduce_front_back_sum_mean_ops.h
1 #ifndef CAFFE2_OPERATORS_REDUCE_FRONT_BACK_SUM_MEAN_OPS_H_
2 #define CAFFE2_OPERATORS_REDUCE_FRONT_BACK_SUM_MEAN_OPS_H_
3 
4 #include "caffe2/core/context.h"
5 #include "caffe2/core/logging.h"
6 #include "caffe2/core/operator.h"
7 #include "caffe2/utils/math.h"
8 
9 namespace caffe2 {
10 
11 template <class Context, bool FIRSTDIMS, bool NORMALIZE>
12 class SumReduceDimsOp final : public Operator<Context> {
13  public:
14  template <class... Args>
15  explicit SumReduceDimsOp(Args&&... args)
16  : Operator<Context>(std::forward<Args>(args)...),
17  num_reduce_dims_(
18  this->template GetSingleArgument<int32_t>("num_reduce_dim", 1)) {}
19 
20  USE_OPERATOR_CONTEXT_FUNCTIONS;
21 
22  bool RunOnDevice() override {
24  this, Input(0));
25  }
26 
27  template <typename T>
28  bool DoRunWithType() {
29  auto& X = Input(0);
30 
31  CAFFE_ENFORCE(
32  num_reduce_dims_ >= 0 && num_reduce_dims_ <= X.dim(),
33  "For N-dim input tensor, support num_reduce_dims in range [0, N].");
34 
35  vector<int64_t> output_shape;
36  int start_index = FIRSTDIMS ? num_reduce_dims_ : 0;
37  int end_index =
38  FIRSTDIMS ? X.dim() : X.dim() - num_reduce_dims_;
39  for (int i = start_index; i < end_index; ++i) {
40  output_shape.push_back(X.sizes()[i]);
41  }
42  auto* Y = Output(0, output_shape, at::dtype<T>());
43 
44  const int rows = FIRSTDIMS ? X.size_to_dim(num_reduce_dims_)
45  : X.size_to_dim(X.dim() - num_reduce_dims_);
46  const int cols = FIRSTDIMS ? X.size_from_dim(num_reduce_dims_)
47  : X.size_from_dim(X.dim() - num_reduce_dims_);
48 
49  const T* in_data = X.template data<T>();
50  T* out_data = Y->template mutable_data<T>();
51 
52  if (cols == 0 || rows == 0) {
53  math::Set(Y->numel(), static_cast<T>(0), out_data, &context_);
54  return true;
55  }
56 
57  const int32_t* lengths_data = nullptr;
58  if (InputSize() > 1) {
59  const auto& lengths = Input(1);
60  lengths_data = lengths.template data<int32_t>();
61  CAFFE_ENFORCE(
62  num_reduce_dims_ == 1,
63  "Given lengths input, the number of reduce dimensions should be one.");
64  const int batch_size = FIRSTDIMS ? cols : rows;
65  CAFFE_ENFORCE(
66  lengths.numel() == batch_size,
67  "The size of lengths vector doesn't match the batch size.");
68  }
69 
70  Compute(rows, cols, in_data, lengths_data, out_data);
71 
72  return true;
73  }
74 
75  private:
76  template <typename T>
77  void Compute(
78  int rows,
79  int cols,
80  const T* in_data,
81  const int32_t* lengths_data,
82  T* out_data);
83 
84  int num_reduce_dims_;
85 };
86 
87 template <class Context, bool FIRSTDIMS, bool NORMALIZE>
88 class SumReduceDimsGradientOp final : public Operator<Context> {
89  public:
90  template <class... Args>
91  explicit SumReduceDimsGradientOp(Args&&... args)
92  : Operator<Context>(std::forward<Args>(args)...),
93  num_reduce_dims_(
94  this->template GetSingleArgument<int32_t>("num_reduce_dim", 1)) {}
95 
96  USE_OPERATOR_CONTEXT_FUNCTIONS;
97 
98  bool RunOnDevice() override {
100  this, Input(0));
101  }
102 
103  template <typename T>
104  bool DoRunWithType() {
105  auto& dY = Input(0);
106  auto& input_1 = Input(1);
107 
108  vector<int64_t> dX_sizes;
109  // In previous diff we changed the semantic: Input(1) was changed from
110  // the shape of the input to the data tensor. This made the backward
111  // computation incompatible with old models. To fix this, we check
112  // the dimension and type of Input(1).
113  if (input_1.dim() == 1 && input_1.template IsType<int64_t>()) {
114  // Input(1) is the shape of the input
115  shape_.CopyFrom(input_1);
116  // Copy first dims
117  dX_sizes = vector<int64_t>(
118  shape_.template data<int64_t>(),
119  shape_.template data<int64_t>() + shape_.numel());
120  } else {
121  // Input(1) is data tensor X
122  dX_sizes = input_1.sizes().vec();
123  }
124  auto* dX = Output(0, dX_sizes, at::dtype<T>());
125 
126  const int rows = FIRSTDIMS ? dX->size_to_dim(num_reduce_dims_)
127  : dX->size_to_dim(dX->dim() - num_reduce_dims_);
128  const int cols = FIRSTDIMS
129  ? dX->size_from_dim(num_reduce_dims_)
130  : dX->size_from_dim(dX->dim() - num_reduce_dims_);
131 
132  const int32_t* lengths_data = nullptr;
133  if (InputSize() > 2) {
134  const auto& lengths = Input(2);
135  lengths_data = lengths.template data<int32_t>();
136  CAFFE_ENFORCE(
137  num_reduce_dims_ == 1,
138  "Given lengths input, the number of reduce dimensions should be one.");
139  const int batch_size = FIRSTDIMS ? cols : rows;
140  CAFFE_ENFORCE(
141  lengths.numel() == batch_size,
142  "The size of lengths vector doesn't match the batch size.");
143  }
144 
145  const T* dYdata = dY.template data<T>();
146  T* dXdata = dX->template mutable_data<T>();
147  Compute<T>(rows, cols, dYdata, lengths_data, dXdata);
148  return true;
149  }
150 
151  private:
152  template <typename T>
153  void Compute(
154  int rows,
155  int cols,
156  const T* dYdata,
157  const int32_t* lengths_data,
158  T* dXdata);
159  int num_reduce_dims_;
160  // scratch space used for former version of this reducer
161  Tensor shape_{Context::GetDeviceType()};
162 };
163 
164 } // namespace caffe2
165 
166 #endif // CAFFE2_OPERATORS_REDUCE_FRONT_BACK_SUM_MEAN_OPS_H_
const Tensor & Input(int idx, DeviceType type=Context::GetDeviceType())
Retrieve a non-owning reference to the input at position &#39;idx&#39; for this operator. ...
Definition: operator.h:702
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13