Caffe2 - C++ API
A deep learning, cross platform ML framework
reduce_front_back_mean_ops.cc
1 #include "caffe2/core/operator_gradient.h"
2 #include "caffe2/operators/reduce_front_back_sum_mean_ops.h"
3 
4 namespace caffe2 {
5 
6 #define REDUCTION_OP_SHAPE_INFERENCE(is_front_reducer) \
7  CAFFE_ENFORCE_LE(1, in.size()); \
8  CAFFE_ENFORCE_GE(2, in.size()); \
9  ArgumentHelper helper(def); \
10  int num_reduce_dims = helper.GetSingleArgument<int>("num_reduce_dim", 1); \
11  int start_index = is_front_reducer ? num_reduce_dims : 0; \
12  int end_index = is_front_reducer ? in[0].dims_size() \
13  : in[0].dims_size() - num_reduce_dims; \
14  vector<int> output_shape; \
15  for (int i = start_index; i < end_index; ++i) { \
16  output_shape.push_back(in[0].dims(i)); \
17  } \
18  return vector<TensorShape>{ \
19  CreateTensorShape(output_shape, in[0].data_type())};
20 
21 /***
22  Mean Ops
23 ***/
24 
25 // ReduceFrontMean: columnwise mean
26 template <>
27 template <typename T>
28 void SumReduceDimsOp<CPUContext, true, true>::Compute(
29  int rows,
30  int cols,
31  const T* in_data,
32  const int32_t* lengths_data,
33  T* out_data) {
34  for (int j = 0; j < cols; j++) {
35  T sum = in_data[j];
36  int length = lengths_data == nullptr ? rows : lengths_data[j];
37  for (int i = 1; i < length; i++) {
38  sum += in_data[i * cols + j];
39  }
40  out_data[j] = sum / length;
41  }
42 }
43 
44 // ReduceBackMean: rowwise mean
45 template <>
46 template <typename T>
47 void SumReduceDimsOp<CPUContext, false, true>::Compute(
48  int rows,
49  int cols,
50  const T* in_data,
51  const int32_t* lengths_data,
52  T* out_data) {
53  for (int i = 0; i < rows; i++) {
54  int offset = i * cols;
55  T sum = in_data[offset];
56  int length = lengths_data == nullptr ? cols : lengths_data[i];
57  for (int j = 1; j < length; j++) {
58  sum += in_data[offset + j];
59  }
60  out_data[i] = sum / length;
61  }
62 }
63 
64 // ReduceFrontMeanGradient
65 template <>
66 template <typename T>
67 void SumReduceDimsGradientOp<CPUContext, true, true>::Compute(
68  int rows,
69  int cols,
70  const T* dYdata,
71  const int* lengths_data,
72  T* dXdata) {
73  for (int i = 0; i < rows * cols; i++) {
74  int row = i / cols;
75  int col = i % cols;
76  if (lengths_data == nullptr) {
77  dXdata[i] = dYdata[col] / rows;
78  } else if (row < lengths_data[col]) {
79  dXdata[i] = dYdata[col] / lengths_data[col];
80  } else {
81  dXdata[i] = 0;
82  }
83  }
84 }
85 
86 // ReduceBackMeanGradient
87 template <>
88 template <typename T>
89 void SumReduceDimsGradientOp<CPUContext, false, true>::Compute(
90  int rows,
91  int cols,
92  const T* dYdata,
93  const int* lengths_data,
94  T* dXdata) {
95  for (int i = 0; i < rows * cols; i++) {
96  int row = i / cols;
97  int col = i % cols;
98  if (lengths_data == nullptr) {
99  dXdata[i] = dYdata[row] / cols;
100  } else if (col < lengths_data[row]) {
101  dXdata[i] = dYdata[row] / lengths_data[row];
102  } else {
103  dXdata[i] = 0;
104  }
105  }
106 }
107 
108 REGISTER_CPU_OPERATOR(ReduceFrontMean, SumReduceDimsOp<CPUContext, true, true>);
109 REGISTER_CPU_OPERATOR(
110  ReduceFrontMeanGradient,
111  SumReduceDimsGradientOp<CPUContext, true, true>);
112 
114  using GradientMakerBase::GradientMakerBase;
115  vector<OperatorDef> GetGradientDefs() override {
116  vector<string> grad_in = {GO(0), I(0)};
117  if (def_.input_size() == 2) {
118  grad_in.push_back(I(1));
119  }
120  return SingleGradientDef(
121  "ReduceFrontMeanGradient", "", grad_in, vector<string>{GI(0)});
122  }
123 };
124 
125 REGISTER_GRADIENT(ReduceFrontMean, GetReduceFrontMeanGradient);
126 
127 OPERATOR_SCHEMA(ReduceFrontMean)
128  .NumInputs(1, 2)
129  .NumOutputs(1)
130  .Arg(
131  "num_reduce_dims",
132  "(*int*): number of dimensions to reduce (default=1)")
133  .SetDoc(R"DOC(
134 Reduces the input tensor along the last dimension of the by applying **mean**.
135 
136 Can reduce more than one of the "first" dimensions by setting `num_reduce_dim`.
137 
138 A second (optional) input, `lengths`, can be passed, which enforces that only a subset of the elements are considered in the mean operation.
139 - If input tensor `X` has shape $(d_0, d_1, d_2, ..., d_n)$, `lengths` must have shape $(d_1 * d_2 * ... * d_{n})$.
140 - The values of the `lengths` tensor determine how many of the values to consider for each vector in the $d_{0}$ dimension.
141 
142 For example if $X = [[1,5,2,9],[4,1,8,2],[2,7,0,3]]$ and $lengths = [2,3,1,2]$, then $Y = [mean(1,4), mean(5,1,7), mean(2), mean(9,2)] = [2.5, 4.333, 2, 5.5]$
143 
144 Github Links:
145 - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/reduce_front_back_mean_ops.cc
146 
147 <details>
148 
149 <summary> <b>Example</b> </summary>
150 
151 **Code**
152 
153 ```
154 
155 workspace.ResetWorkspace()
156 
157 op = core.CreateOperator(
158  "ReduceFrontMean",
159  ["X"],
160  ["Y"],
161  num_reduce_dim=2
162 )
163 
164 workspace.FeedBlob("X", np.random.randint(10, size=(2,3,3)).astype(np.float32))
165 print("X:", workspace.FetchBlob("X"))
166 workspace.RunOperatorOnce(op)
167 print("Y:", workspace.FetchBlob("Y"))
168 
169 ```
170 
171 **Result**
172 
173 ```
174 
175 X:
176 [[[5. 0. 9.]
177  [4. 1. 1.]
178  [9. 0. 8.]]
179 
180  [[2. 6. 7.]
181  [6. 2. 6.]
182  [0. 4. 5.]]]
183 Y: [4.3333335 2.1666667 6.]
184 
185 ```
186 
187 </details>
188 
189 )DOC")
190  .Input(0, "X", "(*Tensor`<float>`*): input tensor")
191  .Input(1, "lengths", "(*Tensor`<int>`*): number of elements in each sample")
192  .Output(0, "Y", "(*Tensor`<float>`*): reduced tensor")
193  .TensorInferenceFunction([](const OperatorDef& def,
194  const vector<TensorShape>& in) {
195  REDUCTION_OP_SHAPE_INFERENCE(true)
196  })
197  .InheritOnnxSchema("ReduceMean");
198 OPERATOR_SCHEMA(ReduceFrontMeanGradient).NumInputs(2, 3).NumOutputs(1);
199 
200 REGISTER_CPU_OPERATOR(ReduceBackMean, SumReduceDimsOp<CPUContext, false, true>);
201 REGISTER_CPU_OPERATOR(
202  ReduceBackMeanGradient,
204 
205 class GetReduceBackMeanGradient : public GradientMakerBase {
206  using GradientMakerBase::GradientMakerBase;
207  vector<OperatorDef> GetGradientDefs() override {
208  vector<string> grad_in = {GO(0), I(0)};
209  if (def_.input_size() == 2) {
210  grad_in.push_back(I(1));
211  }
212  return SingleGradientDef(
213  "ReduceBackMeanGradient", "", grad_in, vector<string>{GI(0)});
214  }
215 };
216 
217 REGISTER_GRADIENT(ReduceBackMean, GetReduceBackMeanGradient);
218 
219 OPERATOR_SCHEMA(ReduceBackMean)
220  .NumInputs(1, 2)
221  .NumOutputs(1)
222  .Arg(
223  "num_reduce_dims",
224  "(*int*): number of dimensions to reduce (default=1)")
225  .SetDoc(R"DOC(
226 Reduces the input tensor along the last dimension of the by applying **mean**.
227 
228 Can reduce more than one of the "last" dimensions by setting `num_reduce_dim`.
229 
230 A second (optional) input, `lengths`, can be passed, which enforces that only a subset of the elements are considered in the mean operation.
231 - If input tensor `X` has shape $(d_0, d_1, d_2, ..., d_n)$, `lengths` must have shape $(d_0 * d_1 * d_2 * ... * d_{n-1})$.
232 - The values of the `lengths` tensor determine how many of the values to consider for each vector in the $d_{n-1}$ dimension.
233 
234 For example if $X = [[1,5,2,9],[4,1,8,2],[2,7,0,3]]$ and $lengths = [2,3,1]$, then $Y = [mean(1,5), mean(4,1,8), mean(2)] = [3, 4.333, 2]$
235 
236 
237 Github Links:
238 - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/reduce_front_back_mean_ops.cc
239 
240 <details>
241 
242 <summary> <b>Example</b> </summary>
243 
244 **Code**
245 
246 ```
247 
248 workspace.ResetWorkspace()
249 
250 op = core.CreateOperator(
251  "ReduceBackMean",
252  ["X"],
253  ["Y"],
254  num_reduce_dim=2
255 )
256 
257 workspace.FeedBlob("X", np.random.randint(10, size=(1,2,3,3)).astype(np.float32))
258 print("X:", workspace.FetchBlob("X"))
259 workspace.RunOperatorOnce(op)
260 print("Y:", workspace.FetchBlob("Y"))
261 
262 ```
263 
264 **Result**
265 
266 ```
267 
268 X:
269 [[[[5. 9. 0.]
270  [8. 4. 0.]
271  [2. 2. 4.]]
272 
273  [[9. 0. 9.]
274  [7. 9. 7.]
275  [1. 0. 2.]]]]
276 Y: [[3.7777777 4.888889 ]]
277 
278 ```
279 
280 </details>
281 
282 )DOC")
283  .Input(0, "X", "(*Tensor`<float>`*): input tensor")
284  .Input(1, "lengths", "(*Tensor`<int>`*): number of elements in each sample")
285  .Output(0, "Y", "(*Tensor`<float>`*): reduced tensor")
286  .TensorInferenceFunction([](const OperatorDef& def,
287  const vector<TensorShape>& in) {
288  REDUCTION_OP_SHAPE_INFERENCE(false)
289  })
290  .InheritOnnxSchema("ReduceMean");
291 OPERATOR_SCHEMA(ReduceBackMeanGradient).NumInputs(2, 3).NumOutputs(1);
292 
293 #undef REDUCTION_OP_SHAPE_INFERENCE
294 
295 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
static vector< OperatorDef > SingleGradientDef(const Args &...args)
a helper function to allow one to create one single operator def, which is usually the case for many ...