| #include "caffe2/operators/locally_connected_op.h" |
| |
| #include <functional> |
| #include <vector> |
| |
| #include "caffe2/operators/locally_connected_op_impl.h" |
| |
| namespace caffe2 { |
| |
| namespace { |
| |
| // NOLINTNEXTLINE(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) |
| constexpr char kLCDoc[] = R"DOC( |
| Note that other parameters, such as the stride and |
| kernel size, or the pads' sizes in each direction are not necessary for input |
| because they are provided by the ConvPoolOpBase operator. Various dimension |
| checks are done implicitly, and the sizes are specified in the Input docs for |
| this operator. As is expected, the filter is locally connected with a subset of |
| the image and the bias is added; this is done throughout the image data and the |
| output is computed. As a side note on the implementation layout: |
| locally_connected_op_impl.h is the templated implementation of the |
| locally_connected_op.h file, which is why they are separate files. |
| )DOC"; |
| |
| std::function<void(OpSchema&)> LCDocGenerator(const char* dim) { |
| return [dim](OpSchema& schema) { |
| string doc = R"DOC( |
| The locally connected operator consumes an input vector, a {dim}filter blob |
| and a bias blob and computes the output. {lc_doc})DOC"; |
| c10::ReplaceAll(doc, "{dim}", dim); |
| c10::ReplaceAll(doc, "{lc_doc}", kLCDoc); |
| schema.SetDoc(doc); |
| schema.Input( |
| 1, |
| "filter", |
| "The filter blob that will be used in the locally connected op; " |
| "has size (YH * YW * M x C x kH x kW) if order == NCHW else " |
| "(YH * YW * M * KH * KW * C), where YH and YW are the height " |
| "and width of the output image, C is the number of channels, and kH " |
| "and kW are the height and width of the kernel."); |
| schema.Input( |
| 2, |
| "bias", |
| "The 1D bias blob that is added through the locally connected op; " |
| "has size (YH * YW * M)."); |
| schema.Output( |
| 0, |
| "Y", |
| "Output data blob that contains the result of the locally connected op." |
| "The output dimensions are functions of the kernel size, stride size, " |
| "and pad lengths." |
| ""); |
| }; |
| } |
| |
| } // namespace |
| |
| REGISTER_CPU_OPERATOR(LC, LocallyConnectedOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LC) |
| .NumInputs(2, 3) |
| .NumOutputs(1) |
| .TensorInferenceFunction(ConvPoolOpBase<CPUContext>::TensorInferenceForLC) |
| .FillUsing(LCDocGenerator("")); |
| |
| REGISTER_CPU_OPERATOR(LC1D, LocallyConnectedOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LC1D) |
| .NumInputs(2, 3) |
| .NumOutputs(1) |
| .TensorInferenceFunction(ConvPoolOpBase<CPUContext>::TensorInferenceForLC) |
| .FillUsing(LCDocGenerator("1D ")); |
| |
| REGISTER_CPU_OPERATOR(LC2D, LocallyConnectedOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LC2D) |
| .NumInputs(2, 3) |
| .NumOutputs(1) |
| .TensorInferenceFunction(ConvPoolOpBase<CPUContext>::TensorInferenceForLC) |
| .FillUsing(LCDocGenerator("2D ")); |
| |
| REGISTER_CPU_OPERATOR(LC3D, LocallyConnectedOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LC3D) |
| .NumInputs(2, 3) |
| .NumOutputs(1) |
| .TensorInferenceFunction(ConvPoolOpBase<CPUContext>::TensorInferenceForLC) |
| .FillUsing(LCDocGenerator("3D ")); |
| |
| REGISTER_CPU_OPERATOR( |
| LCGradient, |
| LocallyConnectedGradientOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LCGradient).NumInputs(2, 3).NumOutputs(1, 3); |
| |
| REGISTER_CPU_OPERATOR( |
| LC1DGradient, |
| LocallyConnectedGradientOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LC1DGradient).NumInputs(2, 3).NumOutputs(1, 3); |
| |
| REGISTER_CPU_OPERATOR( |
| LC2DGradient, |
| LocallyConnectedGradientOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LC2DGradient).NumInputs(2, 3).NumOutputs(1, 3); |
| |
| REGISTER_CPU_OPERATOR( |
| LC3DGradient, |
| LocallyConnectedGradientOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LC3DGradient).NumInputs(2, 3).NumOutputs(1, 3); |
| |
| namespace { |
| |
| class GetLocallyConnectedGradient : public GradientMakerBase { |
| using GradientMakerBase::GradientMakerBase; |
| |
| std::vector<OperatorDef> GetGradientDefs() override { |
| CAFFE_ENFORCE(def_.input_size() == 3 || def_.input_size() == 2); |
| ArgumentHelper argsHelper(def_); |
| const bool compute_dX = |
| // NOLINTNEXTLINE(modernize-use-bool-literals) |
| !argsHelper.GetSingleArgument<bool>("no_gradient_to_input", 0); |
| |
| if (def_.input_size() == 3) { |
| if (compute_dX) { |
| return SingleGradientDef( |
| def_.type() + "Gradient", |
| "", |
| std::vector<string>{I(0), I(1), GO(0)}, |
| std::vector<string>{GI(1), GI(2), GI(0)}); |
| } else { |
| return SingleGradientDef( |
| def_.type() + "Gradient", |
| "", |
| std::vector<string>{I(0), I(1), GO(0)}, |
| std::vector<string>{GI(1), GI(2)}); |
| } |
| } else { |
| if (compute_dX) { |
| return SingleGradientDef( |
| def_.type() + "Gradient", |
| "", |
| std::vector<string>{I(0), I(1), GO(0)}, |
| std::vector<string>{GI(1), GI(0)}, |
| std::vector<Argument>{MakeArgument<int>("no_bias", 1)}); |
| } else { |
| return SingleGradientDef( |
| def_.type() + "Gradient", |
| "", |
| std::vector<string>{I(0), I(1), GO(0)}, |
| std::vector<string>{GI(1)}, |
| std::vector<Argument>{MakeArgument<int>("no_bias", 1)}); |
| } |
| } |
| } |
| }; |
| |
| } // namespace |
| |
| REGISTER_GRADIENT(LC, GetLocallyConnectedGradient); |
| REGISTER_GRADIENT(LC1D, GetLocallyConnectedGradient); |
| REGISTER_GRADIENT(LC2D, GetLocallyConnectedGradient); |
| REGISTER_GRADIENT(LC3D, GetLocallyConnectedGradient); |
| |
| } // namespace caffe2 |