Caffe2 - C++ API
A deep learning, cross platform ML framework
locally_connected_op.cc
1 
17 #include <functional>
18 #include <vector>
19 
20 #include "caffe2/operators/locally_connected_op.h"
21 #include "caffe2/operators/locally_connected_op_impl.h"
22 
23 namespace caffe2 {
24 
25 namespace {
26 
27 constexpr char kLCDoc[] = R"DOC(
28 Note that other parameters, such as the stride and
29 kernel size, or the pads' sizes in each direction are not necessary for input
30 because they are provided by the ConvPoolOpBase operator. Various dimension
31 checks are done implicitly, and the sizes are specified in the Input docs for
32 this operator. As is expected, the filter is locally connected with a subset of
33 the image and the bias is added; this is done throughout the image data and the
34 output is computed. As a side note on the implementation layout:
35 locally_connected_op_impl.h is the templated implementation of the
36 locally_connected_op.h file, which is why they are separate files.
37 )DOC";
38 
39 std::function<void(OpSchema&)> LCDocGenerator(const char* dim) {
40  return [dim](OpSchema& schema) {
41  string doc = R"DOC(
42 The locally connected operator consumes an input vector, a {dim}filter blob
43 and a bias blob and computes the output. {lc_doc})DOC";
44  ReplaceAll(doc, "{dim}", dim);
45  ReplaceAll(doc, "{lc_doc}", kLCDoc);
46  schema.SetDoc(doc);
47  schema.Input(
48  1,
49  "filter",
50  "The filter blob that will be used in the locally connected op; "
51  "has size (YH * YW * M x C x kH x kW), where YH and YW are the height "
52  "and width of the output image, C is the number of channels, and kH "
53  "and kW are the height and width of the kernel.");
54  schema.Input(
55  2,
56  "bias",
57  "The 1D bias blob that is added through the locally connected op; "
58  "has size (YH * YW * M).");
59  schema.Output(
60  0,
61  "Y",
62  "Output data blob that contains the result of the locally connected op."
63  "The output dimensions are functions of the kernel size, stride size, "
64  "and pad lengths."
65  "");
66  };
67 }
68 
69 } // namespace
70 
71 REGISTER_CPU_OPERATOR(LC, LocallyConnectedOp<float, CPUContext>);
72 
73 OPERATOR_SCHEMA(LC)
74  .NumInputs(2, 3)
75  .NumOutputs(1)
76  .TensorInferenceFunction(ConvPoolOpBase<CPUContext>::TensorInferenceForConv)
77  .FillUsing(LCDocGenerator(""));
78 
79 REGISTER_CPU_OPERATOR(LC1D, LocallyConnectedOp<float, CPUContext>);
80 
81 OPERATOR_SCHEMA(LC1D)
82  .NumInputs(2, 3)
83  .NumOutputs(1)
84  .TensorInferenceFunction(ConvPoolOpBase<CPUContext>::TensorInferenceForConv)
85  .FillUsing(LCDocGenerator("1D "));
86 
87 REGISTER_CPU_OPERATOR(LC2D, LocallyConnectedOp<float, CPUContext>);
88 
89 OPERATOR_SCHEMA(LC2D)
90  .NumInputs(2, 3)
91  .NumOutputs(1)
92  .TensorInferenceFunction(ConvPoolOpBase<CPUContext>::TensorInferenceForConv)
93  .FillUsing(LCDocGenerator("2D "));
94 
95 REGISTER_CPU_OPERATOR(LC3D, LocallyConnectedOp<float, CPUContext>);
96 
97 OPERATOR_SCHEMA(LC3D)
98  .NumInputs(2, 3)
99  .NumOutputs(1)
100  .TensorInferenceFunction(ConvPoolOpBase<CPUContext>::TensorInferenceForConv)
101  .FillUsing(LCDocGenerator("3D "));
102 
103 REGISTER_CPU_OPERATOR(
104  LCGradient,
105  LocallyConnectedGradientOp<float, CPUContext>);
106 
107 OPERATOR_SCHEMA(LCGradient).NumInputs(2, 3).NumOutputs(1, 3);
108 
109 REGISTER_CPU_OPERATOR(
110  LC1DGradient,
111  LocallyConnectedGradientOp<float, CPUContext>);
112 
113 OPERATOR_SCHEMA(LC1DGradient).NumInputs(2, 3).NumOutputs(1, 3);
114 
115 REGISTER_CPU_OPERATOR(
116  LC2DGradient,
117  LocallyConnectedGradientOp<float, CPUContext>);
118 
119 OPERATOR_SCHEMA(LC2DGradient).NumInputs(2, 3).NumOutputs(1, 3);
120 
121 REGISTER_CPU_OPERATOR(
122  LC3DGradient,
123  LocallyConnectedGradientOp<float, CPUContext>);
124 
125 OPERATOR_SCHEMA(LC3DGradient).NumInputs(2, 3).NumOutputs(1, 3);
126 
127 namespace {
128 
129 class GetLocallyConnectedGradient : public GradientMakerBase {
130  using GradientMakerBase::GradientMakerBase;
131 
132  std::vector<OperatorDef> GetGradientDefs() override {
133  CAFFE_ENFORCE(def_.input_size() == 3 || def_.input_size() == 2);
134  ArgumentHelper argsHelper(def_);
135  const bool compute_dX =
136  !argsHelper.GetSingleArgument<bool>("no_gradient_to_input", 0);
137 
138  if (def_.input_size() == 3) {
139  if (compute_dX) {
140  return SingleGradientDef(
141  def_.type() + "Gradient",
142  "",
143  std::vector<string>{I(0), I(1), GO(0)},
144  std::vector<string>{GI(1), GI(2), GI(0)});
145  } else {
146  return SingleGradientDef(
147  def_.type() + "Gradient",
148  "",
149  std::vector<string>{I(0), I(1), GO(0)},
150  std::vector<string>{GI(1), GI(2)});
151  }
152  } else {
153  if (compute_dX) {
154  return SingleGradientDef(
155  def_.type() + "Gradient",
156  "",
157  std::vector<string>{I(0), I(1), GO(0)},
158  std::vector<string>{GI(1), GI(0)},
159  std::vector<Argument>{MakeArgument<int>("no_bias", 1)});
160  } else {
161  return SingleGradientDef(
162  def_.type() + "Gradient",
163  "",
164  std::vector<string>{I(0), I(1), GO(0)},
165  std::vector<string>{GI(1)},
166  std::vector<Argument>{MakeArgument<int>("no_bias", 1)});
167  }
168  }
169  }
170 };
171 
172 } // namespace
173 
174 REGISTER_GRADIENT(LC, GetLocallyConnectedGradient);
175 REGISTER_GRADIENT(LC1D, GetLocallyConnectedGradient);
176 REGISTER_GRADIENT(LC2D, GetLocallyConnectedGradient);
177 REGISTER_GRADIENT(LC3D, GetLocallyConnectedGradient);
178 
179 } // namespace caffe2
Copyright (c) 2016-present, Facebook, Inc.