Skip to content

Commit d2934a7

Browse files
authored
Added RunBackward and HookUtils to Eager Dygraph (PaddlePaddle#37599)
1 parent fd41456 commit d2934a7

File tree

13 files changed

+997
-2
lines changed

13 files changed

+997
-2
lines changed

paddle/fluid/eager/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ cc_library(autograd_meta SRCS autograd_meta.cc DEPS pten pten_api)
66
cc_library(grad_node_info SRCS grad_node_info.cc DEPS pten pten_api)
77
cc_library(grad_tensor_holder SRCS grad_tensor_holder.cc DEPS grad_node_info gradient_accumulation)
88
cc_library(utils SRCS utils.cc DEPS pten pten_api global_utils layer proto_desc operator op_registry variable_helper memcpy scale_op autograd_meta)
9+
cc_library(backward SRCS backward.cc DEPS grad_tensor_holder utils autograd_meta grad_node_info)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
add_subdirectory(utils)
22
add_subdirectory(generated)
33

4-
cc_library(eager_api SRCS all.cc DEPS global_utils eager_scale)
4+
cc_library(eager_api SRCS all.cc DEPS tensor_utils hook_utils global_utils eager_scale)

paddle/fluid/eager/api/all.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@
1616

1717
#include "paddle/fluid/eager/api/generated/eager_generated/forwards/scale.h"
1818
#include "paddle/fluid/eager/api/utils/global_utils.h"
19+
#include "paddle/fluid/eager/api/utils/hook_utils.h"
20+
#include "paddle/fluid/eager/api/utils/tensor_utils.h"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
cc_library(tensor_utils SRCS tensor_utils.cc DEPS pten pten_api autograd_meta grad_node_info accumulation_node)
2+
cc_library(hook_utils SRCS hook_utils.cc DEPS pten tensor_utils autograd_meta grad_node_info utils accumulation_node)
23
cc_library(global_utils SRCS global_utils.cc DEPS place)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "paddle/fluid/eager/api/utils/hook_utils.h"
16+
#include "paddle/fluid/eager/accumulation/accumulation_node.h"
17+
#include "paddle/fluid/eager/api/utils/tensor_utils.h"
18+
#include "paddle/fluid/eager/autograd_meta.h"
19+
#include "paddle/fluid/eager/utils.h"
20+
#include "paddle/pten/core/dense_tensor.h"
21+
22+
namespace egr {
23+
24+
void RegisterGradientHookForTensor(
25+
const egr::EagerTensor& tensor,
26+
std::function<egr::EagerTensor(const egr::EagerTensor&)>& hook) {
27+
// Find grad_node and out_rank from AutogradMeta
28+
std::shared_ptr<GradNodeBase> grad_node = EagerUtils::grad_node(tensor);
29+
auto rank_info = EagerUtils::unsafe_autograd_meta(tensor)->OutRankInfo();
30+
31+
grad_node->RegisterGradientHook(rank_info.first, rank_info.second, hook);
32+
}
33+
34+
void RegisterReduceHookForTensor(const egr::EagerTensor& tensor,
35+
const std::function<void(void)>& hook) {
36+
// Find grad_node and out_rank from AutogradMeta
37+
std::shared_ptr<GradNodeBase> grad_node = EagerUtils::grad_node(tensor);
38+
39+
grad_node->RegisterReduceHook(hook);
40+
}
41+
42+
void RetainGradForTensor(const egr::EagerTensor& tensor) {
43+
// TODO(jiabin): Support More Tensor type here
44+
AutogradMeta* meta = EagerUtils::unsafe_autograd_meta(tensor);
45+
egr::EagerTensor* grad_tensor = meta->MutableGrad();
46+
47+
// Define Hook
48+
std::function<egr::EagerTensor(const egr::EagerTensor&)> hook =
49+
[grad_tensor](const egr::EagerTensor& t) {
50+
if (!grad_tensor) {
51+
PADDLE_THROW(paddle::platform::errors::Fatal(
52+
"Detected null grad_tensor."
53+
"Grad tensor in AutogradMeta of should not be nullptr"));
54+
}
55+
if (t.defined()) {
56+
// Simply Copy impl() to grad_tensor
57+
grad_tensor->set_impl(t.impl());
58+
return *grad_tensor;
59+
} else {
60+
PADDLE_ENFORCE_EQ(
61+
t.Var().IsInitialized(), true,
62+
paddle::platform::errors::Fatal(
63+
"Detected uninitialized variable, causing segmentation fault "
64+
"inside the hook."
65+
"Variable %s has to be initialized while we need to set it."
66+
"please check tensor initialization status.",
67+
t.name()));
68+
grad_tensor->MutableVar()
69+
->GetMutable<paddle::framework::LoDTensor>()
70+
->ShareDataWith(t.Var().Get<paddle::framework::LoDTensor>());
71+
return *grad_tensor;
72+
}
73+
};
74+
75+
if (IsLeafTensor(tensor)) {
76+
// Add RetainGrad as PostHook to AccumulationNode
77+
std::shared_ptr<GradNodeBase> grad_node = EagerUtils::grad_node(tensor);
78+
PADDLE_ENFORCE(
79+
grad_node.get() != nullptr,
80+
paddle::platform::errors::Fatal("Detected NULL grad_node"
81+
"Leaf tensor should have had grad_node "
82+
"with type: GradNodeAccumulation"));
83+
auto accumulation_grad_node =
84+
std::dynamic_pointer_cast<GradNodeAccumulation>(grad_node);
85+
accumulation_grad_node->RetainGrad(hook);
86+
87+
} else {
88+
// Append to GradientHooks
89+
RegisterGradientHookForTensor(tensor, hook);
90+
}
91+
}
92+
93+
} // namespace egr
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "paddle/fluid/eager/eager_tensor.h"
18+
#include "paddle/fluid/eager/grad_node_info.h"
19+
#include "paddle/pten/api/all.h"
20+
namespace egr {
21+
22+
void RegisterGradientHookForTensor(
23+
const egr::EagerTensor& tensor,
24+
std::function<egr::EagerTensor(const egr::EagerTensor&)>& hook);
25+
26+
void RegisterReduceHookForTensor(const egr::EagerTensor& tensor,
27+
const std::function<void(void)>& hook);
28+
void RetainGradForTensor(const egr::EagerTensor& tensor);
29+
30+
} // namespace egr

paddle/fluid/eager/backward.cc

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "paddle/fluid/eager/backward.h"
16+
#include <queue>
17+
18+
#include "paddle/fluid/eager/autograd_meta.h"
19+
#include "paddle/fluid/eager/grad_node_info.h"
20+
#include "paddle/fluid/eager/grad_tensor_holder.h"
21+
#include "paddle/fluid/eager/utils.h"
22+
23+
#include "paddle/fluid/platform/enforce.h"
24+
#include "paddle/fluid/platform/errors.h"
25+
26+
#include "glog/logging.h"
27+
28+
namespace egr {
29+
30+
std::unordered_map<GradNodeBase*, int> getInDegreeMap(
31+
const std::queue<GradNodeBase*>& init_queue) {
32+
// Calculate in_degree for each node
33+
// We can completely remove this pass, if in_degree were set during forward
34+
// pass
35+
std::unordered_map<GradNodeBase*, int> node_in_degree_map;
36+
37+
// Copy nodes
38+
std::queue<GradNodeBase*> queue = init_queue;
39+
std::unordered_set<GradNodeBase*> visited;
40+
41+
// Visit each node exactly once in any order
42+
while (!queue.empty()) {
43+
GradNodeBase* node = queue.front();
44+
queue.pop();
45+
46+
if (visited.count(node)) {
47+
continue;
48+
}
49+
visited.insert(node);
50+
51+
// Find and append next nodes
52+
const std::vector<std::vector<Edge>>& edges = node->GetEdges();
53+
for (const auto& edge_list : edges) {
54+
for (const Edge& edge : edge_list) {
55+
GradNodeBase* next_node = edge.GetMutableGradNode().get();
56+
// Update in_degree
57+
if (!node_in_degree_map.count(next_node))
58+
node_in_degree_map[next_node] = 0;
59+
node_in_degree_map[next_node]++;
60+
queue.push(next_node);
61+
}
62+
}
63+
}
64+
65+
return node_in_degree_map;
66+
}
67+
68+
void RunBackward(const std::vector<egr::EagerTensor>& tensors,
69+
const std::vector<egr::EagerTensor>& grad_tensors,
70+
bool retain_graph) {
71+
VLOG(6) << "Start Backward";
72+
// *Gradient Hook should happen at node-level
73+
// *Inplace version check should perform at node-level
74+
// *Cross-batch accumulation happens at forward pass
75+
76+
/* --- Initialization --- */
77+
// 1. Init queue with starting nodes
78+
// 2. Prepare initial input buffers
79+
std::queue<GradNodeBase*> queue;
80+
std::unordered_map<GradNodeBase*, std::unique_ptr<GradTensorHolder>>
81+
node_input_buffers_dict;
82+
for (size_t i = 0; i < tensors.size(); i++) {
83+
const egr::EagerTensor& tensor = tensors[i];
84+
85+
AutogradMeta* auto_grad_meta = EagerUtils::unsafe_autograd_meta(tensor);
86+
// Get grad input info from target tensors
87+
auto input_info = auto_grad_meta->OutRankInfo();
88+
89+
VLOG(2) << "Out Rank of Tensor is slot: " << input_info.first
90+
<< ", rank: " << input_info.second;
91+
// Get target GradNodeBase from target tensors
92+
GradNodeBase* grad_node = auto_grad_meta->GetMutableGradNode().get();
93+
94+
PADDLE_ENFORCE(grad_node,
95+
paddle::platform::errors::Fatal(
96+
"Detected null grad_node."
97+
"Grad Node is nullptr for grad input tensor %d",
98+
i));
99+
// Prepare GradTensorHolder
100+
if (!node_input_buffers_dict.count(grad_node)) {
101+
VLOG(6) << "Create Value for grad input tensor " << i;
102+
node_input_buffers_dict[grad_node] =
103+
std::make_unique<GradTensorHolder>(grad_node->InputMeta());
104+
}
105+
106+
if (grad_tensors.size() > 0) {
107+
PADDLE_ENFORCE(
108+
grad_tensors.size() == tensors.size(),
109+
paddle::platform::errors::Fatal(
110+
"Detected size mismatch between tensors and grad_tensors"
111+
"grad_tensors should either have "
112+
"size = 0 or same size as tensors"));
113+
// Feed given tensor if it's provided
114+
VLOG(6) << "Fill grad input tensor " << i << "with give grad tensor";
115+
node_input_buffers_dict[grad_node]->add(
116+
input_info.first, input_info.second, grad_tensors[i]);
117+
118+
} else {
119+
VLOG(6) << "Fill grad input tensor " << i << " with 1.0";
120+
// Initialize tensor with 1.0
121+
// Forward Tensor "tensor" is passed to indicate tensortype, datatype and
122+
// dims
123+
// GradTensorHolder will initialize another tensor with same tensortype,
124+
// datatype and dims but filled with 1.0
125+
node_input_buffers_dict[grad_node]->add(
126+
input_info.first, input_info.second, tensor, true /*fill_one=true*/);
127+
}
128+
129+
// Prepare queue
130+
queue.push(grad_node);
131+
}
132+
133+
VLOG(6) << "Update In degree Map for backward";
134+
// 3. Compute in_degree for each node
135+
std::unordered_map<GradNodeBase*, int> node_in_degree_map =
136+
getInDegreeMap(queue);
137+
138+
/* --- Topological Visit --- */
139+
// 1. Pop queue
140+
// 2. Run node
141+
// |- node(grads)
142+
// |- Prepare for next node
143+
// 3. Update queue
144+
VLOG(6) << "Run Backward";
145+
while (!queue.empty()) {
146+
GradNodeBase* node = queue.front();
147+
queue.pop();
148+
149+
// Run node: This is where Hook happens
150+
PADDLE_ENFORCE(
151+
node_input_buffers_dict.count(node),
152+
paddle::platform::errors::Fatal(
153+
"Unable to find next node in the InputBuufer"
154+
"Trying to run Node without configuring its GradTensorHolder"));
155+
156+
std::unique_ptr<GradTensorHolder> node_input_buffer =
157+
std::move(node_input_buffers_dict[node]);
158+
VLOG(6) << "Run Backward Kernel with input_buffer";
159+
// Run Backward Node and get outputs
160+
std::vector<std::vector<egr::EagerTensor>> grad_output_tensors =
161+
(*node)(node_input_buffer->Buffers());
162+
// TODO(jiabin): Should we erase it or find a more efficient way.
163+
node_input_buffers_dict.erase(node);
164+
165+
// Prepare GradTensorHolder for next node
166+
const std::vector<std::vector<Edge>>& edges = node->GetEdges();
167+
168+
PADDLE_ENFORCE(edges.size() == grad_output_tensors.size() || edges.empty(),
169+
paddle::platform::errors::Fatal(
170+
"Number of edges should be either empty ( for leaf node "
171+
") or the same as number of output grad tensors"));
172+
173+
for (size_t i = 0; i < edges.size(); i++) {
174+
for (size_t j = 0; j < edges[i].size(); j++) {
175+
const Edge& edge = edges[i][j];
176+
auto edge_rank = edge.GetEdgeRankInfo();
177+
// Since we make edge has as same rank as bwd outputs, we indexing them
178+
// with
179+
// the same rank(i, j)
180+
VLOG(6) << "Get Edge with slot: " << i << ", rank: " << j;
181+
egr::EagerTensor& grad_output_tensor = grad_output_tensors[i][j];
182+
if (!grad_output_tensor.defined() ||
183+
!grad_output_tensor.initialized()) {
184+
VLOG(6) << "We get grad_output_tensor with slot: " << i
185+
<< ", rank: " << j << " as uninitialized or undefined tensor";
186+
}
187+
GradNodeBase* next_node = edge.GetMutableGradNode().get();
188+
189+
if (!node_input_buffers_dict.count(next_node)) {
190+
node_input_buffers_dict[next_node] =
191+
std::make_unique<GradTensorHolder>(next_node->InputMeta());
192+
}
193+
VLOG(6) << "Sum grad inputs for edge slot: " << edge_rank.first
194+
<< ", rank: " << edge_rank.second;
195+
node_input_buffers_dict[next_node]->add(
196+
edge_rank.first, edge_rank.second, grad_output_tensor);
197+
198+
// Update queue
199+
node_in_degree_map[next_node]--;
200+
PADDLE_ENFORCE(node_in_degree_map[next_node] >= 0,
201+
paddle::platform::errors::Fatal(
202+
"Detected in-degree value smaller than zero."
203+
"Node's in-degree cannot be negative"));
204+
if (node_in_degree_map[next_node] == 0) {
205+
queue.emplace(std::move(next_node));
206+
}
207+
}
208+
}
209+
}
210+
}
211+
212+
} // namespace egr

paddle/fluid/eager/backward.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "paddle/fluid/eager/eager_tensor.h"
18+
#include "paddle/pten/api/all.h"
19+
20+
namespace egr {
21+
22+
// run_backward():
23+
// tensors corresponds to those lived in the backward graph
24+
// each grad_tensors[i] keeps the value for its corresponding tensors[i]
25+
void RunBackward(const std::vector<egr::EagerTensor> &tensors,
26+
const std::vector<egr::EagerTensor> &grad_tensors,
27+
bool retain_graph = false);
28+
29+
// Reserved for gradient()
30+
31+
} // namespace egr

paddle/fluid/eager/tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
set(eager_deps pten pten_api tensor_utils utils global_utils pten_tensor autograd_meta grad_node_info grad_tensor_holder gradient_accumulation accumulation_node)
1+
set(eager_deps pten pten_api hook_utils tensor_utils utils global_utils backward pten_tensor autograd_meta grad_node_info grad_tensor_holder gradient_accumulation accumulation_node)
22
set(fluid_deps tracer layer proto_desc operator op_registry variable_helper memcpy)
33

44
add_subdirectory(data_structure_tests)

0 commit comments

Comments
 (0)