// Copyright (c) 2020 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 #include #include #include #include "glog/logging.h" #include "gtest/gtest.h" #include "paddle/fluid/framework/op_registry.h" #include "paddle/fluid/imperative/basic_engine.h" #include "paddle/fluid/imperative/hooks.h" #include "paddle/fluid/imperative/tracer.h" #include "paddle/fluid/memory/memcpy.h" namespace platform = paddle::platform; namespace framework = paddle::framework; namespace memory = paddle::memory; DECLARE_bool(sort_sum_gradient); namespace paddle { namespace imperative { using vb_vector = std::vector>; using var_pair = std::pair; TEST(TestHooks, TestGradVarLeafBackwardHook) { // 1. prepare Tracer tracer; std::shared_ptr x(new VarBase(true, "x")); std::shared_ptr y(new VarBase(true, "y")); std::shared_ptr out(new VarBase(true, "out")); x->SetOverridedStopGradient(false); y->SetOverridedStopGradient(false); platform::CPUPlace place; std::vector src_data(10, 2.0); std::vector x_dims = {2, 5}; std::vector y_dims = {5, 2}; auto* x_tensor = x->MutableVar()->GetMutable(); auto* y_tensor = y->MutableVar()->GetMutable(); x_tensor->Resize(framework::make_ddim(x_dims)); auto* mutable_x = x_tensor->mutable_data(place); memory::Copy(place, mutable_x, place, src_data.data(), sizeof(float) * src_data.size()); y_tensor->Resize(framework::make_ddim(y_dims)); auto* mutable_y = y_tensor->mutable_data(place); memory::Copy(place, mutable_y, place, src_data.data(), sizeof(float) * src_data.size()); var_pair x_pair = var_pair("X", vb_vector(1, x)); var_pair y_pair = var_pair("Y", vb_vector(1, y)); var_pair out_pair = var_pair("Out", vb_vector(1, out)); NameVarBaseMap ins = {x_pair, y_pair}; NameVarBaseMap outs = {out_pair}; framework::AttributeMap mul_attr_map; mul_attr_map["use_mkldnn"] = false; // add GradAccumulatorPostHook x->GradVarBase()->AddMutableHook( std::make_shared( [=](VariableWrapper* grad) { auto* grad_tensor = grad->MutableVar()->GetMutable(); for (int i = 0; i < grad_tensor->numel(); ++i) { grad_tensor->mutable_data(place)[i] *= 2.0; } })); // 2. forward tracer.TraceOp("mul", ins, outs, mul_attr_map, place, true); ASSERT_EQ(x->GradVarBase()->GradOpNum(), 0UL); ASSERT_EQ(y->GradVarBase()->GradOpNum(), 0UL); ASSERT_EQ(out->GradVarBase()->GradOpNum(), 1UL); // 3. backward std::vector> tensors{out}; std::vector> grad_tensors{nullptr}; BasicEngine engine; engine.Init(tensors, grad_tensors); engine.Execute(); framework::LoDTensor x_grad; framework::TensorCopySync(x->GradVar().Get(), place, &x_grad); for (int i = 0; i < x_grad.numel(); ++i) { ASSERT_EQ(x_grad.data()[i], 8.0); } framework::LoDTensor y_grad; framework::TensorCopySync(y->GradVar().Get(), place, &y_grad); for (int i = 0; i < y_grad.numel(); ++i) { ASSERT_EQ(y_grad.data()[i], 4.0); } } void GradVarLeafBackwardHookWithGradAccmulatedTest() { // 1. prepare Tracer tracer; std::shared_ptr x(new VarBase(true, "x")); std::shared_ptr y(new VarBase(true, "y")); std::shared_ptr z(new VarBase(true, "z")); std::shared_ptr out_xy(new VarBase(true, "out_xy")); std::shared_ptr out_xz(new VarBase(true, "out_xz")); std::shared_ptr out(new VarBase(true, "out")); x->SetOverridedStopGradient(false); y->SetOverridedStopGradient(false); z->SetOverridedStopGradient(false); platform::CPUPlace place; std::vector src_data(10, 2.0); std::vector x_dims = {2, 5}; std::vector y_dims = {5, 2}; std::vector z_dims = {5, 2}; auto* x_tensor = x->MutableVar()->GetMutable(); auto* y_tensor = y->MutableVar()->GetMutable(); auto* z_tensor = z->MutableVar()->GetMutable(); x_tensor->Resize(framework::make_ddim(x_dims)); auto* mutable_x = x_tensor->mutable_data(place); memory::Copy(place, mutable_x, place, src_data.data(), sizeof(float) * src_data.size()); y_tensor->Resize(framework::make_ddim(y_dims)); auto* mutable_y = y_tensor->mutable_data(place); memory::Copy(place, mutable_y, place, src_data.data(), sizeof(float) * src_data.size()); z_tensor->Resize(framework::make_ddim(z_dims)); auto* mutable_z = z_tensor->mutable_data(place); memory::Copy(place, mutable_z, place, src_data.data(), sizeof(float) * src_data.size()); // add ReduceBackwardHook x->GradVarBase()->AddMutableHook( std::make_shared( [=](VariableWrapper* grad) { auto* grad_tensor = grad->MutableVar()->GetMutable(); for (int i = 0; i < grad_tensor->numel(); ++i) { grad_tensor->mutable_data(place)[i] *= 2.0; } })); // 2. forward var_pair x_pair = var_pair("X", vb_vector(1, x)); var_pair y_pair = var_pair("Y", vb_vector(1, y)); var_pair out_xy_pair = var_pair("Out", vb_vector(1, out_xy)); NameVarBaseMap ins = {x_pair, y_pair}; NameVarBaseMap outs = {out_xy_pair}; framework::AttributeMap mul_attr_map; mul_attr_map["use_mkldnn"] = false; tracer.TraceOp("mul", ins, outs, mul_attr_map, place, true); var_pair z_pair = var_pair("Y", vb_vector(1, z)); var_pair out_xz_pair = var_pair("Out", vb_vector(1, out_xz)); ins = {x_pair, z_pair}; outs = {out_xz_pair}; tracer.TraceOp("mul", ins, outs, mul_attr_map, place, true); var_pair xy_pair = var_pair("X", vb_vector(1, out_xy)); var_pair xz_pair = var_pair("Y", vb_vector(1, out_xz)); var_pair out_pair = var_pair("Out", vb_vector(1, out)); ins = {xy_pair, xz_pair}; outs = {out_pair}; framework::AttributeMap add_attr_map; tracer.TraceOp("elementwise_add", ins, outs, add_attr_map, place, true); ASSERT_EQ(x->GradVarBase()->GradOpNum(), 0UL); ASSERT_EQ(y->GradVarBase()->GradOpNum(), 0UL); ASSERT_EQ(z->GradVarBase()->GradOpNum(), 0UL); ASSERT_EQ(out->GradVarBase()->GradOpNum(), 1UL); // 3. backward std::vector> tensors{out}; std::vector> grad_tensors{nullptr}; BasicEngine engine; engine.Init(tensors, grad_tensors); engine.Execute(); framework::LoDTensor x_grad; framework::TensorCopySync(x->GradVar().Get(), place, &x_grad); for (int i = 0; i < x_grad.numel(); ++i) { ASSERT_EQ(x_grad.data()[i], 16.0); } framework::LoDTensor y_grad; framework::TensorCopySync(y->GradVar().Get(), place, &y_grad); for (int i = 0; i < y_grad.numel(); ++i) { ASSERT_EQ(y_grad.data()[i], 4.0); } framework::LoDTensor z_grad; framework::TensorCopySync(z->GradVar().Get(), place, &z_grad); for (int i = 0; i < z_grad.numel(); ++i) { ASSERT_EQ(z_grad.data()[i], 4.0); } } TEST(TestHooks, TestGradVarLeafBackwardHookWithGradAccmulated) { GradVarLeafBackwardHookWithGradAccmulatedTest(); } TEST(TestHooks, TestGradVarLeafBackwardHookWithSortedGradAccmulated) { FLAGS_sort_sum_gradient = true; GradVarLeafBackwardHookWithGradAccmulatedTest(); FLAGS_sort_sum_gradient = false; } } // namespace imperative } // namespace paddle USE_OP(mul); USE_OP(mul_grad); USE_OP(elementwise_add); USE_OP(elementwise_add_grad);