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