// 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; std::shared_ptr DoubleHook( const std::shared_ptr& var) { // 1. create out var auto out_var = std::make_shared(var->Name()); out_var->SetType(var->Type()); out_var->SetDataType(var->DataType()); out_var->SetForwardDataType(var->ForwardDataType()); out_var->InnerSetOverridedStopGradient(var->InnerOverridedStopGradient()); // 2. get input and output var's tensor auto* out_tensor = out_var->MutableVar()->GetMutable(); auto& tensor = var->Var().Get(); out_tensor->Resize(tensor.dims()); // 3. double calc auto* data = tensor.data(); auto* out_data = out_tensor->mutable_data(platform::CPUPlace()); for (int64_t i = 0; i < out_tensor->numel(); ++i) { out_data[i] = data[i] * 2.0; } return out_var; } 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 VariableWrapper hook x->GradVarBase()->AddVariableWrapperHook( std::make_shared(DoubleHook)); // add Void hook int64_t hook_value = 0; x->GradVarBase()->AddVoidHook( std::make_shared>([&]() { hook_value = 10; })); // 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(); // verify VariableWrapper hook result 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); } // verify Void hook result ASSERT_EQ(hook_value, 10); 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 VariableWrapper hook x->GradVarBase()->AddVariableWrapperHook( std::make_shared(DoubleHook)); // add Void hook int64_t hook_value = 0; x->GradVarBase()->AddVoidHook( std::make_shared>([&]() { hook_value = 100; })); // 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(); // verify VariableWrapper hook result 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); } // verify Void hook result ASSERT_EQ(hook_value, 100); 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_ITSELF(elementwise_add); USE_OP_ITSELF(elementwise_add_grad);