softmax_op.cc 9.6 KB
Newer Older
1
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
2

Q
Qiao Longfei 已提交
3 4 5
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
6

Q
Qiao Longfei 已提交
7 8 9 10 11 12 13
    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. */
14

L
liuwei1031 已提交
15
#include <memory>
16
#include <string>
L
liuwei1031 已提交
17
#include <unordered_map>
18

19
#include "paddle/fluid/framework/op_registry.h"
20
#include "paddle/fluid/platform/device/gpu/gpu_dnn.h"
21

22 23 24
#ifdef PADDLE_WITH_MKLDNN
#include "paddle/fluid/platform/mkldnn_helper.h"
#endif
25

26 27 28
namespace paddle {
namespace operators {

D
dongzhihong 已提交
29
class SoftmaxOp : public framework::OperatorWithKernel {
Y
Yu Yang 已提交
30 31 32
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

33
  void InferShape(framework::InferShapeContext* ctx) const override {
34 35 36 37 38 39
    PADDLE_ENFORCE_EQ(
        ctx->HasInput("X"), true,
        platform::errors::NotFound("Input(X) of SoftmaxOp is not found."));
    PADDLE_ENFORCE_EQ(
        ctx->HasOutput("Out"), true,
        platform::errors::NotFound("Output(Out) of SoftmaxOp is not found."));
Q
Qiao Longfei 已提交
40

41 42 43
    auto dim_x = ctx->GetInputDim("X");
    auto rank_x = dim_x.size();
    auto axis = ctx->Attrs().Get<int>("axis");
44 45 46 47 48 49 50 51
    PADDLE_ENFORCE_GE(axis, -rank_x,
                      platform::errors::InvalidArgument(
                          "Attr(axis) value should be in range [-R, R-1], "
                          "R is the rank of Input(X)."));
    PADDLE_ENFORCE_LT(axis, rank_x,
                      platform::errors::InvalidArgument(
                          "Attr(axis) value should be in range [-R, R-1], "
                          "R is the rank of Input(X)."));
52

F
fengjiayi 已提交
53
    ctx->SetOutputDim("Out", ctx->GetInputDim("X"));
Q
Qiao Longfei 已提交
54
    ctx->ShareLoD("X", /*->*/ "Out");
55
  }
56 57 58 59 60

