fc_op.cc 6.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#include "paddle/fluid/operators/fc_op.h"
16
#include <vector>
T
tensor-tang 已提交
17
#include "paddle/fluid/operators/math/blas.h"
18
#include "paddle/fluid/operators/math/fc_compute.h"
T
tensor-tang 已提交
19

20 21 22 23 24 25 26 27 28 29
namespace paddle {
namespace operators {

void FCOp::InferShape(framework::InferShapeContext* ctx) const {
  PADDLE_ENFORCE(ctx->HasInput("Input"),
                 "X(Input) of Fully Connected should not be null.");
  PADDLE_ENFORCE(ctx->HasOutput("Out"),
                 "Out(Output) of Fully Connected should not be null.");
  PADDLE_ENFORCE(ctx->HasInput("W"),
                 "W(Input) of Fully Connected should not be null.");
T
Tao Luo 已提交
30

31 32 33
  auto in_dims = ctx->GetInputDim("Input");
  auto w_dims = ctx->GetInputDim("W");

T
tensor-tang 已提交
34 35
  if (ctx->HasInput("Bias")) {
    auto bias_dims = ctx->GetInputDim("Bias");
36 37 38 39 40 41 42 43
    if (bias_dims.size() == 2) {
      PADDLE_ENFORCE_EQ(bias_dims[0], 1, "The shape of Bias must be [1, dim].");
      PADDLE_ENFORCE_EQ(bias_dims[1], w_dims[1],
                        "The shape of Bias must be [1, dim].");
    } else if (bias_dims.size() == 1) {
      PADDLE_ENFORCE_EQ(bias_dims[0], w_dims[1],
                        "The shape of Bias must be [1, dim].");
    }
T
tensor-tang 已提交
44
  }
T
Tao Luo 已提交
45 46 47 48 49

  if (ctx->Attrs().Get<bool>("use_mkldnn")) {
    PADDLE_ENFORCE(in_dims.size() == 2 || in_dims.size() == 4,
                   "Fully Connected input should be 2-D or 4-D tensor.");
  }
T
tensor-tang 已提交
50
  PADDLE_ENFORCE_EQ(w_dims.size(), 2,
T
tensor-tang 已提交
51
                    "Fully Connected input should be 2-D tensor.");
T
Tao Luo 已提交
52 53 54 55 56 57 58
  int in_num_col_dims = ctx->Attrs().Get<int>("in_num_col_dims");
  PADDLE_ENFORCE_GT(
      in_dims.size(), in_num_col_dims,
      "The input tensor Input's rank of FCOp should be larger than "
      "in_num_col_dims.");

  std::vector<int64_t> output_dims;
L
luotao1 已提交
59
  FCOutputSize(in_dims, w_dims, output_dims, in_num_col_dims);
T
tensor-tang 已提交
60

T
Tao Luo 已提交
61
  ctx->SetOutputDim("Out", framework::make_ddim(output_dims));
62 63 64 65 66
  ctx->ShareLoD("Input", "Out");
}

framework::OpKernelType FCOp::GetExpectedKernelType(
    const framework::ExecutionContext& ctx) const {
T
tensor-tang 已提交
67 68
  framework::LibraryType library = framework::LibraryType::kPlain;
  framework::DataLayout layout = framework::DataLayout::kAnyLayout;
T
tensor-tang 已提交
69
  if (ctx.Attr<bool>("use_mkldnn")) {
T
tensor-tang 已提交
70 71 72
    library = framework::LibraryType::kMKLDNN;
    layout = framework::DataLayout::kMKLDNN;
  }
Y
Yu Yang 已提交
73 74
  return framework::OpKernelType(ctx.Input<Tensor>("Input")->type(),
                                 ctx.GetPlace(), layout, library);
75 76 77 78 79 80 81 82 83 84 85 86
}

void FCOpGrad::InferShape(framework::InferShapeContext* ctx) const {
  auto in_dims = ctx->GetInputDim("Input");
  auto w_dims = ctx->GetInputDim("W");

  if (ctx->HasOutput(framework::GradVarName("Input"))) {
    ctx->SetOutputDim(framework::GradVarName("Input"), in_dims);
  }
  if (ctx->HasOutput(framework::GradVarName("W"))) {
    ctx->SetOutputDim(framework::GradVarName("W"), w_dims);
  }
T
tensor-tang 已提交
87 88

  if (ctx->HasInput("Bias")) {
T
tensor-tang 已提交
89 90
    PADDLE_ENFORCE(ctx->HasOutput(framework::GradVarName("Bias")),
                   "Should have bias grad");
T
tensor-tang 已提交
91 92 93
    auto bias_dims = ctx->GetInputDim("Bias");
    ctx->SetOutputDim(framework::GradVarName("Bias"), bias_dims);
  }
94 95 96 97
}

framework::OpKernelType FCOpGrad::GetExpectedKernelType(
    const framework::ExecutionContext& ctx) const {
T
tensor-tang 已提交
98 99
  framework::LibraryType library = framework::LibraryType::kPlain;
  framework::DataLayout layout = framework::DataLayout::kAnyLayout;
T
tensor-tang 已提交
100
  if (ctx.Attr<bool>("use_mkldnn")) {
T
tensor-tang 已提交
101 102 103
    library = framework::LibraryType::kMKLDNN;
    layout = framework::DataLayout::kMKLDNN;
  }
Y
Yu Yang 已提交
104 105
  return framework::OpKernelType(ctx.Input<Tensor>("Input")->type(),
                                 ctx.GetPlace(), layout, library);
106 107
}

Y
Yu Yang 已提交
108
void FCOpMaker::Make() {
T
Tao Luo 已提交
109
  AddInput("Input", "(Tensor), The input tensor of fully connected operator.");
T
tensor-tang 已提交
110 111
  AddInput("W", "(Tensor), The weight fc op with shape (I, O).");
  AddInput("Bias", "(Tensor, optional) Bias vector with shape (1 x O")
T
tensor-tang 已提交
112
      .AsDispensable();
T
Tao Luo 已提交
113
  AddAttr<int>("in_num_col_dims",
T
Tao Luo 已提交
114 115 116 117
               "(int, default 1), The fc op can take tensors with more than "
               "two dimensions as its inputs.")
      .SetDefault(1)
      .EqualGreaterThan(1);
118
  AddOutput("Out", "(Tensor) The output tensor of fully connected operator. ");
119 120 121
  AddAttr<bool>("use_mkldnn",
                "(bool, default false) Only used in mkldnn kernel")
      .SetDefault(false);
L
luotao1 已提交
122 123
  AddAttr<bool>(framework::kAllKernelsMustComputeRuntimeShape,
                "Skip calling InferShape() function in the runtime.")
L
luotao1 已提交
124
      .SetDefault(true);
125 126 127
  AddComment(R"DOC(
  Fully Connected Operator.

128
  The fully connected operation calculates the output based on the input, weights and bias.
129 130 131 132
  The size of each dimension of the parameters checked in the infer-shape.
)DOC");
}

T
tensor-tang 已提交
133 134 135 136
template <typename T>
class FCOpKernel : public framework::OpKernel<T> {
 public:
  void Compute(const paddle::framework::ExecutionContext& ctx) const override {
T
tensor-tang 已提交
137
    PADDLE_ENFORCE(platform::is_cpu_place(ctx.GetPlace()),
T
tensor-tang 已提交
138
                   "It must use CPUPlace.");
L
luotao1 已提交
139
    auto input = ctx.Input<framework::LoDTensor>("Input");
L
luotao1 已提交
140 141
    auto w = ctx.Input<Tensor>("W");
    auto bias = ctx.Input<Tensor>("Bias");
L
luotao1 已提交
142 143
    auto output = ctx.Output<framework::LoDTensor>("Out");
    int in_num_col_dims = ctx.Attr<int>("in_num_col_dims");
T
tensor-tang 已提交
144
    auto w_dims = w->dims();
L
luotao1 已提交
145 146 147 148 149 150

    std::vector<int64_t> output_dims;
    FCOutputSize(input->dims(), w_dims, output_dims, in_num_col_dims);
    output->Resize(framework::make_ddim(output_dims));
    output->set_lod(input->lod());

T
Tao Luo 已提交
151
    auto out_dims = output->dims();
L
luotao1 已提交
152
    int M = framework::product(out_dims) / w_dims[1];
T
tensor-tang 已提交
153 154 155 156

    const T* input_data = input->data<T>();
    const T* w_data = w->data<T>();
    T* output_data = output->mutable_data<T>(ctx.GetPlace());
157 158
    auto blas = math::GetBlas<platform::CPUDeviceContext, T>(ctx);
    math::FCCompute<platform::CPUDeviceContext, T>(
T
Tao Luo 已提交
159
        blas, M, w_dims[1], w_dims[0], input_data, w_data, output_data,
160
        bias ? bias->data<T>() : NULL);
T
tensor-tang 已提交
161 162

    // TODO(TJ): fuse act
T
tensor-tang 已提交
163 164 165
  }
};

166 167 168
}  // namespace operators
}  // namespace paddle

T
tensor-tang 已提交
169 170
namespace ops = paddle::operators;
REGISTER_OPERATOR(fc, ops::FCOp, ops::FCOpMaker,
171
                  paddle::framework::DefaultGradOpDescMaker<true>);
T
tensor-tang 已提交
172
REGISTER_OPERATOR(fc_grad, ops::FCOpGrad);
T
tensor-tang 已提交
173
REGISTER_OP_CPU_KERNEL(fc, ops::FCOpKernel<float>, ops::FCOpKernel<double>);