Files
paddlepaddle--paddle/test/cpp/eager/data_structure_tests/accumulation_node_test.cc
T
2026-07-13 12:40:42 +08:00

418 lines
18 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/accumulation/accumulation_node.h"
#include <sstream>
#include "gtest/gtest.h"
#include "paddle/fluid/eager/api/utils/hook_utils.h"
#include "paddle/fluid/eager/eager_tensor.h"
#include "paddle/fluid/eager/grad_node_info.h"
#include "paddle/fluid/eager/grad_tensor_holder.h"
#include "paddle/fluid/eager/hooks.h"
#include "paddle/fluid/eager/utils.h"
#include "paddle/phi/api/lib/utils/allocator.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/selected_rows.h"
// TODO(jiabin): remove nolint here!!!
using namespace egr; // NOLINT
TEST(AccumulationNode, SelectedRowsAddToTensor) {
// Construct Eager Tensor
phi::DenseTensorMeta meta =
phi::DenseTensorMeta(phi::DataType::FLOAT32, common::make_ddim({1, 1}));
std::vector<int64_t> rows = {0};
std::shared_ptr<phi::SelectedRows> sr0 =
std::make_shared<phi::SelectedRows>(rows, 1);
sr0->mutable_value()->Resize(common::make_ddim({1, 1}));
sr0->mutable_value()->mutable_data<float>(phi::CPUPlace())[0] =
static_cast<float>(10.0f);
paddle::Tensor et0 = paddle::Tensor(sr0);
std::shared_ptr<phi::DenseTensor> dt1 = std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(phi::CPUPlace())
.get(),
meta);
dt1->mutable_data<float>(phi::CPUPlace())[0] = static_cast<float>(20.0f);
paddle::Tensor et1 = paddle::Tensor(dt1);
std::shared_ptr<phi::DenseTensor> input_dt =
std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(
phi::CPUPlace())
.get(),
meta);
paddle::Tensor input_et = paddle::Tensor(input_dt);
auto grad_meta = EagerUtils::autograd_meta(&input_et);
// Initialize Grad Tensor
std::shared_ptr<phi::SelectedRows> grad_dt =
std::make_shared<phi::SelectedRows>(rows, 1);
grad_dt->mutable_value()->Resize(common::make_ddim({1, 1}));
grad_dt->mutable_value()->mutable_data<float>(phi::CPUPlace())[0] =
static_cast<float>(0.0f);
grad_meta->MutableGrad()->set_impl(grad_dt);
// AccumulationNode
auto node = std::make_shared<GradNodeAccumulation>(input_et);
grad_meta->SetGradNode(node);
grad_meta->SetStopGradient(false);
// operator()
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
et0_vec = {{et0}};
paddle::Tensor ret_et0 = node->operator()(et0_vec)[0][0];
auto* ret_et0_ptr =
std::dynamic_pointer_cast<phi::SelectedRows>(ret_et0.impl())
->value()
.data<float>();
PADDLE_ENFORCE_EQ(ret_et0_ptr[0],
static_cast<float>(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the selected rows "
"should be 10.0f."));
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
et1_vec = {{et1}};
paddle::Tensor ret_et1 = node->operator()(et1_vec)[0][0];
auto* ret_et1_ptr =
std::dynamic_pointer_cast<phi::DenseTensor>(ret_et1.impl())
->data<float>();
PADDLE_ENFORCE_EQ(ret_et1_ptr[0],
static_cast<float>(20.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 20.0f"));
// Check Retain Grad
PADDLE_ENFORCE_EQ(
std::dynamic_pointer_cast<phi::SelectedRows>(et0.impl())
->value()
.data<float>()[0],
static_cast<float>(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor should be "
"10.0f"));
paddle::Tensor* grad = EagerUtils::mutable_grad(input_et);
auto* grad_ptr =
std::dynamic_pointer_cast<phi::DenseTensor>(grad->impl())->data<float>();
PADDLE_ENFORCE_EQ(grad_ptr[0],
static_cast<float>(30.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 30.0f"));
}
TEST(AccumulationNode, SelectedRowsMerge) {
// Construct Eager Tensor
phi::DenseTensorMeta meta =
phi::DenseTensorMeta(phi::DataType::FLOAT32, common::make_ddim({1, 1}));
std::vector<int64_t> rows = {0};
std::shared_ptr<phi::SelectedRows> sr0 =
std::make_shared<phi::SelectedRows>(rows, 1);
sr0->mutable_value()->Resize(common::make_ddim({1, 1}));
sr0->mutable_value()->mutable_data<float>(phi::CPUPlace())[0] =
static_cast<float>(10.0f);
paddle::Tensor et0 = paddle::Tensor(sr0);
std::shared_ptr<phi::SelectedRows> sr1 =
std::make_shared<phi::SelectedRows>(rows, 1);
sr1->mutable_value()->Resize(common::make_ddim({1, 1}));
sr1->mutable_value()->mutable_data<float>(phi::CPUPlace())[0] =
static_cast<float>(20.0f);
paddle::Tensor et1 = paddle::Tensor(sr1);
std::shared_ptr<phi::DenseTensor> input_dt =
std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(
phi::CPUPlace())
.get(),
meta);
paddle::Tensor input_et = paddle::Tensor(input_dt);
auto grad_meta = EagerUtils::autograd_meta(&input_et);
// Initialize Grad Tensor
std::shared_ptr<phi::SelectedRows> grad_dt =
std::make_shared<phi::SelectedRows>(rows, 1);
grad_dt->mutable_value()->Resize(common::make_ddim({1, 1}));
grad_dt->mutable_value()->mutable_data<float>(phi::CPUPlace())[0] =
static_cast<float>(0.0f);
grad_meta->MutableGrad()->set_impl(grad_dt);
// AccumulationNode
auto node = std::make_shared<GradNodeAccumulation>(input_et);
grad_meta->SetGradNode(node);
grad_meta->SetStopGradient(false);
// operator()
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
et0_vec = {{et0}};
paddle::Tensor ret_et0 = node->operator()(et0_vec)[0][0];
auto* ret_et0_ptr =
std::dynamic_pointer_cast<phi::SelectedRows>(ret_et0.impl())
->value()
.data<float>();
PADDLE_ENFORCE_EQ(ret_et0_ptr[0],
static_cast<float>(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the selected rows "
"should be 10.0f."));
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
et1_vec = {{et1}};
paddle::Tensor ret_et1 = node->operator()(et1_vec)[0][0];
auto* ret_et1_ptr =
std::dynamic_pointer_cast<phi::SelectedRows>(ret_et1.impl())
->value()
.data<float>();
PADDLE_ENFORCE_EQ(ret_et1_ptr[0],
static_cast<float>(20.0f),
common::errors::InvalidArgument(
"The value of the first element of the selected rows "
"should be 20.0f."));
// Check Retain Grad
PADDLE_ENFORCE_EQ(std::dynamic_pointer_cast<phi::SelectedRows>(et0.impl())
->value()
.data<float>()[0],
static_cast<float>(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the selected rows "
"should be 10.0f."));
paddle::Tensor* grad = EagerUtils::mutable_grad(input_et);
auto* grad_ptr = std::dynamic_pointer_cast<phi::SelectedRows>(grad->impl())
->value()
.data<float>();
PADDLE_ENFORCE_EQ(grad_ptr[0],
static_cast<float>(30.0f),
common::errors::InvalidArgument(
"The value of the first element of the selected rows "
"should be 30.0f."));
}
TEST(AccumulationNode, SelectedRowsAddTensor) {
// Construct Eager Tensor
phi::DenseTensorMeta meta =
phi::DenseTensorMeta(phi::DataType::FLOAT32, common::make_ddim({1, 1}));
std::vector<int64_t> rows = {0};
std::shared_ptr<phi::SelectedRows> sr0 =
std::make_shared<phi::SelectedRows>(rows, 1);
sr0->mutable_value()->Resize(common::make_ddim({1, 1}));
sr0->mutable_value()->mutable_data<float>(phi::CPUPlace())[0] =
static_cast<float>(10.0f);
paddle::Tensor et0 = paddle::Tensor(sr0);
std::shared_ptr<phi::SelectedRows> sr1 =
std::make_shared<phi::SelectedRows>(rows, 1);
sr1->mutable_value()->Resize(common::make_ddim({1, 1}));
sr1->mutable_value()->mutable_data<float>(phi::CPUPlace())[0] =
static_cast<float>(20.0f);
paddle::Tensor et1 = paddle::Tensor(sr1);
std::shared_ptr<phi::DenseTensor> input_dt =
std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(
phi::CPUPlace())
.get(),
meta);
paddle::Tensor input_et = paddle::Tensor(input_dt);
auto grad_meta = EagerUtils::autograd_meta(&input_et);
// Initialize Grad Tensor
std::shared_ptr<phi::DenseTensor> grad_dt =
std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(
phi::CPUPlace())
.get(),
meta);
grad_dt->mutable_data<float>(phi::CPUPlace())[0] = static_cast<float>(0.0f);
grad_meta->MutableGrad()->set_impl(grad_dt);
// AccumulationNode
auto node = std::make_shared<GradNodeAccumulation>(input_et);
grad_meta->SetGradNode(node);
grad_meta->SetStopGradient(false);
// operator()
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
et0_vec = {{et0}};
paddle::Tensor ret_et0 = node->operator()(et0_vec)[0][0];
auto* ret_et0_ptr =
std::dynamic_pointer_cast<phi::SelectedRows>(ret_et0.impl())
->value()
.data<float>();
PADDLE_ENFORCE_EQ(ret_et0_ptr[0],
static_cast<float>(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the selected rows "
"should be 10.0f."));
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
et1_vec = {{et1}};
paddle::Tensor ret_et1 = node->operator()(et1_vec)[0][0];
auto* ret_et1_ptr =
std::dynamic_pointer_cast<phi::SelectedRows>(ret_et1.impl())
->value()
.data<float>();
PADDLE_ENFORCE_EQ(ret_et1_ptr[0],
static_cast<float>(20.0f),
common::errors::InvalidArgument(
"The value of the first element of the selected rows "
"should be 20.0f"));
// Check Retain Grad
PADDLE_ENFORCE_EQ(std::dynamic_pointer_cast<phi::SelectedRows>(et0.impl())
->value()
.data<float>()[0],
static_cast<float>(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the selected rows "
"should be 10.0f"));
paddle::Tensor* grad = EagerUtils::mutable_grad(input_et);
auto* grad_ptr =
std::dynamic_pointer_cast<phi::DenseTensor>(grad->impl())->data<float>();
PADDLE_ENFORCE_EQ(grad_ptr[0],
static_cast<float>(30.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 30.0f"));
}
TEST(AccumulationNode, Tensor) {
// Construct Eager Tensor
phi::DenseTensorMeta meta =
phi::DenseTensorMeta(phi::DataType::FLOAT16, common::make_ddim({1, 1}));
std::shared_ptr<phi::DenseTensor> dt0 = std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(phi::CPUPlace())
.get(),
meta);
dt0->mutable_data<phi::dtype::float16>(phi::CPUPlace())[0] =
phi::dtype::float16(10.0f);
paddle::Tensor et0 = paddle::Tensor(dt0);
std::shared_ptr<phi::DenseTensor> dt1 = std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(phi::CPUPlace())
.get(),
meta);
dt1->mutable_data<phi::dtype::float16>(phi::CPUPlace())[0] =
phi::dtype::float16(20.0f);
paddle::Tensor et1 = paddle::Tensor(dt1);
std::shared_ptr<phi::DenseTensor> input_dt =
std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(
phi::CPUPlace())
.get(),
meta);
paddle::Tensor input_et = paddle::Tensor(input_dt);
auto grad_meta = EagerUtils::autograd_meta(&input_et);
// Initialize Grad Tensor
std::shared_ptr<phi::DenseTensor> grad_dt =
std::make_shared<phi::DenseTensor>(
std::make_unique<paddle::experimental::DefaultAllocator>(
phi::CPUPlace())
.get(),
meta);
grad_dt->mutable_data<phi::dtype::float16>(phi::CPUPlace())[0] =
phi::dtype::float16(0.0f);
grad_meta->MutableGrad()->set_impl(grad_dt);
// AccumulationNode
auto node = std::make_shared<GradNodeAccumulation>(input_et);
grad_meta->SetGradNode(node);
grad_meta->SetStopGradient(false);
// operator()
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
et0_vec = {{et0}};
paddle::Tensor ret_et0 = node->operator()(et0_vec)[0][0];
auto* ret_et0_ptr =
std::dynamic_pointer_cast<phi::DenseTensor>(ret_et0.impl())
->data<phi::dtype::float16>();
PADDLE_ENFORCE_EQ(ret_et0_ptr[0],
phi::dtype::float16(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 10.0f."));
paddle::small_vector<std::vector<paddle::Tensor>, kSlotSmallVectorSize>
et1_vec = {{et1}};
paddle::Tensor ret_et1 = node->operator()(et1_vec)[0][0];
auto* ret_et1_ptr =
std::dynamic_pointer_cast<phi::DenseTensor>(ret_et1.impl())
->data<phi::dtype::float16>();
PADDLE_ENFORCE_EQ(ret_et1_ptr[0],
phi::dtype::float16(20.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 20.0f"));
// Check Retain Grad
PADDLE_ENFORCE_EQ(std::dynamic_pointer_cast<phi::DenseTensor>(et0.impl())
->data<phi::dtype::float16>()[0],
phi::dtype::float16(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 10.0f"));
paddle::Tensor* grad = EagerUtils::mutable_grad(input_et);
auto* grad_ptr = std::dynamic_pointer_cast<phi::DenseTensor>(grad->impl())
->data<phi::dtype::float16>();
PADDLE_ENFORCE_EQ(grad_ptr[0],
phi::dtype::float16(30.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 30.0f"));
// Reduce Hook case 1: Call RegisterReduceHook and run operator()
VLOG(6) << "Test Reduce Hook";
PADDLE_ENFORCE_EQ(std::dynamic_pointer_cast<phi::DenseTensor>(et0.impl())
->data<phi::dtype::float16>()[0],
phi::dtype::float16(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 10.0f"));
auto reduce_hook_1 = [&]() -> void {
auto* input_et_ptr =
std::dynamic_pointer_cast<phi::DenseTensor>(input_et.impl())
->mutable_data<phi::dtype::float16>(phi::CPUPlace());
input_et_ptr[0] = 36.0;
VLOG(6) << "Running Reduce Hook";
};
node->RegisterReduceHook(std::make_shared<egr::CppVoidHook>(reduce_hook_1));
// operator()
paddle::Tensor _ret = node->operator()(et0_vec)[0][0];
// Check operator() result, should be 36.0
auto* _ret_ptr = std::dynamic_pointer_cast<phi::DenseTensor>(_ret.impl())
->data<phi::dtype::float16>();
PADDLE_ENFORCE_EQ(_ret_ptr[0],
phi::dtype::float16(10.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 10.0f"));
// Check Retain Grad, should be 36.0
auto* _ret_input_et_ptr =
std::dynamic_pointer_cast<phi::DenseTensor>(input_et.impl())
->data<phi::dtype::float16>();
PADDLE_ENFORCE_EQ(_ret_input_et_ptr[0],
phi::dtype::float16(36.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 36.0f"));
// Reduce Hook case 2: Call RegisterReduceHook and ApplyReduceHooks directly
VLOG(6) << "Test Reduce Hook";
auto reduce_hook_2 = [&]() -> void {
auto* ret_et0_ptr =
std::dynamic_pointer_cast<phi::DenseTensor>(et0.impl())
->mutable_data<phi::dtype::float16>(phi::CPUPlace());
ret_et0_ptr[0] = 100.0; // set to 100.0
VLOG(6) << "Running Reduce Hook";
};
node->RegisterReduceHook(std::make_shared<egr::CppVoidHook>(reduce_hook_2));
node->ApplyReduceHooks();
// Check ApplyReduceHooks result
PADDLE_ENFORCE_EQ(std::dynamic_pointer_cast<phi::DenseTensor>(et0.impl())
->data<phi::dtype::float16>()[0],
phi::dtype::float16(100.0f),
common::errors::InvalidArgument(
"The value of the first element of the dense tensor "
"should be 100.0f"));
}