 protected:
  framework::OpKernelType GetExpectedKernelType(
      const framework::ExecutionContext& ctx) const override {
    // choose cudnn kernel if the runtime supported.
K
Kexin Zhao 已提交
61
    framework::LibraryType library_{framework::LibraryType::kPlain};
M
mozga-intel 已提交
62 63
    std::string data_format = ctx.Attr<std::string>("data_format");
    framework::DataLayout layout_ = framework::StringToDataLayout(data_format);
64
    auto input_data_type = OperatorWithKernel::IndicateVarDataType(ctx, "X");
M
mozga-intel 已提交
65

66
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
K
Kexin Zhao 已提交
67
    if (platform::CanCUDNNBeUsed(ctx)) {
K
Kexin Zhao 已提交
68
      library_ = framework::LibraryType::kCUDNN;
69 70
    }
#endif
71 72
#ifdef PADDLE_WITH_MKLDNN
    if (library_ == framework::LibraryType::kPlain &&
73
        this->CanMKLDNNBeUsed(ctx, input_data_type)) {
74
      library_ = framework::LibraryType::kMKLDNN;
M
mozga-intel 已提交
75
      layout_ = framework::DataLayout::kMKLDNN;
76 77
    }
#endif
K
Kexin Zhao 已提交
78

79
#ifndef PADDLE_WITH_ASCEND_CL
K
Kexin Zhao 已提交
80
    if (input_data_type == framework::proto::VarType::FP16) {
81 82 83 84
      PADDLE_ENFORCE_EQ(platform::is_gpu_place(ctx.GetPlace()) ||
                            platform::is_xpu_place(ctx.GetPlace()),
                        true, platform::errors::InvalidArgument(
                                  "float16 can only be used on GPU/XPU place"));
K
Kexin Zhao 已提交
85
    }
86
#endif
K
Kexin Zhao 已提交
87

M
mozga-intel 已提交
88
    return framework::OpKernelType(input_data_type, ctx.GetPlace(), layout_,
K
Kexin Zhao 已提交
89
                                   library_);
90
  }
91
};
92

D
dongzhihong 已提交
93
class SoftmaxOpMaker : public framework::OpProtoAndCheckerMaker {
94
 public:
Y
Yu Yang 已提交
95
  void Make() override {
96
    AddInput("X",
F
fengjiayi 已提交
97
             "The input tensor of softmax, "
D
dengkaipeng 已提交
98
             "whose dimension :attr:`axis` is the input_feature_dimensions.");
99
    AddOutput("Out", "The normalized values with the same shape as X.");
100
    AddAttr<int>("axis",
D
dengkaipeng 已提交
101
                 "The dimension index of Input(x) to perform softmax,"
102 103
                 "default -1 for last dimension")
        .SetDefault(-1);
104 105 106
    AddAttr<bool>(
        "use_cudnn",
        "(bool, default false) Only used in cudnn kernel, need install cudnn")
107 108
        .SetDefault(false)
        .AsExtra();
109 110 111 112 113 114 115
    AddAttr<std::string>(
        "data_format",
        "(string, default NCHW) Only used in "
        "An optional string from: \"NHWC\", \"NCHW\". "
        "Defaults to \"NHWC\". Specify the data format of the output data, "
        "the input will be transformed automatically. ")
        .SetDefault("AnyLayout");
116 117
    AddAttr<bool>("use_mkldnn",
                  "(bool, default false) Only used in mkldnn kernel")
118 119
        .SetDefault(false)
        .AsExtra();
120 121 122 123
    AddAttr<std::string>(
        "mkldnn_data_type",
        "(string, default \"float32\"). Data type of mkldnn kernel")
        .SetDefault("float32")
124 125
        .InEnum({"float32", "bfloat16"})
        .AsExtra();
J
Jacek Czaja 已提交
126
    AddAttr<bool>("is_test",
127 128
                  "(bool, default false) Set to true for inference only, false "
                  "for training. Some layers may run faster when this is true.")
129 130
        .SetDefault(false)
        .AsExtra();
C
caoying03 已提交
131
    AddComment(R"DOC(
132 133
Softmax Operator.

134
The input of the softmax operator is a tensor of any rank. The output tensor
F
fengjiayi 已提交
135
has the same shape as the input.
C
caoying03 已提交
136

D
dengkaipeng 已提交
137
The dimension :attr:`axis` of the input tensor will be permuted to the last.
D
dengkaipeng 已提交
138
Then the input tensor will be logically flattened to a 2-D matrix. The matrix's
D
dengkaipeng 已提交
139
second dimension(row length) is as same as the dimension :attr:`axis` of the input
140 141 142
tensor, and the first dimension(column length) is the product of all other
dimensions of the input tensor. For each row of the matrix, the softmax operator
squashes the K-dimensional(K is the width of the matrix, which is also the size
D
dengkaipeng 已提交
143
of the input tensor's dimension :attr:`axis`) vector of arbitrary real values to a
F
fengjiayi 已提交
144
K-dimensional vector of real values in the range [0, 1] that add up to 1.
145 146 147 148 149
It computes the exponential of the given dimension and the sum of exponential
values of all the other dimensions in the K-dimensional vector input.
Then the ratio of the exponential of the given dimension and the sum of
exponential values of all the other dimensions is the output of the softmax
operator.
C
caoying03 已提交
150

F
fengjiayi 已提交
151
For each row $i$ and each column $j$ in the matrix, we have:
F
fengjiayi 已提交
152
    $$Out[i, j] = \frac{\exp(X[i, j])}{\sum_j(exp(X[i, j])}$$
C
caoying03 已提交
153 154

)DOC");
155 156 157
  }
};

C
chengduo 已提交
158 159
class SoftmaxOpInferVarType : public framework::PassInDtypeAndVarTypeToOutput {
 protected:
160
  std::unordered_map<std::string, std::string>& GetInputOutputWithSameType()
C
chengduo 已提交
161
      const override {
162 163
    static std::unordered_map<std::string, std::string> m{{"X", /*->*/ "Out"}};
    return m;
C
chengduo 已提交
164 165 166
  }
};

D
dongzhihong 已提交
167
class SoftmaxOpGrad : public framework::OperatorWithKernel {
Y
Yu Yang 已提交
168 169 170
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

171
  void InferShape(framework::InferShapeContext* ctx) const override {
172 173 174 175 176 177 178 179 180 181 182
    PADDLE_ENFORCE_EQ(
        ctx->HasInput("Out"), true,
        platform::errors::InvalidArgument("Input(Out) is not found."));
    PADDLE_ENFORCE_EQ(
        ctx->HasInput(framework::GradVarName("Out")), true,
        platform::errors::InvalidArgument("Input(Out@GRAD) is not found."));
    PADDLE_ENFORCE_EQ(
        ctx->GetInputDim("Out"),
        ctx->GetInputDim(framework::GradVarName("Out")),
        platform::errors::InvalidArgument("Input(Out) and its gradients "
                                          "should have a same shape."));
183

184 185
    ctx->SetOutputDim(framework::GradVarName("X"),
                      ctx->GetInputDim(framework::GradVarName("Out")));
D
dongzhihong 已提交
186
  }
187 188 189 190 191

 protected:
  framework::OpKernelType GetExpectedKernelType(
      const framework::ExecutionContext& ctx) const override {
    // choose cudnn kernel if the runtime supported.
K
Kexin Zhao 已提交
192
    framework::LibraryType library_{framework::LibraryType::kPlain};
J
Jacek Czaja 已提交
193 194
    std::string data_format = ctx.Attr<std::string>("data_format");
    framework::DataLayout layout_ = framework::StringToDataLayout(data_format);
195 196
    auto input_data_type = OperatorWithKernel::IndicateVarDataType(
        ctx, framework::GradVarName("Out"));
197
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
K
Kexin Zhao 已提交
198
    if (platform::CanCUDNNBeUsed(ctx)) {
K
Kexin Zhao 已提交
199
      library_ = framework::LibraryType::kCUDNN;
200 201
    }
#endif
J
Jacek Czaja 已提交
202 203
#ifdef PADDLE_WITH_MKLDNN
    if (library_ == framework::LibraryType::kPlain &&
204
        this->CanMKLDNNBeUsed(ctx, input_data_type)) {
J
Jacek Czaja 已提交
205 206 207 208 209
      library_ = framework::LibraryType::kMKLDNN;
      layout_ = framework::DataLayout::kMKLDNN;
    }
#endif
    if (input_data_type == framework::proto::VarType::FP16) {
210
      if (!(platform::is_gpu_place(ctx.GetPlace()) ||
211 212
            platform::is_npu_place(ctx.GetPlace()) ||
            platform::is_xpu_place(ctx.GetPlace())))
213
        PADDLE_THROW(platform::errors::InvalidArgument(
214
            "float16 can only be used on GPU/NPU/XPU place"));
J
Jacek Czaja 已提交
215 216 217 218
    }

    return framework::OpKernelType(input_data_type, ctx.GetPlace(), layout_,
                                   library_);
219
  }
D
dongzhihong 已提交
220 221
};

H
hong 已提交
222 223
template <typename T>
class SoftmaxOpGradMaker : public framework::SingleGradOpMaker<T> {
224
 public:
H
hong 已提交
225
  using framework::SingleGradOpMaker<T>::SingleGradOpMaker;
226 227

 protected:
228
  void Apply(GradOpPtr<T> op) const override {
229 230
    op->SetType("softmax_grad");

H
hong 已提交
231 232
    op->SetInput("Out", this->Output("Out"));
    op->SetInput(framework::GradVarName("Out"), this->OutputGrad("Out"));
233

H
hong 已提交
234
    op->SetAttrMap(this->Attrs());
235

H
hong 已提交
236
    op->SetOutput(framework::GradVarName("X"), this->InputGrad("X"));
237 238
  }
};
D
dzhwinter 已提交
239

240 241
DECLARE_INPLACE_OP_INFERER(SoftmaxInplaceInferer, {"X", "Out"});

242 243 244
}  // namespace operators
}  // namespace paddle

D
dongzhihong 已提交
245
namespace ops = paddle::operators;
D
dongzhihong 已提交
246

Y
Yang Yang 已提交
247
REGISTER_OPERATOR(softmax, ops::SoftmaxOp, ops::SoftmaxOpMaker,
H
hong 已提交
248 249 250
                  ops::SoftmaxOpInferVarType,
                  ops::SoftmaxOpGradMaker<paddle::framework::OpDesc>,
                  ops::SoftmaxOpGradMaker<paddle::imperative::OpBase>,
251
                  ops::SoftmaxInplaceInferer);
252
REGISTER_OPERATOR(softmax_grad, ops::SoftmaxOpGrad);