// 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 #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 rows = {0}; std::shared_ptr sr0 = std::make_shared(rows, 1); sr0->mutable_value()->Resize(common::make_ddim({1, 1})); sr0->mutable_value()->mutable_data(phi::CPUPlace())[0] = static_cast(10.0f); paddle::Tensor et0 = paddle::Tensor(sr0); std::shared_ptr dt1 = std::make_shared( std::make_unique(phi::CPUPlace()) .get(), meta); dt1->mutable_data(phi::CPUPlace())[0] = static_cast(20.0f); paddle::Tensor et1 = paddle::Tensor(dt1); std::shared_ptr input_dt = std::make_shared( std::make_unique( 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 grad_dt = std::make_shared(rows, 1); grad_dt->mutable_value()->Resize(common::make_ddim({1, 1})); grad_dt->mutable_value()->mutable_data(phi::CPUPlace())[0] = static_cast(0.0f); grad_meta->MutableGrad()->set_impl(grad_dt); // AccumulationNode auto node = std::make_shared(input_et); grad_meta->SetGradNode(node); grad_meta->SetStopGradient(false); // operator() paddle::small_vector, kSlotSmallVectorSize> et0_vec = {{et0}}; paddle::Tensor ret_et0 = node->operator()(et0_vec)[0][0]; auto* ret_et0_ptr = std::dynamic_pointer_cast(ret_et0.impl()) ->value() .data(); PADDLE_ENFORCE_EQ(ret_et0_ptr[0], static_cast(10.0f), common::errors::InvalidArgument( "The value of the first element of the selected rows " "should be 10.0f.")); paddle::small_vector, kSlotSmallVectorSize> et1_vec = {{et1}}; paddle::Tensor ret_et1 = node->operator()(et1_vec)[0][0]; auto* ret_et1_ptr = std::dynamic_pointer_cast(ret_et1.impl()) ->data(); PADDLE_ENFORCE_EQ(ret_et1_ptr[0], static_cast(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(et0.impl()) ->value() .data()[0], static_cast(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(grad->impl())->data(); PADDLE_ENFORCE_EQ(grad_ptr[0], static_cast(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 rows = {0}; std::shared_ptr sr0 = std::make_shared(rows, 1); sr0->mutable_value()->Resize(common::make_ddim({1, 1})); sr0->mutable_value()->mutable_data(phi::CPUPlace())[0] = static_cast(10.0f); paddle::Tensor et0 = paddle::Tensor(sr0); std::shared_ptr sr1 = std::make_shared(rows, 1); sr1->mutable_value()->Resize(common::make_ddim({1, 1})); sr1->mutable_value()->mutable_data(phi::CPUPlace())[0] = static_cast(20.0f); paddle::Tensor et1 = paddle::Tensor(sr1); std::shared_ptr input_dt = std::make_shared( std::make_unique( 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 grad_dt = std::make_shared(rows, 1); grad_dt->mutable_value()->Resize(common::make_ddim({1, 1})); grad_dt->mutable_value()->mutable_data(phi::CPUPlace())[0] = static_cast(0.0f); grad_meta->MutableGrad()->set_impl(grad_dt); // AccumulationNode auto node = std::make_shared(input_et); grad_meta->SetGradNode(node); grad_meta->SetStopGradient(false); // operator() paddle::small_vector, kSlotSmallVectorSize> et0_vec = {{et0}}; paddle::Tensor ret_et0 = node->operator()(et0_vec)[0][0]; auto* ret_et0_ptr = std::dynamic_pointer_cast(ret_et0.impl()) ->value() .data(); PADDLE_ENFORCE_EQ(ret_et0_ptr[0], static_cast(10.0f), common::errors::InvalidArgument( "The value of the first element of the selected rows " "should be 10.0f.")); paddle::small_vector, kSlotSmallVectorSize> et1_vec = {{et1}}; paddle::Tensor ret_et1 = node->operator()(et1_vec)[0][0]; auto* ret_et1_ptr = std::dynamic_pointer_cast(ret_et1.impl()) ->value() .data(); PADDLE_ENFORCE_EQ(ret_et1_ptr[0], static_cast(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(et0.impl()) ->value() .data()[0], static_cast(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(grad->impl()) ->value() .data(); PADDLE_ENFORCE_EQ(grad_ptr[0], static_cast(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 rows = {0}; std::shared_ptr sr0 = std::make_shared(rows, 1); sr0->mutable_value()->Resize(common::make_ddim({1, 1})); sr0->mutable_value()->mutable_data(phi::CPUPlace())[0] = static_cast(10.0f); paddle::Tensor et0 = paddle::Tensor(sr0); std::shared_ptr sr1 = std::make_shared(rows, 1); sr1->mutable_value()->Resize(common::make_ddim({1, 1})); sr1->mutable_value()->mutable_data(phi::CPUPlace())[0] = static_cast(20.0f); paddle::Tensor et1 = paddle::Tensor(sr1); std::shared_ptr input_dt = std::make_shared( std::make_unique( 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 grad_dt = std::make_shared( std::make_unique( phi::CPUPlace()) .get(), meta); grad_dt->mutable_data(phi::CPUPlace())[0] = static_cast(0.0f); grad_meta->MutableGrad()->set_impl(grad_dt); // AccumulationNode auto node = std::make_shared(input_et); grad_meta->SetGradNode(node); grad_meta->SetStopGradient(false); // operator() paddle::small_vector, kSlotSmallVectorSize> et0_vec = {{et0}}; paddle::Tensor ret_et0 = node->operator()(et0_vec)[0][0]; auto* ret_et0_ptr = std::dynamic_pointer_cast(ret_et0.impl()) ->value() .data(); PADDLE_ENFORCE_EQ(ret_et0_ptr[0], static_cast(10.0f), common::errors::InvalidArgument( "The value of the first element of the selected rows " "should be 10.0f.")); paddle::small_vector, kSlotSmallVectorSize> et1_vec = {{et1}}; paddle::Tensor ret_et1 = node->operator()(et1_vec)[0][0]; auto* ret_et1_ptr = std::dynamic_pointer_cast(ret_et1.impl()) ->value() .data(); PADDLE_ENFORCE_EQ(ret_et1_ptr[0], static_cast(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(et0.impl()) ->value() .data()[0], static_cast(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(grad->impl())->data(); PADDLE_ENFORCE_EQ(grad_ptr[0], static_cast(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 dt0 = std::make_shared( std::make_unique(phi::CPUPlace()) .get(), meta); dt0->mutable_data(phi::CPUPlace())[0] = phi::dtype::float16(10.0f); paddle::Tensor et0 = paddle::Tensor(dt0); std::shared_ptr dt1 = std::make_shared( std::make_unique(phi::CPUPlace()) .get(), meta); dt1->mutable_data(phi::CPUPlace())[0] = phi::dtype::float16(20.0f); paddle::Tensor et1 = paddle::Tensor(dt1); std::shared_ptr input_dt = std::make_shared( std::make_unique( 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 grad_dt = std::make_shared( std::make_unique( phi::CPUPlace()) .get(), meta); grad_dt->mutable_data(phi::CPUPlace())[0] = phi::dtype::float16(0.0f); grad_meta->MutableGrad()->set_impl(grad_dt); // AccumulationNode auto node = std::make_shared(input_et); grad_meta->SetGradNode(node); grad_meta->SetStopGradient(false); // operator() paddle::small_vector, kSlotSmallVectorSize> et0_vec = {{et0}}; paddle::Tensor ret_et0 = node->operator()(et0_vec)[0][0]; auto* ret_et0_ptr = std::dynamic_pointer_cast(ret_et0.impl()) ->data(); 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, kSlotSmallVectorSize> et1_vec = {{et1}}; paddle::Tensor ret_et1 = node->operator()(et1_vec)[0][0]; auto* ret_et1_ptr = std::dynamic_pointer_cast(ret_et1.impl()) ->data(); 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(et0.impl()) ->data()[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(grad->impl()) ->data(); 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(et0.impl()) ->data()[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(input_et.impl()) ->mutable_data(phi::CPUPlace()); input_et_ptr[0] = 36.0; VLOG(6) << "Running Reduce Hook"; }; node->RegisterReduceHook(std::make_shared(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(_ret.impl()) ->data(); 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(input_et.impl()) ->data(); 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(et0.impl()) ->mutable_data(phi::CPUPlace()); ret_et0_ptr[0] = 100.0; // set to 100.0 VLOG(6) << "Running Reduce Hook"; }; node->RegisterReduceHook(std::make_shared(reduce_hook_2)); node->ApplyReduceHooks(); // Check ApplyReduceHooks result PADDLE_ENFORCE_EQ(std::dynamic_pointer_cast(et0.impl()) ->data()[0], phi::dtype::float16(100.0f), common::errors::InvalidArgument( "The value of the first element of the dense tensor " "should be 100.0f")); }