batch_norm_op.cc 24.9 KB
Newer Older
1
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
Q
Qiao Longfei 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14

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. */

Y
Yi Wang 已提交
15
#include "paddle/fluid/operators/batch_norm_op.h"
16

Q
qingqing01 已提交
17
#include <memory>
S
Siddharth Goyal 已提交
18
#include <string>
Q
qingqing01 已提交
19
#include <unordered_map>
20

Y
Yi Wang 已提交
21
#include "paddle/fluid/framework/data_layout.h"
22
#ifdef PADDLE_WITH_DNNL
23 24
#include "paddle/fluid/platform/mkldnn_helper.h"
#endif
Q
Qiao Longfei 已提交
25

26 27 28 29
#include "paddle/fluid/prim/api/composite_backward/composite_backward_api.h"
#include "paddle/fluid/prim/utils/static/composite_grad_desc_maker.h"
#include "paddle/fluid/prim/utils/static/desc_tensor.h"

H
hong 已提交
30 31 32
#include "paddle/fluid/framework/infershape_utils.h"
#include "paddle/phi/infermeta/multiary.h"

Q
Qiao Longfei 已提交
33 34 35
namespace paddle {
namespace operators {

Q
qingqing01 已提交
36
void BatchNormOp::InferShape(framework::InferShapeContext *ctx) const {
37 38 39 40 41 42 43
  OP_INOUT_CHECK(ctx->HasInput("X"), "Input", "X", "BatchNorm");
  OP_INOUT_CHECK(ctx->HasInput("Scale"), "Input", "Scale", "BatchNorm");
  OP_INOUT_CHECK(ctx->HasInput("Bias"), "Input", "Bias", "BatchNorm");
  OP_INOUT_CHECK(ctx->HasInput("Mean"), "Input", "Mean", "BatchNorm");
  OP_INOUT_CHECK(ctx->HasInput("Variance"), "Input", "Variance", "BatchNorm");
  OP_INOUT_CHECK(ctx->HasOutput("Y"), "Output", "Y", "BatchNorm");

Q
qingqing01 已提交
44
  bool is_test = ctx->Attrs().Get<bool>("is_test");
45 46 47
  bool trainable_stats = ctx->Attrs().Get<bool>("trainable_statistics");
  bool test_mode = is_test && (!trainable_stats);
  if (!test_mode) {
48
    OP_INOUT_CHECK(ctx->HasOutput("MeanOut"), "Output", "MeanOut", "BatchNorm");
49 50 51 52 53 54 55
    OP_INOUT_CHECK(
        ctx->HasOutput("VarianceOut"), "Output", "VarianceOut", "BatchNorm");
    OP_INOUT_CHECK(
        ctx->HasOutput("SavedMean"), "Output", "SavedMean", "BatchNorm");
    OP_INOUT_CHECK(ctx->HasOutput("SavedVariance"),
                   "Output",
                   "SavedVariance",
56
                   "BatchNorm");
Q
Qiao Longfei 已提交
57
  }
K
Kexin Zhao 已提交
58

Q
qingqing01 已提交
59
  // make sure Mean/MeanOut and Variance/VarianceOut share memory in Python
60 61
  PADDLE_ENFORCE_EQ(ctx->Inputs("Mean")[0],
                    ctx->Outputs("MeanOut")[0],
62 63 64
                    platform::errors::InvalidArgument(
                        "Mean and MeanOut should share the same memory"));
  PADDLE_ENFORCE_EQ(
65 66
      ctx->Inputs("Variance")[0],
      ctx->Outputs("VarianceOut")[0],
67 68
      platform::errors::InvalidArgument(
          "Variance and VarianceOut should share the same memory"));
Q
qingqing01 已提交
69 70

  const auto x_dims = ctx->GetInputDim("X");
71 72 73

  for (int i = 0; i < x_dims.size(); i++) {
    PADDLE_ENFORCE_EQ(
74 75
        (x_dims[i] == -1) || (x_dims[i] > 0),
        true,
76 77
        platform::errors::InvalidArgument(
            "Each dimension of input tensor is expected to be -1 or a "
78
            "positive number, but received %d. Input's shape is [%s].",
79 80
            x_dims[i],
            x_dims));
81 82
  }

83 84
  const DataLayout data_layout =
      phi::StringToDataLayout(ctx->Attrs().Get<std::string>("data_layout"));
Q
qingqing01 已提交
85

86 87
  if (ctx->IsRuntime() && ctx->HasInput("MomentumTensor")) {
    auto mom = ctx->Inputs("MomentumTensor");
88 89
    PADDLE_ENFORCE_EQ(mom.size(),
                      1,
90
                      platform::errors::InvalidArgument(
C
ceci3 已提交
91 92 93
                          "The input tensor MomentumTensor's size must be 1"
                          "But received: MomentumTensor's size is [%d]",
                          mom.size()));
94 95
  }

96
  PADDLE_ENFORCE_GE(
97 98
      x_dims.size(),
      2,
K
Kaipeng Deng 已提交
99 100 101 102
      platform::errors::InvalidArgument(
          "ShapeError: the dimension of input "
          "X must greater than or equal to 2. But received: the shape of input "
          "X = [%s], the dimension of input X =[%d]",
103 104
          x_dims,
          x_dims.size()));
105
  PADDLE_ENFORCE_LE(
106 107
      x_dims.size(),
      5,
K
Kaipeng Deng 已提交
108 109 110 111
      platform::errors::InvalidArgument(
          "ShapeError: the dimension of input X "
          "must smaller than or equal to 5. But received: the shape of input X "
          "= [%s], the dimension of input X = [%d]",
112 113
          x_dims,
          x_dims.size()));
114 115
  VLOG(4) << ctx->IsRunMKLDNNKernel();
  VLOG(4) << data_layout;
Q
qingqing01 已提交
116
  const int64_t C =
117
      ((ctx->IsRunMKLDNNKernel() == true) || (data_layout == DataLayout::kNCHW)
118 119
           ? x_dims[1]
           : x_dims[x_dims.size() - 1]);
Q
qingqing01 已提交
120

121 122
  auto scale_dim = ctx->GetInputDim("Scale");
  auto bias_dim = ctx->GetInputDim("Bias");
Q
qingqing01 已提交
123

124
  PADDLE_ENFORCE_EQ(
125 126
      scale_dim.size(),
      1UL,
127 128 129 130
      platform::errors::InvalidArgument(
          "ShapeError: the dimension of scale must equal to 1."
          "But received: the shape of scale is [%s], the dimension "
          "of scale is [%d]",
131 132 133 134
          scale_dim,
          scale_dim.size()));
  PADDLE_ENFORCE_EQ(bias_dim.size(),
                    1UL,
135 136 137 138
                    platform::errors::InvalidArgument(
                        "ShapeError: the dimension of bias must equal to 1."
                        "But received: the shape of bias is [%s],the dimension "
                        "of bias is [%d]",
139 140
                        bias_dim,
                        bias_dim.size()));
C
ceci3 已提交
141

142
  bool check = true;
143
  if ((!ctx->IsRuntime()) &&
144
      (phi::product(scale_dim) <= 0 || phi::product(bias_dim) <= 0)) {
145 146 147 148
    check = false;
  }

  if (check) {
149 150
    PADDLE_ENFORCE_EQ(scale_dim[0],
                      C,
151 152 153
                      platform::errors::InvalidArgument(
                          "ShapeError: the shape of scale must equal to [%d]"
                          "But received: the shape of scale is [%d]",
154 155 156 157
                          C,
                          scale_dim[0]));
    PADDLE_ENFORCE_EQ(bias_dim[0],
                      C,
158 159 160
                      platform::errors::InvalidArgument(
                          "ShapeError: the shape of bias must equal to [%d]"
                          "But received: the shape of bias is [%d]",
161 162
                          C,
                          bias_dim[0]));
163
  }
Q
qingqing01 已提交
164
  ctx->SetOutputDim("Y", x_dims);
165
  ctx->ShareLoD("X", "Y");
166
  VLOG(4) << x_dims;
Q
qingqing01 已提交
167 168
  ctx->SetOutputDim("MeanOut", {C});
  ctx->SetOutputDim("VarianceOut", {C});
169 170 171 172
  if (!test_mode) {
    ctx->SetOutputDim("SavedMean", {C});
    ctx->SetOutputDim("SavedVariance", {C});
  }
173
  if (ctx->HasOutput("ReserveSpace")) {
174 175
    ctx->SetOutputDim("ReserveSpace", {-1});
  }
Q
qingqing01 已提交
176 177
}

178
phi::KernelKey BatchNormOp::GetExpectedKernelType(
Q
qingqing01 已提交
179
    const framework::ExecutionContext &ctx) const {
180
  auto input_data_type = OperatorWithKernel::IndicateVarDataType(ctx, "X");
Q
qingqing01 已提交
181 182 183 184 185 186 187
  // By default, the type of the scale, bias, mean,
  // and var tensors should both be float. (For float or float16 input tensor)
  // or double (For double input tensor).
  auto bn_param_type = framework::proto::VarType::FP32;
  if (input_data_type == framework::proto::VarType::FP64) {
    bn_param_type = framework::proto::VarType::FP64;
  }
K
Kaipeng Deng 已提交
188
  PADDLE_ENFORCE_EQ(
189
      bn_param_type,
190 191
      framework::TransToProtoVarType(
          ctx.Input<phi::DenseTensor>("Scale")->dtype()),
K
Kaipeng Deng 已提交
192 193
      platform::errors::InvalidArgument("Scale input should be of float type"));
  PADDLE_ENFORCE_EQ(
194
      bn_param_type,
195 196
      framework::TransToProtoVarType(
          ctx.Input<phi::DenseTensor>("Bias")->dtype()),
K
Kaipeng Deng 已提交
197 198
      platform::errors::InvalidArgument("Bias input should be of float type"));
  PADDLE_ENFORCE_EQ(
199
      bn_param_type,
200 201
      framework::TransToProtoVarType(
          ctx.Input<phi::DenseTensor>("Mean")->dtype()),
K
Kaipeng Deng 已提交
202
      platform::errors::InvalidArgument("Mean input should be of float type"));
203 204 205 206 207
  PADDLE_ENFORCE_EQ(bn_param_type,
                    framework::TransToProtoVarType(
                        ctx.Input<phi::DenseTensor>("Variance")->dtype()),
                    platform::errors::InvalidArgument(
                        "Variance input should be of float type"));
Q
qingqing01 已提交
208

209
  return phi::KernelKey(input_data_type, ctx.GetPlace());
Q
qingqing01 已提交
210 211
}

212
phi::KernelKey BatchNormOp::GetKernelTypeForVar(
213
    const std::string &var_name,
214
    const phi::DenseTensor &tensor,
215
    const phi::KernelKey &expected_kernel_type) const {
216
#ifdef PADDLE_WITH_DNNL
217 218 219
  // Only input require reshaping, weights and
  // bias are having shape in NCHW order
  if ((var_name == "X") &&
220
      (expected_kernel_type.layout() == phi::DataLayout::ONEDNN) &&
221
      (tensor.layout() != phi::DataLayout::ONEDNN)) {
222 223 224
    auto attrs = Attrs();
    auto ar = paddle::framework::AttrReader(attrs);
    const std::string data_layout = ar.Get<std::string>("data_layout");
225
    auto dl = phi::StringToDataLayout(data_layout);
226 227
    // Some models may have intentionally set "AnyLayout" for pool
    // op. Treat this as NCHW (default data_format value)
228
    if (dl != phi::DataLayout::kAnyLayout) {
229
      return phi::KernelKey(tensor.place(), dl, expected_kernel_type.dtype());
230 231 232
    }
  }
#endif
233 234
  return phi::KernelKey(
      tensor.place(), tensor.layout(), expected_kernel_type.dtype());
235 236
}

Q
qingqing01 已提交
237 238 239 240 241 242 243 244 245
void BatchNormOpMaker::Make() {
  AddAttr<bool>("is_test",
                "(bool, default false) Set to true for inference only, false "
                "for training. Some layers may run faster when this is true.")
      .SetDefault(false);
  AddAttr<float>("momentum", "").SetDefault(0.9);
  AddAttr<float>("epsilon", "")
      .SetDefault(1e-5)
      .AddCustomChecker([](const float &epsilon) {
K
Kaipeng Deng 已提交
246
        PADDLE_ENFORCE_GE(
247 248
            epsilon,
            0.0f,
K
Kaipeng Deng 已提交
249 250
            platform::errors::InvalidArgument(
                "'epsilon' should be greater or equal than 0.0."));
251 252
        PADDLE_ENFORCE_LE(epsilon,
                          0.001f,
K
Kaipeng Deng 已提交
253 254
                          platform::errors::InvalidArgument(
                              "'epsilon' should be less or equal than 0.001."));
Q
qingqing01 已提交
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
      });
  AddAttr<std::string>("data_layout", "").SetDefault("NCHW");
  AddInput("X", "The input tensor");
  AddInput("Scale",
           "Scale is a 1-dimensional tensor of size C "
           "that is applied to the output");
  AddInput("Bias",
           "Bias is a 1-dimensional tensor of size C "
           "that is applied to the output");
  AddInput("Mean",
           "The global mean (for training) or "
           "estimated mean (for testing)");
  AddInput("Variance",
           "The global variance (for training) "
           "or estimated Variance (for testing)");
270
  AddInput("MomentumTensor",
271
           "(phi::DenseTensor<float32>, optional) If provided, batch_norm will "
272 273 274
           "use this as momentum, this has a higher priority than "
           "attr(momentum), the shape of this tensor MUST BE [1].")
      .AsDispensable();
Q
qingqing01 已提交
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
  AddOutput("Y", "result after normalization");
  AddOutput("MeanOut",
            "Share memory with Mean. "
            "Store the global mean when training");
  AddOutput("VarianceOut",
            "Share memory with Variance. "
            "Store the global Variance when training");
  AddOutput("SavedMean",
            "Mean of the current mini batch, "
            "will apply to output when training")
      .AsIntermediate();
  AddOutput("SavedVariance",
            "Variance of the current mini batch, "
            "will apply to output when training")
      .AsIntermediate();
290 291 292
  AddOutput("ReserveSpace",
            "Reserve GPU space for triggering the new semi-persistent "
            "NHWC kernel")
C
ceci3 已提交
293 294
      .AsDispensable()
      .AsExtra();
Q
qingqing01 已提交
295 296 297 298 299 300 301 302
  AddAttr<bool>("use_global_stats",
                "(bool, default false) Whether to use global mean and "
                "variance. In inference or test mode, set use_global_stats "
                "to true or is_test true. the behavior is equivalent. "
                "In train mode, when setting use_global_stats True, the "
                "global mean and variance are also used during train time, "
                "the BN acts as scaling and shiffting.")
      .SetDefault(false);
303 304 305 306 307
  AddAttr<bool>("trainable_statistics",
                "(bool, default false) Whether to calculate mean and variance "
                "in test mode. If setting true in test mode, mean and variace "
                "will be calculated by current batch statistics.")
      .SetDefault(false);
Q
qingqing01 已提交
308
  AddComment(R"DOC(
309
Batch Normalization.
Q
Qiao Longfei 已提交
310

311 312 313 314 315 316
Batch Norm has been implemented as discussed in the paper:
https://arxiv.org/pdf/1502.03167.pdf
Can be used as a normalizer function for conv2d and fully_connected operations.
The required data format for this layer is one of the following:
1. NHWC `[batch, in_height, in_width, in_channels]`
2. NCHW `[batch, in_channels, in_height, in_width]`
Q
Qiao Longfei 已提交
317 318

)DOC");
Q
qingqing01 已提交
319
}
C
chengduo 已提交
320

Q
qingqing01 已提交
321 322
void BatchNormGradOp::InferShape(framework::InferShapeContext *ctx) const {
  // check input
323
  OP_INOUT_CHECK(ctx->HasInput("Scale"), "Input", "Scale", "BatchNormGrad");
324 325 326
  OP_INOUT_CHECK(ctx->HasInput(framework::GradVarName("Y")),
                 "Input",
                 framework::GradVarName("Y"),
327
                 "BatchNormGrad");
328 329 330 331 332
  OP_INOUT_CHECK(
      ctx->HasInput("SavedMean"), "Input", "SavedMean", "BatchNormGrad");
  OP_INOUT_CHECK(ctx->HasInput("SavedVariance"),
                 "Input",
                 "SavedVariance",
333
                 "BatchNormGrad");
Q
qingqing01 已提交
334 335

  // check output
336 337
  const bool has_scale_grad = ctx->HasOutput(framework::GradVarName("Scale"));
  const bool has_bias_grad = ctx->HasOutput(framework::GradVarName("Bias"));
338
  const bool has_x_grad = ctx->HasOutput(framework::GradVarName("X"));
339

340 341
  PADDLE_ENFORCE_EQ((has_scale_grad == has_bias_grad),
                    true,
342
                    platform::errors::NotFound(
343 344 345
                        "Output(Scale@GRAD) and Output(Bias@GRAD) must be null "
                        "or not be null at same time. But now, "
                        "has Scale@Grad=[%d], has Bias@GRAD=[%d]",
346 347
                        has_scale_grad,
                        has_bias_grad));
348

Q
qingqing01 已提交
349 350
  const bool use_global_stats = ctx->Attrs().Get<bool>("use_global_stats");
  if (use_global_stats) {
K
Kaipeng Deng 已提交
351
    PADDLE_ENFORCE_EQ(
352 353
        !ctx->Attrs().Get<bool>("use_mkldnn"),
        true,
K
Kaipeng Deng 已提交
354 355
        platform::errors::InvalidArgument(
            "Using global stats during training is not supported "
356
            "in oneDNN version of batch_norm_gradient kernel now."));
Q
qingqing01 已提交
357
  }
Q
Qiao Longfei 已提交
358

359 360
  OP_INOUT_CHECK(ctx->HasInput("X"), "Input", "X", "BatchNormGrad");
  const auto x_dims = ctx->GetInputDim("X");
361 362
  const DataLayout data_layout =
      phi::StringToDataLayout(ctx->Attrs().Get<std::string>("data_layout"));
Q
Qiao Longfei 已提交
363

364
  const int C =
365
      ((ctx->IsRunMKLDNNKernel() == true) || (data_layout == DataLayout::kNCHW)
366 367 368 369 370 371 372
           ? x_dims[1]
           : x_dims[x_dims.size() - 1]);

  // has_scale_grad == has_bias_grad, judge has_scale_grad is enough
  if (has_scale_grad) {
    ctx->SetOutputDim(framework::GradVarName("Scale"), {C});
    ctx->SetOutputDim(framework::GradVarName("Bias"), {C});
Q
Qiao Longfei 已提交
373
  }
374 375 376
  if (has_x_grad) {
    ctx->SetOutputDim(framework::GradVarName("X"), x_dims);
  }
Q
qingqing01 已提交
377
}
Q
Qiao Longfei 已提交
378

379
phi::KernelKey BatchNormGradOp::GetExpectedKernelType(
Q
qingqing01 已提交
380 381 382
    const framework::ExecutionContext &ctx) const {
  const auto *var = ctx.InputVar(framework::GradVarName("Y"));
  if (var == nullptr) {
K
Kaipeng Deng 已提交
383 384
    PADDLE_THROW(
        platform::errors::InvalidArgument("can't find gradient variable of Y"));
Q
qingqing01 已提交
385
  }
386 387 388
  const phi::DenseTensor *t = nullptr;
  if (var->IsType<phi::DenseTensor>()) {
    t = &var->Get<phi::DenseTensor>();
389 390
  } else if (var->IsType<phi::DenseTensor>()) {
    t = &var->Get<phi::DenseTensor>();
Q
qingqing01 已提交
391 392
  }
  if (t == nullptr) {
K
Kaipeng Deng 已提交
393 394
    PADDLE_THROW(
        platform::errors::InvalidArgument("gradient variable of Y is empty"));
Q
qingqing01 已提交
395
  }
396

397
  auto data_type = OperatorWithKernel::IndicateVarDataType(ctx, "X");
398
  return phi::KernelKey(data_type, ctx.GetPlace());
Q
qingqing01 已提交
399
}
Q
Qiao Longfei 已提交
400

401
phi::KernelKey BatchNormGradOp::GetKernelTypeForVar(
402
    const std::string &var_name,
403
    const phi::DenseTensor &tensor,
404
    const phi::KernelKey &expected_kernel_type) const {
405
#ifdef PADDLE_WITH_DNNL
406 407 408
  // Only input require reshaping, weights and
  // bias are having shape in NCHW order
  if (((var_name == "X") || (var_name == framework::GradVarName("Y"))) &&
409
      (expected_kernel_type.layout() == phi::DataLayout::ONEDNN) &&
410
      (tensor.layout() != phi::DataLayout::ONEDNN)) {
411 412 413
    auto attrs = Attrs();
    auto ar = paddle::framework::AttrReader(attrs);
    const std::string data_layout = ar.Get<std::string>("data_layout");
414
    auto dl = phi::StringToDataLayout(data_layout);
415 416
    // Some models may have intentionally set "AnyLayout" for pool
    // op. Treat this as NCHW (default data_format value)
417
    if (dl != phi::DataLayout::kAnyLayout) {
418
      return phi::KernelKey(tensor.place(), dl, expected_kernel_type.dtype());
419 420 421
    }
  }
#endif
422 423
  return phi::KernelKey(
      tensor.place(), tensor.layout(), expected_kernel_type.dtype());
424 425
}

H
hong 已提交
426
template <typename T>
427
void BatchNormGradMaker<T>::Apply(GradOpPtr<T> op) const {
428 429 430 431 432 433 434 435
  op->SetType(this->ForwardOpType() + "_grad");
  op->SetInput("X", this->Input("X"));
  op->SetInput(framework::GradVarName("Y"), this->OutputGrad("Y"));

  op->SetInput("Scale", this->Input("Scale"));
  op->SetInput("Bias", this->Input("Bias"));
  op->SetInput("SavedMean", this->Output("SavedMean"));
  op->SetInput("SavedVariance", this->Output("SavedVariance"));
436 437 438
  if (this->HasOutput("ReserveSpace")) {
    op->SetInput("ReserveSpace", this->Output("ReserveSpace"));
  }
439 440

  // used when setting use_global_stats True during training
R
Ruibiao Chen 已提交
441 442
  if (PADDLE_GET_CONST(bool, this->GetAttr("use_global_stats")) ||
      PADDLE_GET_CONST(bool, this->GetAttr("is_test"))) {
443 444 445
    op->SetInput("Mean", this->Output("MeanOut"));
    op->SetInput("Variance", this->Output("VarianceOut"));
  }
446

H
hong 已提交
447 448 449
  op->SetInput("MeanOut", this->Output("MeanOut"));
  op->SetInput("VarianceOut", this->Output("VarianceOut"));

450
  op->SetAttrMap(this->Attrs());
Y
Yu Yang 已提交
451

452 453 454 455
  op->SetOutput(framework::GradVarName("X"), this->InputGrad("X"));
  op->SetOutput(framework::GradVarName("Scale"), this->InputGrad("Scale"));
  op->SetOutput(framework::GradVarName("Bias"), this->InputGrad("Bias"));
}
Y
Yu Yang 已提交
456

457 458 459 460 461 462 463
template <typename T>
void BatchNormDoubleGradMaker<T>::Apply(GradOpPtr<T> op) const {
  op->SetType("batch_norm_grad_grad");
  op->SetInput("X", this->Input("X"));
  op->SetInput("Scale", this->Input("Scale"));
  op->SetInput("SavedMean", this->Input("SavedMean"));
  op->SetInput("SavedVariance", this->Input("SavedVariance"));
R
Ruibiao Chen 已提交
464
  if (PADDLE_GET_CONST(bool, this->GetAttr("use_global_stats"))) {
465
    op->SetInput("Mean", this->Input("Mean"));
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
    op->SetInput("Variance", this->Input("Variance"));
  }
  op->SetInput("DDX", this->OutputGrad(framework::GradVarName("X")));
  op->SetInput("DDScale", this->OutputGrad(framework::GradVarName("Scale")));
  op->SetInput("DDBias", this->OutputGrad(framework::GradVarName("Bias")));
  op->SetInput("DY", this->Input(framework::GradVarName("Y")));

  op->SetAttrMap(this->Attrs());
  op->SetOutput("DX", this->InputGrad("X"));
  op->SetOutput("DScale", this->InputGrad("Scale"));
  op->SetOutput("DDY", this->InputGrad(framework::GradVarName("Y")));
}

void BatchNormDoubleGradOp::InferShape(
    framework::InferShapeContext *ctx) const {
  OP_INOUT_CHECK(ctx->HasInput("X"), "Input", "X", "BatchNormDoubleGrad");
482 483 484 485 486 487 488
  OP_INOUT_CHECK(
      ctx->HasInput("Scale"), "Input", "Scale", "BatchNormDoubleGrad");
  OP_INOUT_CHECK(
      ctx->HasInput("SavedMean"), "Input", "SavedMean", "BatchNormDoubleGrad");
  OP_INOUT_CHECK(ctx->HasInput("SavedVariance"),
                 "Input",
                 "SavedVariance",
489 490 491 492
                 "BatchNormDoubleGrad");

  const bool use_global_stats = ctx->Attrs().Get<bool>("use_global_stats");
  if (use_global_stats) {
493 494 495
    OP_INOUT_CHECK(ctx->HasInput("Variance"),
                   "Input",
                   "VarianceOut",
496 497 498 499 500 501 502 503 504
                   "BatchNormDoubleGrad");
  }

  OP_INOUT_CHECK(ctx->HasInput("DY"), "Input", "DY", "BatchNormDoubleGrad");

  // check output
  OP_INOUT_CHECK(ctx->HasOutput("DX"), "Output", "DX", "BatchNormDoubleGrad");

  const auto x_dims = ctx->GetInputDim("X");
505 506
  const DataLayout data_layout =
      phi::StringToDataLayout(ctx->Attrs().Get<std::string>("data_layout"));
507
  const int C =
508
      ((ctx->IsRunMKLDNNKernel() == true) || (data_layout == DataLayout::kNCHW)
509 510 511
           ? x_dims[1]
           : x_dims[x_dims.size() - 1]);

512 513 514 515 516 517 518 519 520 521 522
  if (ctx->HasOutput("DX")) {
    ctx->SetOutputDim("DX", x_dims);
  }
  if (ctx->HasOutput("DScale")) {
    ctx->SetOutputDim("DScale", {C});
  }
  if (ctx->HasOutput("DDY")) {
    ctx->ShareDim("X", "DDY");
  }
}

523
phi::KernelKey BatchNormDoubleGradOp::GetExpectedKernelType(
524 525 526 527 528 529
    const framework::ExecutionContext &ctx) const {
  const auto *var = ctx.InputVar("DY");
  if (var == nullptr) {
    PADDLE_THROW(
        platform::errors::NotFound("cannot find gradient variable of Y"));
  }
530 531 532
  const phi::DenseTensor *t = nullptr;
  if (var->IsType<phi::DenseTensor>()) {
    t = &var->Get<phi::DenseTensor>();
533 534
  } else if (var->IsType<phi::DenseTensor>()) {
    t = &var->Get<phi::DenseTensor>();
535 536 537 538 539
  }
  if (t == nullptr) {
    PADDLE_THROW(
        platform::errors::InvalidArgument("gradient variable of Y is empty"));
  }
540 541
  return phi::KernelKey(OperatorWithKernel::IndicateVarDataType(ctx, "X"),
                        ctx.GetPlace());
542 543
}

544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
class BatchNormCompositeGradOpMaker : public prim::CompositeGradOpMakerBase {
  using prim::CompositeGradOpMakerBase::CompositeGradOpMakerBase;

