Caffe2 - C++ API
A deep learning, cross platform ML framework
conv_gradient_op.cc
1 #include "caffe2/operators/conv_op.h"
2 #include "caffe2/operators/conv_op_impl.h"
3 #include "caffe2/operators/conv_pool_op_base.h"
4 
5 namespace caffe2 {
6 
7 std::vector<TensorShape> TensorInferenceForConvGradient(
8  const OperatorDef& def,
9  const std::vector<TensorShape>& in) {
10  CAFFE_ENFORCE_EQ(in.size(), 3, "ConvGradient requires 3 inputs");
11 
12  if (in[0].unknown_shape()) {
13  std::vector<TensorShape> out(1);
14  out[0].set_unknown_shape(true);
15  return out;
16  }
17  ArgumentHelper helper(def);
18  const auto no_bias = helper.GetSingleArgument<int>("no_bias", 0);
19  const auto n_outputs = def.output_size();
20  vector<TensorShape> out(n_outputs);
21 
22  // FILTER_GRAD has the same shape as FILTER
23  out[0] = in[1];
24  if (!no_bias) {
25  vector<int64_t> bias_shape = {in[1].dims(0)};
26  out[1] = CreateTensorShape(bias_shape, in[1].data_type());
27  }
28 
29  if (n_outputs == 3 || (no_bias && n_outputs == 2)) {
30  // INPUT_GRAD has the same shape as INPUT
31  out[out.size() - 1] = in[0];
32  }
33 
34  return out;
35 }
36 
37 OpSchema::Cost CostInferenceForConvGradient(
38  const OperatorDef& def,
39  const vector<TensorShape>& inputs) {
40  CAFFE_ENFORCE_EQ(inputs.size(), 3, "ConvGradient requires 3 inputs");
41  ArgumentHelper helper(def);
42  const auto order =
43  StringToStorageOrder(helper.GetSingleArgument<string>("order", "NCHW"));
44  const auto no_bias = helper.GetSingleArgument<int>("no_bias", 0);
45  const auto n_outputs = def.output_size();
46 
47  const auto& outputs = TensorInferenceForConvGradient(def, inputs);
48  const auto& X = inputs[0];
49  const auto& filter = inputs[1];
50  const auto& dY = inputs[2];
51  const auto N = X.dims(0);
52  const auto M = filter.dims(0);
53  const auto C =
54  (order == StorageOrder::NCHW ? X.dims(1) : X.dims(X.dims_size() - 1));
55  const auto output_image_size =
56  (order == StorageOrder::NCHW
57  ? nElemFromDim(dY, 2)
58  : nElemBetweenDim(dY, 1, dY.dims_size() - 1));
59  auto kernel_elem =
60  (order == StorageOrder::NCHW
61  ? nElemFromDim(filter, 2)
62  : nElemBetweenDim(filter, 1, filter.dims_size() - 1));
63 
64  struct OpSchema::Cost c;
65  c.flops = N * 2 * M * kernel_elem * C * output_image_size;
66  if (!no_bias) {
67  c.flops += N * (M * output_image_size);
68  }
69  if (n_outputs == 3 || (no_bias && n_outputs == 2)) {
70  c.flops += N * 2 * M * kernel_elem * C * output_image_size;
71  }
72 
73  c.bytes_read = (nElemFromDim(X) + nElemFromDim(filter) + nElemFromDim(dY)) *
74  sizeof(float);
75 
76  for (auto i = 0; i < n_outputs; i++) {
77  c.bytes_written += nElemFromDim(outputs[i]) * sizeof(float);
78  }
79  c.params_bytes = nElemFromDim(filter) * sizeof(float);
80 
81  return c;
82 }
83 
84 REGISTER_CPU_OPERATOR(ConvGradient, ConvGradientOp<float, CPUContext>);
85 OPERATOR_SCHEMA(ConvGradient)
86  .NumInputs(2, 3)
87  .NumOutputs(1, 3)
88  .TensorInferenceFunction(TensorInferenceForConvGradient)
89  .CostInferenceFunction(CostInferenceForConvGradient);
90 
91 REGISTER_CPU_OPERATOR(Conv1DGradient, ConvGradientOp<float, CPUContext>);
92 OPERATOR_SCHEMA(Conv1DGradient).NumInputs(2, 3).NumOutputs(1, 3);
93 
94 REGISTER_CPU_OPERATOR(Conv2DGradient, ConvGradientOp<float, CPUContext>);
95 OPERATOR_SCHEMA(Conv2DGradient).NumInputs(2, 3).NumOutputs(1, 3);
96 
97 REGISTER_CPU_OPERATOR(Conv3DGradient, ConvGradientOp<float, CPUContext>);
98 OPERATOR_SCHEMA(Conv3DGradient).NumInputs(2, 3).NumOutputs(1, 3);
99 
101  using GradientMakerBase::GradientMakerBase;
102  vector<OperatorDef> GetGradientDefs() override {
103  CAFFE_ENFORCE(def_.input_size() == 3 || def_.input_size() == 2);
104 
105  ArgumentHelper argsHelper(def_);
106 
107  auto compute_dX = !argsHelper.GetSingleArgument<bool>("no_gradient_to_input", 0);
108 
109  if (def_.input_size() == 3) {
110  if (compute_dX) {
111  return SingleGradientDef(
112  def_.type() + "Gradient",
113  "",
114  vector<string>{I(0), I(1), GO(0)},
115  vector<string>{GI(1), GI(2), GI(0)});
116  } else {
117  return SingleGradientDef(
118  def_.type() + "Gradient",
119  "",
120  vector<string>{I(0), I(1), GO(0)},
121  vector<string>{GI(1), GI(2)});
122  }
123  } else {
124  if (compute_dX) {
125  return SingleGradientDef(
126  def_.type() + "Gradient",
127  "",
128  vector<string>{I(0), I(1), GO(0)},
129  vector<string>{GI(1), GI(0)},
130  vector<Argument>{MakeArgument<int>("no_bias", 1)});
131  } else {
132  return SingleGradientDef(
133  def_.type() + "Gradient",
134  "",
135  vector<string>{I(0), I(1), GO(0)},
136  vector<string>{GI(1)},
137  vector<Argument>{MakeArgument<int>("no_bias", 1)});
138  }
139  }
140  }
141 };
142 REGISTER_GRADIENT(Conv, GetConvGradient);
143 REGISTER_GRADIENT(Conv1D, GetConvGradient);
144 REGISTER_GRADIENT(Conv2D, GetConvGradient);
145 REGISTER_GRADIENT(Conv3D, GetConvGradient);
146 
147 } // namespace caffe2
Definition: any.cpp:108
A helper class to index into arguments.
Definition: proto_utils.h:200
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 ...
Definition: OpClasses.h:13
Definition: static.cpp:64