Files
paddlepaddle--paddle/paddle/fluid/eager/grad_node_info.cc
T
2026-07-13 12:40:42 +08:00

1030 lines
40 KiB
C++

// Copyright (c) 2021 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/eager/grad_node_info.h"
#include "glog/logging.h"
#include "paddle/common/errors.h"
#include "paddle/fluid/eager/accumulation/accumulation_node.h"
#include "paddle/fluid/eager/autograd_meta.h"
#include "paddle/fluid/eager/utils.h"
#include "paddle/fluid/framework/convert_utils.h"
#include "paddle/fluid/framework/data_type.h"
#include "paddle/fluid/framework/data_type_transform.h"
#include "paddle/fluid/framework/var_type.h"
#include "paddle/fluid/platform/enforce.h"
#include "paddle/phi/common/data_type.h"
#include "paddle/phi/core/dense_tensor.h"
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
#include "paddle/phi/core/sparse_coo_tensor.h"
#include "paddle/phi/core/sparse_csr_tensor.h"
COMMON_DECLARE_bool(enable_unique_name);
/**
* Implementation of GradNodeBase and Edge.
**/
namespace egr {
static void CheckTensor(const paddle::Tensor& pre, const paddle::Tensor& post) {
if (!pre.has_allocation() && post.has_allocation()) {
PADDLE_THROW(common::errors::PermissionDenied(
"The tensor in before and after hook are not consistent"));
}
if (pre.has_allocation() && post.has_allocation()) {
VLOG(7) << phi::DataTypeToString(pre.dtype()) << " "
<< phi::DataTypeToString(post.dtype());
PADDLE_ENFORCE_EQ(
pre.dtype(),
post.dtype(),
common::errors::PermissionDenied(
"The dtype of tensor before(%s) and after(%s) hook are not "
"consistent",
phi::DataTypeToString(pre.dtype()),
phi::DataTypeToString(post.dtype())));
PADDLE_ENFORCE_EQ(pre.place(),
post.place(),
common::errors::PermissionDenied(
"The place of tensor before(%s) and after(%s) "
"hook are not consistent",
pre.place().DebugString(),
post.place().DebugString()));
}
}
GradNodeBase::GradNodeBase(size_t bwd_in_slot_num, size_t bwd_out_slot_num)
: bwd_out_meta_(), bwd_in_meta_(), gradient_hooks_() {
VLOG(7) << "Construct GradNodeBase";
bwd_in_meta_.resize(bwd_in_slot_num);
bwd_out_meta_.resize(bwd_out_slot_num);
}
const paddle::small_vector<std::vector<GradSlotMeta>, kSlotSmallVectorSize>&
GradNodeBase::InputMeta() const {
return bwd_in_meta_;
}
const paddle::small_vector<std::vector<GradSlotMeta>, kSlotSmallVectorSize>&
GradNodeBase::OutputMeta() const {
return bwd_out_meta_;
}
paddle::small_vector<std::vector<GradSlotMeta>, kSlotSmallVectorSize>&
GradNodeBase::MutableOutputMeta() {
return bwd_out_meta_;
}
paddle::small_vector<std::vector<GradSlotMeta>, kSlotSmallVectorSize>&
GradNodeBase::MutableInputMeta() {
return bwd_in_meta_;
}
void GradNodeBase::SetGradInMeta(const paddle::Tensor& fwd_out,
size_t slot_rank) {
VLOG(7) << "Set GradSlotMeta for Grad Inputs";
auto* fwd_out_meta = egr::EagerUtils::nullable_autograd_meta(fwd_out);
PADDLE_ENFORCE_LE(
slot_rank,
(bwd_in_meta_.size() - 1),
common::errors::InvalidArgument(
"Slot Rank should less equal than bwd_in_meta_ size, since "
"bwd_in_meta_ is designed to hold as same num as backward "
"inputs."));
auto& metas = bwd_in_meta_.at(slot_rank);
if (metas.empty()) {
metas.resize(1);
}
auto& meta = metas[0];
if (fwd_out_meta && fwd_out_meta->StopGradient()) {
meta.SetStopGradient(fwd_out_meta->StopGradient());
}
if (!fwd_out.has_allocation()) {
if (fwd_out.defined() && fwd_out.is_dist_tensor() &&
phi::distributed::NeedComputationClipForPP(fwd_out.impl())) {
VLOG(5) << "Tensor " << fwd_out.name() << " is DistTensor,"
<< " and needs computation clip for pipeline parallel."
<< " Still SetGradInMeta for it.";
} else {
VLOG(6)
<< "Skip Configuring GradSlotMeta for uninitialized GradInput Tensor";
return;
}
}
const phi::DenseTensor* dense_tensor = nullptr;
// Record TensorMeta
if (phi::DenseTensor::classof(fwd_out.impl().get())) {
// Only Copy Meta
dense_tensor = static_cast<phi::DenseTensor*>(fwd_out.impl().get());
} else if (phi::SparseCooTensor::classof(fwd_out.impl().get())) {
phi::SparseCooTensor* coo_tensor =
static_cast<phi::SparseCooTensor*>(fwd_out.impl().get());
dense_tensor = coo_tensor->mutable_non_zero_elements();
} else if (phi::SparseCsrTensor::classof(fwd_out.impl().get())) {
phi::SparseCsrTensor* csr_tensor =
static_cast<phi::SparseCsrTensor*>(fwd_out.impl().get());
dense_tensor = csr_tensor->mutable_non_zero_elements();
} else if (phi::distributed::DistTensor::classof(fwd_out.impl().get())) {
dense_tensor = // NOLINT
&(static_cast<phi::distributed::DistTensor*>(fwd_out.impl().get())
->value());
meta.SetDistAttr(
static_cast<phi::distributed::DistTensor*>(fwd_out.impl().get())
->dist_attr());
meta.SetDistTensorGlobalDims(
static_cast<phi::distributed::DistTensor*>(fwd_out.impl().get())
->dims());
SetIsRunAutoParallel(true);
} else {
VLOG(5) << "Unable to initialize the DenseTensorMeta of GradSlotMeta with "
"non-DenseTensor argument.";
return;
}
PADDLE_ENFORCE_NE(
dense_tensor->meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal(
"Attempting to copy DenseTensorMeta with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor->meta());
meta.SetPlace(fwd_out.place());
if (dense_tensor->type() == phi::DataType::COMPLEX64 ||
dense_tensor->type() == phi::DataType::COMPLEX128) {
need_complex_to_real_ = true;
}
}
void GradNodeBase::SetGradInMeta(const std::vector<paddle::Tensor>& fwd_out,
size_t slot_rank) {
VLOG(7) << "Set GradSlotMeta for Grad Inputs";
size_t slot_size = fwd_out.size();
PADDLE_ENFORCE_LE(
slot_rank,
(bwd_in_meta_.size() - 1),
common::errors::InvalidArgument(
"Slot Rank should less equal than bwd_in_meta_ size, since "
"bwd_in_meta_ is designed to hold as same num as backward "
"inputs."));
auto& metas = bwd_in_meta_.at(slot_rank);
// Init stop gradient vector before use to avoid push back
if (metas.size() < slot_size) {
VLOG(7) << "Init bwd_in_meta_ with slot rank: " << slot_rank;
metas.resize(slot_size);
}
for (size_t i = 0; i < slot_size; i++) {
auto& meta = metas[i];
const auto& fwd_out_tensor = fwd_out[i];
auto* fwd_out_meta =
egr::EagerUtils::nullable_autograd_meta(fwd_out_tensor);
PADDLE_ENFORCE_NOT_NULL(fwd_out_meta,
common::errors::PreconditionNotMet(
"Bwd_in_meta should only be called while "
"autograd_meta is not null. If you got this "
"error, it indicates bugs in framework."));
if (fwd_out_meta && fwd_out_meta->StopGradient()) {
// Set Stop Gradient only when its true or non-initialized autograd_meta,
// since all default value is false.
meta.SetStopGradient(fwd_out_meta->StopGradient());
}
if (!fwd_out_tensor.has_allocation()) {
if (fwd_out_tensor.defined() && fwd_out_tensor.is_dist_tensor() &&
phi::distributed::NeedComputationClipForPP(fwd_out_tensor.impl())) {
VLOG(3) << "Tensor " << fwd_out_tensor.name() << " is DistTensor,"
<< " and needs computation clip for pipeline parallel."
<< " Still SetGradInMeta for it.";
} else {
VLOG(6) << "Skip Configuring GradSlotMeta for uninitialized GradInput "
"Tensor";
return;
}
}
// Record TensorMeta
if (phi::DenseTensor::classof(fwd_out_tensor.impl().get())) {
// Only Copy Meta
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(fwd_out_tensor.impl().get());
PADDLE_ENFORCE_NE(
dense_tensor->meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor->meta());
meta.SetPlace(fwd_out_tensor.place());
if (dense_tensor->type() == phi::DataType::COMPLEX64 ||
dense_tensor->type() == phi::DataType::COMPLEX128) {
need_complex_to_real_ = true;
}
} else if (phi::distributed::DistTensor::classof(
fwd_out_tensor.impl().get())) {
// Only Copy Meta
meta.SetDistAttr(static_cast<phi::distributed::DistTensor*>(
fwd_out_tensor.impl().get())
->dist_attr());
meta.SetDistTensorGlobalDims(static_cast<phi::distributed::DistTensor*>(
fwd_out_tensor.impl().get())
->dims());
SetIsRunAutoParallel(true);
auto dense_tensor = static_cast<phi::distributed::DistTensor*>(
fwd_out_tensor.impl().get())
->value();
PADDLE_ENFORCE_NE(
dense_tensor.meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_out_tensor.place());
if (dense_tensor.type() == phi::DataType::COMPLEX64 ||
dense_tensor.type() == phi::DataType::COMPLEX128) {
need_complex_to_real_ = true;
}
} else {
VLOG(7) << "Unable to initialize the DenseTensorMeta of GradSlotMeta "
"with non-DenseTensor argument.";
}
}
}
void GradNodeBase::SetGradInMeta(const std::vector<paddle::Tensor*>& fwd_out,
size_t slot_rank) {
VLOG(7) << "Set GradSlotMeta for Grad Inputs";
size_t slot_size = fwd_out.size();
PADDLE_ENFORCE_LE(
slot_rank,
(bwd_in_meta_.size() - 1),
common::errors::InvalidArgument(
"Slot Rank should less equal than bwd_in_meta_ size, since "
"bwd_in_meta_ is designed to hold as same num as backward "
"inputs."));
auto& metas = bwd_in_meta_.at(slot_rank);
// Init stop gradient vector before use to avoid push back
if (metas.size() < slot_size) {
VLOG(7) << "Init bwd_in_meta_ with slot rank: " << slot_rank;
metas.resize(slot_size);
}
for (size_t i = 0; i < slot_size; i++) {
auto& meta = metas[i];
const auto& fwd_out_tensor = *fwd_out[i];
auto* fwd_out_meta =
egr::EagerUtils::nullable_autograd_meta(fwd_out_tensor);
PADDLE_ENFORCE_NOT_NULL(fwd_out_meta,
common::errors::PreconditionNotMet(
"Bwd_in_meta should only be called while "
"autograd_meta is not null. If you got this "
"error, it indicates bugs in framework."));
if (fwd_out_meta && fwd_out_meta->StopGradient()) {
// Set Stop Gradient only when its true or non-initialized autograd_meta,
// since all default value is false.
meta.SetStopGradient(fwd_out_meta->StopGradient());
}
if (!fwd_out_tensor.has_allocation()) {
if (fwd_out_tensor.defined() && fwd_out_tensor.is_dist_tensor() &&
phi::distributed::NeedComputationClipForPP(fwd_out_tensor.impl())) {
VLOG(3) << "Tensor " << fwd_out_tensor.name() << " is DistTensor,"
<< " and needs computation clip for pipeline parallel."
<< " Still SetGradInMeta for it.";
} else {
VLOG(7) << "Skip Configuring GradSlotMeta for uninitialized GradInput "
"Tensor";
return;
}
}
// Record TensorMeta
if (phi::DenseTensor::classof(fwd_out_tensor.impl().get())) {
// Only Copy Meta
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(fwd_out_tensor.impl().get());
PADDLE_ENFORCE_NE(
dense_tensor->meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor->meta());
meta.SetPlace(fwd_out_tensor.place());
if (dense_tensor->type() == phi::DataType::COMPLEX64 ||
dense_tensor->type() == phi::DataType::COMPLEX128) {
need_complex_to_real_ = true;
}
} else if (phi::distributed::DistTensor::classof(
fwd_out_tensor.impl().get())) {
// Only Copy Meta
meta.SetDistAttr(static_cast<phi::distributed::DistTensor*>(
fwd_out_tensor.impl().get())
->dist_attr());
meta.SetDistTensorGlobalDims(static_cast<phi::distributed::DistTensor*>(
fwd_out_tensor.impl().get())
->dims());
SetIsRunAutoParallel(true);
auto dense_tensor = static_cast<phi::distributed::DistTensor*>(
fwd_out_tensor.impl().get())
->value();
PADDLE_ENFORCE_NE(
dense_tensor.meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_out_tensor.place());
if (dense_tensor.type() == phi::DataType::COMPLEX64 ||
dense_tensor.type() == phi::DataType::COMPLEX128) {
need_complex_to_real_ = true;
}
} else {
VLOG(5) << "Unable to initialize the DenseTensorMeta of GradSlotMeta "
"with non-DenseTensor argument.";
}
}
}
void GradNodeBase::SetGradOutMeta(const paddle::Tensor& fwd_in,
size_t slot_rank) {
auto* fwd_in_meta = egr::EagerUtils::nullable_autograd_meta(fwd_in);
PADDLE_ENFORCE_LE(
(slot_rank + 1),
bwd_out_meta_.size(),
common::errors::InvalidArgument(
"Slot Rank should less equal than bwd_out_meta_ size, "
"since bwd_out_meta_ is designed to hold as same num as "
"backward outputs."));
auto& metas = bwd_out_meta_.at(slot_rank);
// Init stop gradient vector before use to avoid push back
if (metas.empty()) {
metas.resize(1);
}
auto& meta = metas[0];
if (VLOG_IS_ON(6) || FLAGS_enable_unique_name) {
// Record the forward input tensor name
meta.SetForwardTensorName(fwd_in.name());
}
// Set Stop_gradient
if (fwd_in_meta) {
meta.SetStopGradient(fwd_in_meta->StopGradient());
} else {
meta.SetStopGradient(true);
}
// Set Adj Edges
if (fwd_in_meta && !fwd_in_meta->StopGradient()) {
auto node = fwd_in_meta->GetMutableGradNode();
if (!node || !node.get()) {
fwd_in_meta->SetGradNode(
std::make_shared<egr::GradNodeAccumulation>(fwd_in));
}
VLOG(5) << "Add Edges for slot: " << slot_rank << ", the Edge is from "
<< this->name() << "(" << this << ")"
<< " to " << fwd_in_meta->GetMutableGradNode()->name() << "("
<< fwd_in_meta->GetMutableGradNode().get() << ")";
meta.SetEdge(fwd_in_meta->GetMutableGradNode(), fwd_in_meta->OutRankInfo());
}
// Record TensorMeta
if (fwd_in.impl() && fwd_in.impl().get()) {
if (phi::DenseTensor::classof(fwd_in.impl().get())) {
// Only Copy Meta
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(fwd_in.impl().get());
PADDLE_ENFORCE_NE(
dense_tensor->meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor->meta());
meta.SetPlace(fwd_in.place());
} else if (phi::distributed::DistTensor::classof(fwd_in.impl().get())) {
const phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(fwd_in.impl().get());
const phi::DenseTensor& dense_tensor = dist_tensor->value();
PADDLE_ENFORCE_NE(
dense_tensor.meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_in.place());
// Set DistAttr
// Forward input DistTensor could be uninitialized.
PADDLE_ENFORCE_NE(
dist_tensor->dist_attr().empty(),
true,
common::errors::InvalidArgument(
"The forward input DistTensor's dist attr is empty."));
auto dist_attr = dist_tensor->dist_attr();
dist_attr.clean_partial_status();
meta.SetDistAttr(dist_attr);
meta.SetDistTensorGlobalDims(dist_tensor->dims());
SetIsRunAutoParallel(true);
} else if (phi::SparseCsrTensor::classof(fwd_in.impl().get())) {
phi::SparseCsrTensor* sparse_tensor =
static_cast<phi::SparseCsrTensor*>(fwd_in.impl().get());
const phi::DenseTensor dense_tensor =
static_cast<const phi::DenseTensor>(sparse_tensor->values());
PADDLE_ENFORCE_NE(
dense_tensor.dtype(),
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_in.place());
} else if (phi::SparseCooTensor::classof(fwd_in.impl().get())) {
phi::SparseCooTensor* sparse_tensor =
static_cast<phi::SparseCooTensor*>(fwd_in.impl().get());
const phi::DenseTensor dense_tensor =
static_cast<const phi::DenseTensor>(sparse_tensor->values());
PADDLE_ENFORCE_NE(
dense_tensor.dtype(),
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_in.place());
} else {
VLOG(7)
<< "Unable to initialize the DenseTensorMeta of GradSlotMeta with "
"non-DenseTensor argument.";
}
} else {
VLOG(5) << "Unable to initialize the DenseTensorMeta because the Tensor "
"is not initialized.";
}
}
/*
special func for matmul_double_grad etc. dx exists when x and y exists,
if stop_gradient of y is true, dy is None who is matmul_grad's out_put, ddy is
None, so dx = ddy * dout should be None.
*/
void GradNodeBase::SetGradOutMeta(const paddle::Tensor& fwd_in,
const AutogradMeta* fwd_out_meta,
size_t slot_rank) {
auto* fwd_in_meta = egr::EagerUtils::nullable_autograd_meta(fwd_in);
PADDLE_ENFORCE_LE(
(slot_rank + 1),
bwd_out_meta_.size(),
common::errors::InvalidArgument(
"Slot Rank should less equal than bwd_out_meta_ size, "
"since bwd_out_meta_ is designed to hold as same num as "
"backward outputs."));
auto& metas = bwd_out_meta_.at(slot_rank);
// Init stop gradient vector before use to avoid push back
if (metas.empty()) {
metas.resize(1);
}
auto& meta = metas[0];
if (VLOG_IS_ON(6) || FLAGS_enable_unique_name) {
// Record the forward input tensor name
meta.SetForwardTensorName(fwd_in.name());
}
// Set Stop_gradient
if (fwd_in_meta && !fwd_in_meta->StopGradient() && fwd_out_meta) {
meta.SetStopGradient(false);
} else {
meta.SetStopGradient(true);
}
// Set Adj Edges
if (fwd_in_meta && !fwd_in_meta->StopGradient() && fwd_out_meta) {
auto node = fwd_in_meta->GetMutableGradNode();
if (!node || !node.get()) {
fwd_in_meta->SetGradNode(
std::make_shared<egr::GradNodeAccumulation>(fwd_in));
}
VLOG(5) << "Add Edges for slot: " << slot_rank << ", the Edge is from "
<< this->name() << "(" << this << ")"
<< " to " << fwd_in_meta->GetMutableGradNode()->name() << "("
<< fwd_in_meta->GetMutableGradNode().get() << ")";
meta.SetEdge(fwd_in_meta->GetMutableGradNode(), fwd_in_meta->OutRankInfo());
}
// Record TensorMeta
if (fwd_in.impl() && fwd_in.impl().get()) {
if (phi::DenseTensor::classof(fwd_in.impl().get())) {
// Only Copy Meta
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(fwd_in.impl().get());
PADDLE_ENFORCE_NE(
dense_tensor->meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor->meta());
meta.SetPlace(fwd_in.place());
} else if (phi::distributed::DistTensor::classof(fwd_in.impl().get())) {
// Only Copy Meta
meta.SetDistAttr(
static_cast<phi::distributed::DistTensor*>(fwd_in.impl().get())
->dist_attr());
meta.SetDistTensorGlobalDims(
static_cast<phi::distributed::DistTensor*>(fwd_in.impl().get())
->dims());
SetIsRunAutoParallel(true);
auto dense_tensor =
static_cast<phi::distributed::DistTensor*>(fwd_in.impl().get())
->value();
PADDLE_ENFORCE_NE(
dense_tensor.meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_in.place());
}
} else {
VLOG(5) << "Unable to initialize the DenseTensorMeta of GradSlotMeta with "
"non-DenseTensor argument.";
}
}
/*
Special func for inplace ops in auto parallel. For now, this func is only used
in reshape_.
*/
void GradNodeBase::SetGradOutMeta(
const paddle::Tensor& fwd_in,
size_t slot_rank,
const phi::distributed::TensorDistAttr& fwd_in_dist_attr,
const phi::DDim& fwd_in_dims) {
auto* fwd_in_meta = egr::EagerUtils::nullable_autograd_meta(fwd_in);
PADDLE_ENFORCE_LE(
(slot_rank + 1),
bwd_out_meta_.size(),
common::errors::InvalidArgument(
"Slot Rank should less equal than bwd_out_meta_ size, "
"since bwd_out_meta_ is designed to hold as same num as "
"backward outputs."));
auto& metas = bwd_out_meta_.at(slot_rank);
// Init stop gradient vector before use to avoid push back
if (metas.empty()) {
metas.resize(1);
}
auto& meta = metas[0];
if (VLOG_IS_ON(6) || FLAGS_enable_unique_name) {
meta.SetForwardTensorName(fwd_in.name());
}
// Set Stop_gradient
if (fwd_in_meta) {
meta.SetStopGradient(fwd_in_meta->StopGradient());
} else {
meta.SetStopGradient(true);
}
// Set Adj Edges
if (fwd_in_meta && !fwd_in_meta->StopGradient()) {
auto node = fwd_in_meta->GetMutableGradNode();
if (!node || !node.get()) {
fwd_in_meta->SetGradNode(
std::make_shared<egr::GradNodeAccumulation>(fwd_in));
}
VLOG(5) << "Add Edges for slot: " << slot_rank << ", the Edge is from "
<< this->name() << "(" << this << ")"
<< " to " << fwd_in_meta->GetMutableGradNode()->name() << "("
<< fwd_in_meta->GetMutableGradNode().get() << ")";
meta.SetEdge(fwd_in_meta->GetMutableGradNode(), fwd_in_meta->OutRankInfo());
}
// Record TensorMeta
if (fwd_in.impl() && fwd_in.impl().get()) {
if (phi::distributed::DistTensor::classof(fwd_in.impl().get())) {
const phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(fwd_in.impl().get());
const phi::DenseTensor& dense_tensor = dist_tensor->value();
PADDLE_ENFORCE_NE(
dense_tensor.meta().dtype,
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_in.place());
// Set DistAttr
// Forward input DistTensor could be uninitialized.
PADDLE_ENFORCE_NE(
dist_tensor->dist_attr().empty(),
true,
common::errors::InvalidArgument(
"The forward input DistTensor's dist attr is empty."));
auto dist_attr = fwd_in_dist_attr;
dist_attr.clean_partial_status();
meta.SetDistAttr(dist_attr);
meta.SetDistTensorGlobalDims(fwd_in_dims);
SetIsRunAutoParallel(true);
} else {
VLOG(7)
<< "Unable to initialize the DenseTensorMeta of GradSlotMeta with "
"non-DistTensor argument.";
}
} else {
VLOG(7) << "Unable to initialize the DenseTensorMeta because the Tensor "
"is not initialized.";
}
}
void GradNodeBase::SetGradOutMeta(const std::vector<paddle::Tensor>& fwd_in,
size_t slot_rank) {
size_t slot_size = fwd_in.size();
PADDLE_ENFORCE_LE(
slot_rank,
(bwd_out_meta_.size() - 1),
common::errors::InvalidArgument(
"Slot Rank should less equal than bwd_out_meta_ size, "
"since bwd_out_meta_ is designed to hold as same num as "
"backward outputs."));
auto& metas = bwd_out_meta_.at(slot_rank);
// Init stop gradient vector before use to avoid push back
if (metas.size() < slot_size) {
metas.resize(slot_size);
}
for (size_t i = 0; i < slot_size; i++) {
const auto& fwd_in_tensor = fwd_in[i];
auto& meta = metas[i];
if (VLOG_IS_ON(6) || FLAGS_enable_unique_name) {
meta.SetForwardTensorName(fwd_in_tensor.name());
}
auto* fwd_in_meta = egr::EagerUtils::nullable_autograd_meta(fwd_in_tensor);
// Set Stop_gradient
if (fwd_in_meta) {
meta.SetStopGradient(fwd_in_meta->StopGradient());
}
// Set Adj Edges
if (fwd_in_meta && !fwd_in_meta->StopGradient()) {
auto node = fwd_in_meta->GetMutableGradNode();
if (!node || !node.get()) {
fwd_in_meta->SetGradNode(
std::make_shared<egr::GradNodeAccumulation>(fwd_in_tensor));
}
VLOG(5) << "Add Edges for slot: " << slot_rank << ", the Edge is from "
<< this->name() << "(" << this << ")"
<< " to " << fwd_in_meta->GetMutableGradNode()->name() << "("
<< fwd_in_meta->GetMutableGradNode().get() << ")";
meta.SetEdge(fwd_in_meta->GetMutableGradNode(),
fwd_in_meta->OutRankInfo());
}
// Record TensorMeta
if (fwd_in_tensor.impl() && fwd_in_tensor.impl().get()) {
if (phi::DenseTensor::classof(fwd_in_tensor.impl().get())) {
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(fwd_in_tensor.impl().get());
PADDLE_ENFORCE_NE(
dense_tensor->dtype(),
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor->meta());
meta.SetPlace(fwd_in_tensor.place());
} else if (phi::distributed::DistTensor::classof(
fwd_in_tensor.impl().get())) {
meta.SetDistAttr(static_cast<phi::distributed::DistTensor*>(
fwd_in_tensor.impl().get())
->dist_attr());
meta.SetDistTensorGlobalDims(static_cast<phi::distributed::DistTensor*>(
fwd_in_tensor.impl().get())
->dims());
SetIsRunAutoParallel(true);
auto dense_tensor = static_cast<phi::distributed::DistTensor*>(
fwd_in_tensor.impl().get())
->value();
PADDLE_ENFORCE_NE(
dense_tensor.dtype(),
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_in_tensor.place());
}
} else {
VLOG(7)
<< "Unable to initialize the DenseTensorMeta of GradSlotMeta with "
"non-DenseTensor argument.";
}
}
}
void GradNodeBase::SetGradOutMeta(
const std::vector<const paddle::Tensor*>& fwd_in, size_t slot_rank) {
size_t slot_size = fwd_in.size();
PADDLE_ENFORCE_LE(
slot_rank,
(bwd_out_meta_.size() - 1),
common::errors::InvalidArgument(
"Slot Rank should less equal than bwd_out_meta_ size, "
"since bwd_out_meta_ is designed to hold as same num as "
"backward outputs."));
auto& metas = bwd_out_meta_.at(slot_rank);
// Init stop gradient vector before use to avoid push back
if (metas.size() < slot_size) {
metas.resize(slot_size);
}
for (size_t i = 0; i < slot_size; i++) {
const auto& fwd_in_tensor = (*fwd_in[i]);
auto& meta = metas[i];
if (VLOG_IS_ON(6) || FLAGS_enable_unique_name) {
meta.SetForwardTensorName(fwd_in_tensor.name());
}
auto* fwd_in_meta = egr::EagerUtils::nullable_autograd_meta(fwd_in_tensor);
// Set Stop_gradient
if (fwd_in_meta) {
meta.SetStopGradient(fwd_in_meta->StopGradient());
}
// Set Adj Edges
if (fwd_in_meta && !fwd_in_meta->StopGradient()) {
auto node = fwd_in_meta->GetMutableGradNode();
if (!node || !node.get()) {
fwd_in_meta->SetGradNode(
std::make_shared<egr::GradNodeAccumulation>(fwd_in_tensor));
}
VLOG(5) << "Add Edges for slot: " << slot_rank << ", the Edge is from "
<< this->name() << "(" << this << ")"
<< " to " << fwd_in_meta->GetMutableGradNode()->name() << "("
<< fwd_in_meta->GetMutableGradNode().get() << ")";
meta.SetEdge(fwd_in_meta->GetMutableGradNode(),
fwd_in_meta->OutRankInfo());
}
// Record TensorMeta
if (fwd_in_tensor.impl() && fwd_in_tensor.impl().get()) {
if (phi::DenseTensor::classof(fwd_in_tensor.impl().get())) {
// Only Copy Meta
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(fwd_in_tensor.impl().get());
PADDLE_ENFORCE_NE(
dense_tensor->dtype(),
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor->meta());
meta.SetPlace(fwd_in_tensor.place());
} else if (phi::distributed::DistTensor::classof(
fwd_in_tensor.impl().get())) {
// Only Copy Meta
meta.SetDistAttr(static_cast<phi::distributed::DistTensor*>(
fwd_in_tensor.impl().get())
->dist_attr());
meta.SetDistTensorGlobalDims(static_cast<phi::distributed::DistTensor*>(
fwd_in_tensor.impl().get())
->dims());
SetIsRunAutoParallel(true);
auto dense_tensor = static_cast<phi::distributed::DistTensor*>(
fwd_in_tensor.impl().get())
->value();
PADDLE_ENFORCE_NE(
dense_tensor.dtype(),
phi::DataType::UNDEFINED,
common::errors::Fatal("Attempting to copy DenseTensorMeta "
"with phi::DataType::UNDEFINED,"
"which is illegal."));
meta.SetTensorMeta(dense_tensor.meta());
meta.SetPlace(fwd_in_tensor.place());
}
} else {
VLOG(7)
<< "Unable to initialize the DenseTensorMeta of GradSlotMeta with "
"non-DenseTensor argument.";
}
}
}
void GradNodeBase::SetDefaultGradInOutMeta() {
PADDLE_ENFORCE((bwd_out_meta_.size() == 1) && (bwd_in_meta_.size() == 1),
common::errors::PreconditionNotMet(
"We can only support 1 input and 1 output in default grad "
"meta setter, other size of inputs and outputs should "
"create with Setter and Getters"));
// Default stop_gradient is false and slot id is 0, slot size is 1;
bwd_out_meta_[0].resize(1);
bwd_in_meta_[0].resize(1);
}
int64_t GradNodeBase::RegisterGradientHook(
size_t slot_id, size_t rank, std::shared_ptr<egr::TensorHook>&& hook) {
gradient_hooks_.emplace(next_hook_id_,
std::make_tuple(slot_id, rank, std::move(hook)));
return next_hook_id_++;
}
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
GradNodeBase::ApplyGradientHooks(
const paddle::small_vector<std::vector<paddle::Tensor>,
kSlotSmallVectorSize>& tensors) {
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize> outs(
tensors.size());
for (auto& hook_pair : gradient_hooks_) {
size_t slot_id = std::get<0>(hook_pair.second);
size_t rank = std::get<1>(hook_pair.second);
auto hook = std::get<2>(hook_pair.second);
PADDLE_ENFORCE(slot_id < tensors.size(),
common::errors::Fatal(
"Slot_id from registered hook should be smaller than "
"slot size of grad_tensors"));
PADDLE_ENFORCE(rank < tensors[slot_id].size(),
common::errors::Fatal(
"rank of slot %d from registered hook should be smaller "
"than rank size of grad_tensors",
slot_id));
std::vector<paddle::Tensor>& slot_out = outs[slot_id];
slot_out.resize(tensors[slot_id].size());
paddle::Tensor& out = slot_out[rank];
if (!out.defined() || !out.has_allocation()) {
out = (*hook)(tensors[slot_id][rank]);
} else {
// If more than one hook is registered, the input to the next hook func
// should be the output of the previous hook
out = (*hook)(out);
}
}
for (size_t i = 0; i < outs.size(); i++) {
if (outs[i].empty() && (!tensors[i].empty())) {
outs[i].resize(tensors[i].size());
}
// TODO(Jiabin): Optimize this if we only add hook slot by slot
for (size_t j = 0; j < outs[i].size(); j++) {
if (!outs[i][j].defined() || !outs[i][j].initialized()) {
outs[i][j] = tensors[i][j];
}
CheckTensor(tensors[i][j], outs[i][j]);
}
}
return outs;
}
void GradNodeBase::HandleComplexGradToRealGrad(
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>*
out_grads) {
for (size_t slot_id = 0; slot_id < out_grads->size(); slot_id++) {
const std::vector<paddle::Tensor>& slot_out_grads = (*out_grads)[slot_id];
for (size_t rank_id = 0; rank_id < slot_out_grads.size(); rank_id++) {
if (bwd_out_meta_[slot_id].size() == 0) continue;
const GradSlotMeta& slot_meta = bwd_out_meta_[slot_id][rank_id];
PADDLE_ENFORCE(
slot_meta.HasTensorMeta() > 0,
common::errors::Fatal(
"We require TensorMeta in GradInputMeta() to obtain forward data "
"types."
"However, no TensorMeta is detected in bwd_out_meta_."));
auto fwd_data_type = paddle::framework::TransToProtoVarType(
slot_meta.GetTensorMeta().dtype);
const paddle::Tensor& grad = slot_out_grads[rank_id];
if (paddle::framework::IsComplexType(fwd_data_type)) continue;
if (!grad.impl()) continue;
// Only Handle Complex To Real for DenseTensor for now
if (phi::DenseTensor::classof(grad.impl().get())) {
phi::DenseTensor* grad_dense_tensor =
static_cast<phi::DenseTensor*>(grad.impl().get());
auto curr_data_type =
paddle::framework::TransToProtoVarType(grad_dense_tensor->type());
if (!paddle::framework::IsComplexType(curr_data_type)) continue;
// Convert Complex GradOut to Real
auto out = std::make_shared<phi::DenseTensor>();
paddle::framework::TransComplexToReal(
fwd_data_type, curr_data_type, *grad_dense_tensor, out.get());
(*out_grads)[slot_id][rank_id].set_impl(out);
} else if (phi::distributed::DistTensor::classof(grad.impl().get())) {
auto grad_dense_tensor =
static_cast<phi::distributed::DistTensor*>(grad.impl().get())
->value();
auto curr_data_type =
paddle::framework::TransToProtoVarType(grad_dense_tensor.type());
if (!paddle::framework::IsComplexType(curr_data_type)) continue;
if (grad_dense_tensor.dims().size() == -1) continue;
// Convert Complex GradOut to Real
auto out = std::make_shared<phi::DenseTensor>();
paddle::framework::TransComplexToReal(
fwd_data_type, curr_data_type, grad_dense_tensor, out.get());
*(static_cast<phi::distributed::DistTensor*>(
(*out_grads)[slot_id][rank_id].impl().get())
->unsafe_mutable_value()) = *(out.get());
}
}
}
}
std::vector<std::shared_ptr<GradNodeBase>> GradNodeBase::NextFunctions() {
std::vector<std::shared_ptr<GradNodeBase>> next_nodes;
const paddle::small_vector<std::vector<GradSlotMeta>, kSlotSmallVectorSize>&
metas = OutputMeta();
for (const auto& meta_list : metas) {
for (const GradSlotMeta& meta : meta_list) {
const auto& edge = meta.GetEdge();
std::shared_ptr<GradNodeBase> next_node = edge.GetMutableGradNode();
next_nodes.push_back(next_node);
}
}
return next_nodes;
}
uintptr_t GradNodeBase::GetPtr() const {
return reinterpret_cast<uintptr_t>(this);
}
int64_t GradNodeBase::RegisterNodePostHook(
std::shared_ptr<NodePostHookBase>&& hook) {
post_hooks_.emplace(next_post_hook_id_, std::move(hook));
return next_post_hook_id_++;
}
bool GradNodeBase::RemoveNodePostHook(int64_t hook_id) {
auto remove_cnt = post_hooks_.erase(hook_id);
if (remove_cnt == 0) {
return false;
}
return true;
}
bool GradNodeBase::HasNodePostHook() { return !post_hooks_.empty(); }
paddle::small_vector<std::vector<paddle::Tensor>, egr::kSlotSmallVectorSize>
GradNodeBase::ApplyNodePostHooks(
const paddle::small_vector<std::vector<paddle::Tensor>,
egr::kSlotSmallVectorSize>& grad_outputs,
const paddle::small_vector<std::vector<paddle::Tensor>,
egr::kSlotSmallVectorSize>& grad_inputs) {
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize> outs =
grad_outputs;
for (auto& iter : post_hooks_) {
auto hook = iter.second;
outs = (*hook)(outs, grad_inputs);
}
for (size_t i = 0; i < outs.size(); i++) {
if (outs[i].empty() && (!grad_outputs[i].empty())) {
outs[i].resize(grad_outputs[i].size());
}
for (size_t j = 0; j < outs[i].size(); j++) {
if (!outs[i][j].defined() || !outs[i][j].initialized()) {
outs[i][j] = grad_outputs[i][j];
} else {
CheckTensor(grad_outputs[i][j], outs[i][j]);
}
}
}
return outs;
}
void Edge::SetGradNode(const std::shared_ptr<GradNodeBase>& node) {
VLOG(7) << "Resetting Edge(" << this << ")'s Grad Node"
<< " from " << (grad_node_ ? grad_node_->name() : "nullptr") << "("
<< grad_node_.get() << ") to " << (node ? node->name() : "nullptr")
<< "(" << node.get() << ")";
grad_node_ = node;
}
} // namespace egr