 public:
  void Apply() override {
    // inputs and outputs of batch_norm
    paddle::Tensor x = this->GetSingleForwardInput("X");
    paddle::Tensor scale = this->GetSingleForwardInput("Scale");
    paddle::Tensor bias = this->GetSingleForwardInput("Bias");
    paddle::Tensor mean = this->GetSingleForwardInput("Mean");
    paddle::Tensor variance = this->GetSingleForwardInput("Variance");
    paddle::Tensor y = this->GetSingleForwardOutput("Y");
    paddle::Tensor mean_out = this->GetSingleForwardOutput("MeanOut");
    paddle::Tensor variance_out = this->GetSingleForwardOutput("VarianceOut");
    paddle::Tensor saved_mean = this->GetSingleForwardOutput("SavedMean");
    paddle::Tensor saved_variance =
        this->GetSingleForwardOutput("SavedVariance");
    paddle::optional<paddle::Tensor> reserve_space;

    paddle::Tensor y_grad = this->GetSingleOutputGrad("Y");
    paddle::Tensor x_grad = this->GetSingleInputGrad("X");
    paddle::Tensor scale_grad = this->GetSingleInputGrad("Scale");
    paddle::Tensor bias_grad = this->GetSingleInputGrad("Bias");

    auto dx_ptr = this->GetOutputPtr(&x_grad);
    std::string dx_name = this->GetOutputName(x_grad);
    auto dscale_ptr = this->GetOutputPtr(&scale_grad);
    std::string dscale_name = this->GetOutputName(scale_grad);
    auto dbias_ptr = this->GetOutputPtr(&bias_grad);
    std::string dbias_name = this->GetOutputName(bias_grad);

    // attrs of batch_norm
    auto momentum = this->Attr<float>("momentum");
    auto epsilon = this->Attr<float>("epsilon");
    auto data_layout = this->Attr<std::string>("data_layout");
    auto is_test = this->Attr<bool>("is_test");
    auto use_global_stats = this->Attr<bool>("use_global_stats");
    auto trainable_statistics = this->Attr<bool>("trainable_statistics");

    VLOG(3) << "Runing batch_norm composite func";
    prim::batch_norm_grad<prim::DescTensor>(x,
                                            scale,
                                            bias,
                                            mean_out,
                                            variance_out,
                                            saved_mean,
                                            saved_variance,
                                            reserve_space,
                                            y_grad,
                                            momentum,
                                            epsilon,
                                            data_layout,
                                            is_test,
                                            use_global_stats,
                                            trainable_statistics,
                                            dx_ptr,
                                            dscale_ptr,
                                            dbias_ptr);
    this->RecoverOutputName(x_grad, dx_name);
    this->RecoverOutputName(scale_grad, dscale_name);
    this->RecoverOutputName(bias_grad, dbias_name);
  }
};

608 609
DECLARE_INPLACE_OP_INFERER(BatchNormDoubleGradOpInplaceInferer, {"DY", "DDY"});

Q
Qiao Longfei 已提交
610 611 612 613
}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
H
hong 已提交
614

615 616
DECLARE_INFER_SHAPE_FUNCTOR(batch_norm,
                            BatchNormInferShapeFunctor,
H
hong 已提交
617 618
                            PD_INFER_META(phi::BatchNormInferMeta));

619 620 621
REGISTER_OPERATOR(batch_norm,
                  ops::BatchNormOp,
                  ops::BatchNormOpMaker,
H
hong 已提交
622 623
                  ops::BatchNormOpInferVarType,
                  ops::BatchNormGradMaker<paddle::framework::OpDesc>,
624 625
                  ops::BatchNormGradMaker<paddle::imperative::OpBase>,
                  ops::BatchNormCompositeGradOpMaker);
626

627 628
REGISTER_OPERATOR(batch_norm_grad,
                  ops::BatchNormGradOp,
629 630
                  ops::BatchNormDoubleGradMaker<paddle::framework::OpDesc>,
                  ops::BatchNormDoubleGradMaker<paddle::imperative::OpBase>);
631 632
REGISTER_OPERATOR(batch_norm_grad_grad,
                  ops::BatchNormDoubleGradOp,
633
                  ops::BatchNormDoubleGradOpInplaceInferer);