Add dygraph double grad implementation (#22939)

* add double grad implementation for dygraph, test=develop

* polish code, add uts, test=develop

* fix place bug, test=develop

* polish codes, add more uts for coverages, test=develop

* add no_grad_set, test=develop

* add star gan ut, test=develop

* follow comments, test=develop
revert-23830-2.0-beta
Zeng Jinle 5 years ago committed by GitHub
parent 995a6376f7
commit a31d7328b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -209,14 +209,13 @@ class SingleGradOpMaker<imperative::OpBase>
public:
using GradOpBaseMakerBase::GradOpBaseMakerBase;
std::vector<std::shared_ptr<imperative::OpBase>> operator()() const {
std::vector<std::shared_ptr<imperative::OpBase>> retv{
std::make_shared<imperative::OpBase>()};
std::shared_ptr<imperative::GradOpNode> operator()() const {
auto node = this->NewGradNode();
{
imperative::TracedGradOp grad_op(retv.front());
this->Apply(&grad_op);
imperative::TracedGradOp traced_grad_op(node);
this->Apply(&traced_grad_op);
}
return retv;
return node->empty() ? nullptr : node;
}
protected:
@ -262,8 +261,9 @@ class EmptyGradOpMaker<imperative::OpBase> final
: public imperative::GradOpBaseMakerBase {
public:
using GradOpBaseMakerBase::GradOpBaseMakerBase;
std::vector<std::shared_ptr<imperative::OpBase>> operator()() const final {
return {};
std::shared_ptr<imperative::GradOpNode> operator()() const final {
return nullptr;
}
};

@ -15,6 +15,7 @@
#include "paddle/fluid/framework/no_need_buffer_vars_inference.h"
#include <string>
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/imperative/saved_variable_wrapper_list.h"
namespace paddle {
namespace framework {

@ -56,7 +56,7 @@ using GradOpMakerFN = std::function<std::vector<std::unique_ptr<OpDesc>>(
const std::vector<BlockDesc*>& grad_block)>;
using DygraphGradOpMakerFN =
std::function<std::vector<std::shared_ptr<imperative::OpBase>>(
std::function<std::shared_ptr<imperative::GradOpNode>(
const std::string& /*op_type*/,
const imperative::NameVarBaseMap& /*var_base_map_in*/,
const imperative::NameVarBaseMap& /*var_base_map_out*/,

@ -6,7 +6,8 @@ cc_library(gradient_accumulator SRCS gradient_accumulator.cc DEPS blas operator
add_subdirectory(jit)
cc_library(tracer SRCS tracer.cc DEPS layer engine program_desc_tracer)
cc_library(engine SRCS engine.cc DEPS layer gradient_accumulator)
cc_library(basic_engine SRCS basic_engine.cc DEPS layer gradient_accumulator)
cc_library(engine SRCS basic_engine.cc partial_grad_engine.cc DEPS layer gradient_accumulator)
cc_library(imperative_profiler SRCS profiler.cc)
if(NOT WIN32)
if(WITH_NCCL)

File diff suppressed because it is too large Load Diff

@ -0,0 +1,57 @@
// Copyright (c) 2018 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.
#pragma once
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
#include "paddle/fluid/imperative/backward_strategy.h"
#include "paddle/fluid/imperative/engine.h"
#include "paddle/fluid/imperative/gradient_accumulator.h"
namespace paddle {
namespace imperative {
class VarBase;
class OpBase;
class BasicEngine : public Engine {
public:
void Init(VarBase* var, const detail::BackwardStrategy& strategy);
void Execute() override;
private:
void PrepareDeps();
void CheckBackwardInputs(const OpBase& op);
void PrepareGradAccumulators(const OpBase& op);
void Clear();
private:
std::shared_ptr<GradOpNode> init_node_;
detail::BackwardStrategy backward_strategy_;
std::unordered_map<GradOpNode*, size_t> node_deps_;
std::unordered_map<VariableWrapper*, std::unique_ptr<GradientAccumulator>>
accumulators_;
std::vector<std::pair<GradientAccumulator*, std::shared_ptr<VariableWrapper>>>
need_accu_var_list_;
};
} // namespace imperative
} // namespace paddle

@ -18,9 +18,11 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "paddle/fluid/imperative/layer.h"
#include "paddle/fluid/imperative/op_base.h"
#include "paddle/fluid/imperative/type_defs.h"
#include "paddle/fluid/platform/enforce.h"
#include "paddle/fluid/platform/macros.h"
@ -51,11 +53,8 @@ class GradOpBaseMakerBase {
attrs_(attrs) {}
virtual ~GradOpBaseMakerBase() = default;
virtual std::vector<std::shared_ptr<OpBase>> operator()() const = 0;
static std::shared_ptr<OpBase> CreateOp() {
return std::make_shared<OpBase>();
}
virtual std::shared_ptr<GradOpNode> operator()() const = 0;
TracedVarList<VarBase, TracedVarRole::kBackward> InputGrad(
const std::string& name, bool drop_empty_grad = true) const {
@ -138,6 +137,10 @@ class GradOpBaseMakerBase {
return var_base_map_out_.count(name) > 0;
}
static std::shared_ptr<GradOpNode> NewGradNode() {
return std::make_shared<GradOpNode>();
}
private:
template <TracedVarRole kRole>
TracedVarList<VarBase, kRole> GetVarBaseList(const std::string& name,
@ -149,7 +152,13 @@ class GradOpBaseMakerBase {
if (iterator != data_map.end()) {
vec_temp.reserve(iterator->second.size());
bool is_valid = false;
for (auto& var_base_temp : iterator->second) {
if (!var_base_temp) {
vec_temp.emplace_back();
continue;
}
if (kRole == TracedVarRole::kBackward) {
if (!var_base_temp->HasGradVar()) {
VLOG(6) << "GradVarBase of var " << var_base_temp->Name()
@ -168,6 +177,11 @@ class GradOpBaseMakerBase {
} else {
vec_temp.emplace_back(var_base_temp);
}
is_valid = true;
}
if (!is_valid) {
vec_temp.clear();
}
}
@ -185,44 +199,63 @@ class TracedGradOp {
DISABLE_COPY_AND_ASSIGN(TracedGradOp);
public:
explicit TracedGradOp(const std::shared_ptr<OpBase>& op) : op_(op) {}
explicit TracedGradOp(const std::shared_ptr<GradOpNode>& node)
: node_(node), op_(&(node->emplace_back())) {}
~TracedGradOp() {
op_->SetGradPendingOps(
{grad_pending_ops_.begin(), grad_pending_ops_.end()});
if (UNLIKELY(op_->GetOutsMap().empty())) {
node_->pop_back();
} else {
op_->CheckAttrs();
}
}
template <TracedVarRole kRole>
void SetInput(const std::string& name,
const TracedVarList<VarBase, kRole>& vars) {
if (vars.empty()) {
return;
}
if (kRole == TracedVarRole::kBackward) {
for (auto& var : vars) {
var->AddGradOp(op_);
if (var && !var->OverridedStopGradient()) {
var->SetGradNode(node_);
}
}
op_->SetInput(name, ToVarWrapperList(vars));
}
auto var_wrappers = ToVarWrapperList<kRole>(vars);
if (!var_wrappers.empty()) {
op_->SetInput(name, std::move(var_wrappers),
kRole == TracedVarRole::kBackward);
}
}
template <TracedVarRole kRole>
void SetOutput(const std::string& name,
const TracedVarList<VarBase, kRole>& vars) {
if (vars.empty()) {
return;
}
if (kRole == TracedVarRole::kBackward) {
if (vars.size() == 1 && vars.front()->OverridedStopGradient()) {
op_->SetOutput(name, VariableWrapperList{});
return;
} else {
for (auto& var : vars) {
if (!var->OverridedStopGradient()) {
for (auto& op : var->GradOps()) {
grad_pending_ops_.emplace(op);
}
if (var && !var->OverridedStopGradient() && var->GradNode()) {
node_->InsertGradPendingNode(var->GradNode());
}
}
}
}
op_->SetOutput(name, ToVarWrapperList(vars));
auto var_wrappers = ToVarWrapperList<kRole>(vars);
if (!var_wrappers.empty()) {
op_->SetOutput(name, std::move(var_wrappers),
kRole == TracedVarRole::kBackward);
}
}
void SetType(const std::string& type) { op_->SetType(type); }
@ -247,19 +280,31 @@ class TracedGradOp {
}
private:
template <TracedVarRole kRole>
static std::vector<std::shared_ptr<VariableWrapper>> ToVarWrapperList(
const std::vector<std::shared_ptr<VarBase>>& vars) {
std::vector<std::shared_ptr<VariableWrapper>> result;
result.reserve(vars.size());
bool has_valid = false;
for (auto& var : vars) {
if (UNLIKELY(!var || (kRole == TracedVarRole::kBackward &&
var->OverridedStopGradient()))) {
result.emplace_back();
} else {
result.emplace_back(var->SharedVar());
has_valid = true;
}
}
if (!has_valid) {
result.clear();
}
return result;
}
private:
const std::shared_ptr<OpBase>& op_;
std::unordered_set<std::shared_ptr<OpBase>> grad_pending_ops_;
const std::shared_ptr<GradOpNode>& node_;
OpBase* op_;
};
} // namespace imperative

File diff suppressed because it is too large Load Diff

@ -14,63 +14,18 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "paddle/fluid/imperative/backward_strategy.h"
#include "paddle/fluid/imperative/gradient_accumulator.h"
#include "paddle/fluid/imperative/layer.h"
#include "paddle/fluid/platform/macros.h"
namespace paddle {
namespace imperative {
// It seems there is no need for Engine to be an
// singleton, we can have multi-engine to run
// mutil-graoh. For future use we may expose a interface
// to Python to support
class Engine {
DISABLE_COPY_AND_ASSIGN(Engine);
public:
Engine() = default;
virtual ~Engine() = default;
virtual void Execute() = 0;
virtual void Init(VarBase* var, const detail::BackwardStrategy& strategy) = 0;
};
class BasicEngine : public Engine {
public:
void Init(VarBase* var, const detail::BackwardStrategy& strategy) override;
void Execute() override;
private:
void PrepareDeps();
void CheckBackwardInputs(OpBase* op);
void PrepareGradAccumulators(OpBase* op);
void SumGradient(OpBase* op, std::shared_ptr<VariableWrapper> src,
VariableWrapper* dst);
// TODO(jiabin): maybe we can optimize the performance of engine by cache the
// result
void Clear() {
init_ops_.clear();
op_deps_.clear();
accumulators_.clear();
}
std::vector<std::shared_ptr<OpBase>> init_ops_;
detail::BackwardStrategy backward_strategy_;
std::unordered_map<OpBase*, size_t> op_deps_;
std::unordered_map<VariableWrapper*, std::unique_ptr<GradientAccumulator>>
accumulators_;
std::vector<std::pair<VariableWrapper*, std::shared_ptr<VariableWrapper>>>
need_accu_var_list_;
};
} // namespace imperative

@ -0,0 +1,199 @@
// 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.
#pragma once
#include <string>
#include <vector>
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/type_defs.h"
#include "paddle/fluid/framework/variable.h"
#include "paddle/fluid/imperative/type_defs.h"
namespace paddle {
namespace imperative {
template <typename VarType>
class DygraphExecutionContext : public framework::ExecutionContext {
using Variable = framework::Variable;
public:
DygraphExecutionContext(const framework::OperatorBase& op,
const framework::Scope& scope,
const platform::DeviceContext& device_context,
const framework::RuntimeContext& ctx,
std::vector<framework::KernelConfig>* configs,
const NameVarMap<VarType>& var_base_map_in,
const NameVarMap<VarType>& var_base_map_out,
const framework::AttributeMap& attrs)
: ExecutionContext(op, scope, device_context, ctx, configs),
var_base_map_in_(var_base_map_in),
var_base_map_out_(var_base_map_out),
attrs_(attrs) {}
std::string InputName(const std::string& name) const override {
auto it = var_base_map_in_.find(name);
PADDLE_ENFORCE_NE(it, var_base_map_in_.end(),
platform::errors::PreconditionNotMet(
"Can not find [%s] in Input", name));
return it->second[0] ? it->second[0]->Name() : framework::kEmptyVarName;
}
std::vector<std::string> InputNames(const std::string& name) const override {
auto it = var_base_map_in_.find(name);
PADDLE_ENFORCE_NE(
it, var_base_map_in_.end(),
platform::errors::NotFound("Can not find [%s] in Input", name));
std::vector<std::string> vec_res;
vec_res.reserve(it->second.size());
for (size_t i = 0; i < it->second.size(); ++i) {
if (it->second[i]) {
vec_res.push_back(it->second[i]->Name());
} else {
vec_res.push_back(framework::kEmptyVarName);
}
}
return vec_res;
}
std::string OutputName(const std::string& name) const override {
auto it = var_base_map_out_.find(name);
PADDLE_ENFORCE_NE(
it, var_base_map_out_.end(),
platform::errors::NotFound("Can not find [%s] in Output", name));
return it->second[0] ? it->second[0]->Name() : framework::kEmptyVarName;
}
std::vector<std::string> OutputNames(const std::string& name) const override {
auto it = var_base_map_out_.find(name);
PADDLE_ENFORCE_NE(
it, var_base_map_out_.end(),
platform::errors::NotFound("Can not find [%s] in Output", name));
std::vector<std::string> vec_res;
vec_res.reserve(it->second.size());
for (size_t i = 0; i < it->second.size(); ++i) {
if (it->second[i]) {
vec_res.push_back(it->second[i]->Name());
} else {
vec_res.push_back(framework::kEmptyVarName);
}
}
return vec_res;
}
bool HasAttr(const std::string& name) const override {
return attrs_.count(name) != 0;
}
const framework::AttributeMap& Attrs() const override { return attrs_; }
const framework::Attribute& GetAttr(const std::string& name) const override {
auto it = attrs_.find(name);
PADDLE_ENFORCE_NE(
it, attrs_.end(),
platform::errors::NotFound("can not find [%s] in attrs", name));
return it->second;
}
std::vector<std::string> InNameList() const override {
std::vector<std::string> vec_temp;
vec_temp.reserve(var_base_map_in_.size());
for (auto& v : var_base_map_in_) {
vec_temp.push_back(v.first);
}
return vec_temp;
}
bool HasInput(const std::string& name) const override {
auto it = var_base_map_in_.find(name);
return (it != var_base_map_in_.end() && it->second.size() > 0);
}
bool HasOutput(const std::string& name) const override {
auto it = var_base_map_out_.find(name);
return (it != var_base_map_out_.end() && it->second.size() > 0);
}
size_t InputSize(const std::string& name) const override {
return InputNames(name).size();
}
size_t OutputSize(const std::string& name) const override {
return OutputNames(name).size();
}
const Variable* InputVar(const std::string& name) const override {
auto it = var_base_map_in_.find(name);
if (it == var_base_map_in_.end()) {
return nullptr;
}
return it->second.empty() || it->second[0] == nullptr
? nullptr
: it->second[0]->MutableVar();
}
Variable* OutputVar(const std::string& name) const override {
auto it = var_base_map_out_.find(name);
if (it == var_base_map_out_.end()) {
return nullptr;
}
return it->second.empty() || it->second[0] == nullptr
? nullptr
: it->second[0]->MutableVar();
}
const std::vector<Variable*> MultiInputVar(
const std::string& name) const override {
auto it = var_base_map_in_.find(name);
if (it == var_base_map_in_.end()) {
return {};
}
std::vector<Variable*> vec_res;
vec_res.reserve(it->second.size());
for (size_t i = 0; i < it->second.size(); ++i) {
vec_res.push_back(it->second[i] ? it->second[i]->MutableVar() : nullptr);
}
return vec_res;
}
std::vector<Variable*> MultiOutputVar(
const std::string& name) const override {
auto it = var_base_map_out_.find(name);
if (it == var_base_map_out_.end()) {
return {};
}
std::vector<Variable*> vec_res;
vec_res.reserve(it->second.size());
for (size_t i = 0; i < it->second.size(); ++i) {
vec_res.push_back(it->second[i] ? it->second[i]->MutableVar() : nullptr);
}
return vec_res;
}
private:
const NameVarMap<VarType>& var_base_map_in_;
const NameVarMap<VarType>& var_base_map_out_;
const framework::AttributeMap& attrs_;
};
} // namespace imperative
} // namespace paddle

File diff suppressed because it is too large Load Diff

@ -26,7 +26,8 @@ class GradientAccumulator {
public:
explicit GradientAccumulator(VariableWrapper* var) : var_(var) {}
virtual void Add(std::shared_ptr<VariableWrapper> var, size_t trace_id) = 0;
virtual void Add(std::shared_ptr<VariableWrapper> var, size_t trace_id,
bool unchange_input = false) = 0;
virtual ~GradientAccumulator() = default;
@ -43,7 +44,8 @@ class EagerGradientAccumulator : public GradientAccumulator {
public:
using GradientAccumulator::GradientAccumulator;
void Add(std::shared_ptr<VariableWrapper> var, size_t trace_id) override;
void Add(std::shared_ptr<VariableWrapper> var, size_t trace_id,
bool unchange_input) override;
private:
size_t cur_cnt_{0};
@ -53,11 +55,23 @@ class SortedGradientAccumulator : public GradientAccumulator {
public:
using GradientAccumulator::GradientAccumulator;
void Add(std::shared_ptr<VariableWrapper> var, size_t trace_id) override;
void Add(std::shared_ptr<VariableWrapper> var, size_t trace_id,
bool unchange_input) override;
private:
std::vector<std::pair<std::shared_ptr<VariableWrapper>, size_t>>
tmp_grad_vars_;
struct SavedVarInfo {
SavedVarInfo(std::shared_ptr<VariableWrapper>&& v, size_t id,
bool enable_unchange_input)
: var(std::move(v)),
trace_id(id),
unchange_input(enable_unchange_input) {}
std::shared_ptr<VariableWrapper> var;
size_t trace_id;
bool unchange_input;
};
std::vector<SavedVarInfo> tmp_grad_vars_;
};
} // namespace imperative

File diff suppressed because it is too large Load Diff

@ -0,0 +1,188 @@
// 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.
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include "paddle/fluid/framework/type_defs.h"
#include "paddle/fluid/framework/var_type_inference.h"
#include "paddle/fluid/imperative/type_defs.h"
#include "paddle/fluid/imperative/variable_wrapper.h"
namespace paddle {
namespace imperative {
// infer var type context for imperative mode
template <typename VarType>
class RuntimeInferVarTypeContext : public framework::InferVarTypeContext {
public:
RuntimeInferVarTypeContext(const NameVarMap<VarType>& inputs,
const NameVarMap<VarType>& outputs,
const framework::AttributeMap& attrs_map)
: InferVarTypeContext(nullptr, nullptr),
inputs_(inputs),
outputs_(outputs),
attrs_(attrs_map),
input_names_(),
output_names_(),
var_set_() {
input_names_.reserve(inputs_.size());
for (auto& it : inputs_) {
for (auto& var : it.second) {
if (var) {
input_names_[it.first].emplace_back(var->Name());
var_set_[var->Name()] = var.get();
}
}
}
output_names_.reserve(outputs_.size());
for (auto& it : outputs_) {
for (auto& var : it.second) {
if (var) {
output_names_[it.first].emplace_back(var->Name());
var_set_[var->Name()] = var.get();
}
}
}
}
virtual ~RuntimeInferVarTypeContext() {}
framework::Attribute GetAttr(const std::string& name) const override {
auto iter = attrs_.find(name);
PADDLE_ENFORCE_EQ(
iter != attrs_.end(), true,
platform::errors::NotFound("Cannot find attribute %s", name));
return iter->second;
}
bool HasVar(const std::string& name) const override {
return var_set_.count(name) > 0;
}
bool HasInput(const std::string& name) const override {
auto it = inputs_.find(name);
return (it != inputs_.end() && it->second.size() > 0);
}
bool HasOutput(const std::string& name) const override {
auto it = outputs_.find(name);
return (it != outputs_.end() && it->second.size() > 0);
}
const std::vector<std::string>& Input(
const std::string& name) const override {
auto iter = input_names_.find(name);
PADDLE_ENFORCE_EQ(
iter != input_names_.end(), true,
platform::errors::NotFound("Cannot find input var %s", name));
return iter->second;
}
const std::vector<std::string>& Output(
const std::string& name) const override {
auto iter = output_names_.find(name);
PADDLE_ENFORCE_EQ(
iter != output_names_.end(), true,
platform::errors::NotFound("Cannot find output var %s", name));
return iter->second;
}
framework::proto::VarType::Type GetType(
const std::string& name) const override {
auto iter = var_set_.find(name);
PADDLE_ENFORCE_EQ(
iter != var_set_.end(), true,
platform::errors::NotFound("Cannot find var %s in GetType", name));
return iter->second->Type();
}
void SetType(const std::string& name,
framework::proto::VarType::Type type) override {
if (name == "kLookupTablePath") {
VLOG(2) << "SUPER UGLY FIX, remove this when move imperative mode in C++";
} else {
var_set_[name]->SetType(type);
if ((var_set_[name]->MutableVar()->IsInitialized() == true) &&
(var_set_[name]->MutableVar()->Type() != type)) {
var_set_[name]->MutableVar()->Clear();
}
}
}
framework::proto::VarType::Type GetDataType(
const std::string& name) const override {
auto iter = var_set_.find(name);
PADDLE_ENFORCE_EQ(
iter != var_set_.end(), true,
platform::errors::NotFound("Cannot find var %s in GetDataType", name));
return iter->second->DataType();
}
void SetDataType(const std::string& name,
framework::proto::VarType::Type type) override {
var_set_[name]->SetDataType(type);
}
std::vector<framework::proto::VarType::Type> GetDataTypes(
const std::string& name) const override {
PADDLE_THROW(platform::errors::PermissionDenied(
"GetDataTypes is not supported in runtime InferVarType"));
}
void SetDataTypes(const std::string& name,
const std::vector<framework::proto::VarType::Type>&
multiple_data_type) override {
PADDLE_THROW(platform::errors::PermissionDenied(
"SetDataTypes is not supported in runtime InferVarType"));
}
std::vector<int64_t> GetShape(const std::string& name) const override {
PADDLE_THROW(platform::errors::PermissionDenied(
"Do not handle Shape in runtime InferVarType"));
}
void SetShape(const std::string& name,
const std::vector<int64_t>& dims) override {
PADDLE_THROW(platform::errors::PermissionDenied(
"Do not handle Shape in runtime InferVarType"));
}
int32_t GetLoDLevel(const std::string& name) const override {
PADDLE_THROW(platform::errors::PermissionDenied(
"Do not handle LoDLevel in runtime InferVarType"));
}
void SetLoDLevel(const std::string& name, int32_t lod_level) override {
PADDLE_THROW(platform::errors::PermissionDenied(
"Do not handle LoDLevel in runtime InferVarType"));
}
private:
const NameVarMap<VarType>& inputs_;
const NameVarMap<VarType>& outputs_;
const framework::AttributeMap& attrs_;
std::unordered_map<std::string, std::vector<std::string>> input_names_;
std::unordered_map<std::string, std::vector<std::string>> output_names_;
std::unordered_map<std::string, VarType*> var_set_;
};
} // namespace imperative
} // namespace paddle

@ -19,6 +19,10 @@
#include "paddle/fluid/framework/framework.pb.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/variable_helper.h"
#include "paddle/fluid/imperative/execution_context.h"
#include "paddle/fluid/imperative/infer_shape_context.h"
#include "paddle/fluid/imperative/infer_var_type_context.h"
#include "paddle/fluid/imperative/op_base.h"
#include "paddle/fluid/imperative/prepared_operator.h"
#include "paddle/fluid/operators/math/math_function.h"
#include "paddle/fluid/platform/device_context.h"
@ -180,7 +184,7 @@ static std::string LayerDebugStringImpl(const std::string& op_type,
size_t i = 0;
for (auto& pair : ins) {
if (i > 0) ss << ", ";
ss << DebugString(pair.first, pair.second);
ss << DebugString<VarType>(pair.first, pair.second);
++i;
}
@ -188,7 +192,7 @@ static std::string LayerDebugStringImpl(const std::string& op_type,
i = 0;
for (auto& pair : outs) {
if (i > 0) ss << ", ";
ss << DebugString(pair.first, pair.second);
ss << DebugString<VarType>(pair.first, pair.second);
++i;
}
return ss.str();
@ -206,6 +210,27 @@ std::string LayerDebugString(const std::string& op_type,
return LayerDebugStringImpl<VariableWrapper>(op_type, ins, outs);
}
VarBase::VarBase(bool has_grad, const std::shared_ptr<VariableWrapper>& var)
: var_(var), grad_node_(var->GetGradNode()) {
if (has_grad) {
if (auto grad_var = var_->GetGradVar()) {
grad_var_ = std::make_shared<VarBase>(false, grad_var);
} else {
grad_var_ = std::make_shared<VarBase>(false, GradVarName());
var_->SetGradVar(grad_var_->var_);
}
}
if (IsDebugEnabled()) {
VLOG(10) << "Construct VarBase: " << Name();
name_set_.Insert(Name());
}
}
size_t VarBase::GradOpNum() const {
return grad_node_ ? grad_node_->size() : 0;
}
void VarBase::ClearGradient() {
if (grad_var_) {
if (grad_var_->Var().IsType<framework::SelectedRows>()) {
@ -292,8 +317,6 @@ void OpBase::SetType(const std::string& type) {
}
void OpBase::ClearBackwardTrace() {
grad_pending_ops_.clear();
allow_empty_vars_.clear();
ins_.clear();
outs_.clear();
}
@ -308,16 +331,18 @@ static void OpBaseRunImpl(const framework::OperatorBase& op,
PADDLE_ENFORCE_NOT_NULL(op_kernel, "only support op with kernel");
auto& info = op.Info();
if (info.infer_var_type_) {
RuntimeInferVarTypeContext<VarType> infer_var_type_ctx(ins, &outs, attrs);
RuntimeInferVarTypeContext<VarType> infer_var_type_ctx(ins, outs, attrs);
info.infer_var_type_(&infer_var_type_ctx);
}
// Initialize output var type
for (auto& var_pair : outs) {
for (auto& var : var_pair.second) {
if (var) {
InitializeVariable(var->MutableVar(), var->Type());
}
}
}
// VLOG(3) << "Running Op " << op.Type();
VLOG(5) << LayerDebugString(op.Type(), ins, outs);
@ -344,5 +369,64 @@ void OpBase::Run(const framework::OperatorBase& op,
OpBaseRunImpl<VariableWrapper>(op, ins, outs, attrs, place);
}
static void ClearNoNeedBufferInputs(OpBase* op) {
auto& inferer = op->Info().NoNeedBufferVarsInferer();
if (!inferer) return;
auto* ins = op->GetMutableInsMap();
const auto& no_need_buffer_slots =
inferer(*ins, op->GetOutsMap(), op->Attrs());
if (no_need_buffer_slots.empty()) return;
for (auto& slot : no_need_buffer_slots) {
auto iter = ins->find(slot);
if (iter == ins->end()) continue;
VLOG(2) << "Clear data buffer of " << slot << " in " << op->Type();
PADDLE_ENFORCE_EQ(
iter->second.IsGrad(), false,
platform::errors::InvalidArgument(
"Only forward variable buffers can be clear, this may be a bug"));
for (auto& each_var : *(iter->second.MutableVarList())) {
if (!each_var) continue;
auto& var = each_var->Var();
PADDLE_ENFORCE_EQ(var.IsType<framework::LoDTensor>(), true,
platform::errors::PermissionDenied(
"NoNeedBufferVars only support LoDTensor"));
// TODO(zjl): support higher order derivatives
auto new_var = new VariableWrapper(each_var->Name());
auto* new_tensor =
new_var->MutableVar()->GetMutable<framework::LoDTensor>();
auto& old_tensor = var.Get<framework::LoDTensor>();
new_tensor->Resize(old_tensor.dims());
new_tensor->set_lod(old_tensor.lod());
each_var.reset(new_var);
}
}
}
std::shared_ptr<GradOpNode> CreateGradOpNode(
const framework::OperatorBase& op, const NameVarBaseMap& ins,
const NameVarBaseMap& outs, const framework::AttributeMap& attrs,
const platform::Place& place) {
const auto& info = op.Info();
if (!info.dygraph_grad_op_maker_) {
return nullptr;
}
auto grad_node = info.dygraph_grad_op_maker_(op.Type(), ins, outs, attrs);
if (grad_node && !grad_node->empty()) {
for (auto& op : *grad_node) {
op.SetId(OpBase::GenerateUniqueId());
op.SetPlace(place);
ClearNoNeedBufferInputs(&op);
}
return grad_node;
} else {
return nullptr;
}
}
} // namespace imperative
} // namespace paddle

File diff suppressed because it is too large Load Diff

@ -0,0 +1,211 @@
// 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.
#pragma once
#include <atomic>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "paddle/fluid/framework/type_defs.h"
#include "paddle/fluid/imperative/type_defs.h"
#include "paddle/fluid/imperative/variable_wrapper.h"
#include "paddle/fluid/platform/place.h"
namespace paddle {
namespace imperative {
// TODO(zjl): to support py_func layer
class OpBase {
public:
OpBase() = default;
OpBase(const OpBase&) = delete;
OpBase(OpBase&&) = default;
OpBase& operator=(const OpBase&) = delete;
OpBase& operator=(OpBase&&) = default;
~OpBase() { VLOG(3) << "Destruct Op: " << Type(); }
const std::string& Type() const { return op_->Type(); }
const framework::AttributeMap& Attrs() const { return attrs_; }
const framework::OpInfo& Info() const { return op_->Info(); }
const framework::OperatorBase& InnerOp() const { return *op_; }
void ClearBackwardTrace();
NameVarMap<VariableWrapper>* GetMutableOutsMap() { return &outs_; }
NameVarMap<VariableWrapper>* GetMutableInsMap() { return &ins_; }
const NameVarMap<VariableWrapper>& GetInsMap() const { return ins_; }
const NameVarMap<VariableWrapper>& GetOutsMap() const { return outs_; }
void SetType(const std::string& type);
void CheckAttrs() {
auto& info = op_->Info();
if (info.Checker() != nullptr) {
info.Checker()->Check(&attrs_, true);
}
}
void SetInput(const std::string& name, VariableWrapperList vars,
bool is_grad) {
auto& in_vars = ins_[name];
*(in_vars.MutableVarList()) = std::move(vars);
in_vars.SetIsGrad(is_grad);
}
void SetOutput(const std::string& name, VariableWrapperList vars,
bool is_grad) {
auto& out_vars = outs_[name];
*(out_vars.MutableVarList()) = std::move(vars);
out_vars.SetIsGrad(is_grad);
}
void SetAttrMap(const framework::AttributeMap& attrs) { attrs_ = attrs; }
void SetAttr(const std::string& name, const framework::Attribute& v) {
attrs_[name] = v;
}
void SetBlockAttr(const std::string& name, framework::BlockDesc* block) {
PADDLE_THROW(platform::errors::PermissionDenied(
"SetBlockAttr is not support in dygraph OpBase"));
}
const framework::AttributeMap& Attrs() { return attrs_; }
bool HasAttr(const std::string& name) const { return attrs_.count(name) > 0; }
const framework::Attribute& GetAttr(const std::string& name) const {
auto it = attrs_.find(name);
PADDLE_ENFORCE_NE(
it, attrs_.end(),
platform::errors::NotFound("can not find attribute [%s]", name));
return it->second;
}
template <typename T>
inline const T& Attr(const std::string& name) const {
return boost::get<T>(GetAttr(name));
}
size_t id() const { return id_; }
void SetId(size_t id) { id_ = id; }
const platform::Place& place() const { return place_; }
void SetPlace(const platform::Place& place) { place_ = place; }
static size_t GenerateUniqueId() {
static std::atomic<size_t> unique_id{0};
return unique_id.fetch_add(1);
}
static void Run(const framework::OperatorBase& op,
const NameVarMap<VarBase>& ins,
const NameVarMap<VarBase>& outs,
const framework::AttributeMap& attrs,
const platform::Place& place);
static void Run(const framework::OperatorBase& op,
const NameVarMap<VariableWrapper>& ins,
const NameVarMap<VariableWrapper>& outs,
const framework::AttributeMap& attrs,
const platform::Place& place);
private:
NameVarMap<VariableWrapper> ins_;
NameVarMap<VariableWrapper> outs_;
framework::AttributeMap attrs_;
std::unique_ptr<framework::OperatorBase> op_;
platform::Place place_;
size_t id_{-1UL};
std::vector<std::function<void()>> backward_hooks_;
};
class GradOpNode {
public:
GradOpNode() = default;
void reserve(size_t size) { ops_.reserve(size); }
size_t size() const { return ops_.size(); }
bool empty() const { return ops_.empty(); }
void clear() { ops_.clear(); }
void pop_back() { ops_.pop_back(); }
template <typename... ARGS>
OpBase& emplace_back(ARGS&&... args) { // NOLINT
ops_.emplace_back(std::forward<ARGS>(args)...);
return ops_.back();
}
const OpBase& back() const { return ops_.back(); }
OpBase& back() { return ops_.back(); }
OpBase& operator[](size_t idx) { return ops_[idx]; }
const OpBase& operator[](size_t idx) const { return ops_[idx]; }
/* Iterator related */
using Iterator = std::vector<OpBase>::iterator;
using ConstIterator = std::vector<OpBase>::const_iterator;
Iterator begin() { return ops_.begin(); }
Iterator end() { return ops_.end(); }
ConstIterator begin() const { return ops_.begin(); }
ConstIterator end() const { return ops_.end(); }
void InsertGradPendingNode(const std::shared_ptr<GradOpNode>& node) {
if (node &&
std::find(grad_pending_nodes_.begin(), grad_pending_nodes_.end(),
node) == grad_pending_nodes_.end()) {
grad_pending_nodes_.emplace_back(node);
}
}
const std::vector<std::shared_ptr<GradOpNode>>& GradPendingNodes() const {
return grad_pending_nodes_;
}
private:
DISABLE_COPY_AND_ASSIGN(GradOpNode);
private:
std::vector<OpBase> ops_;
std::vector<std::shared_ptr<GradOpNode>> grad_pending_nodes_;
};
} // namespace imperative
} // namespace paddle

File diff suppressed because it is too large Load Diff

@ -0,0 +1,58 @@
// 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.
#pragma once
#include <memory>
#include <vector>
#include "paddle/fluid/imperative/backward_strategy.h"
#include "paddle/fluid/imperative/engine.h"
#include "paddle/fluid/platform/place.h"
namespace paddle {
namespace imperative {
class VarBase;
class PartialGradEngine : public Engine {
public:
PartialGradEngine(const std::vector<std::shared_ptr<VarBase>> &input_targets,
const std::vector<std::shared_ptr<VarBase>> &output_targets,
const std::vector<std::shared_ptr<VarBase>> &output_grads,
const std::vector<std::shared_ptr<VarBase>> &no_grad_vars,
const platform::Place &place,
const detail::BackwardStrategy &strategy,
bool create_graph);
void Execute() override;
std::vector<std::shared_ptr<VarBase>> GetResult() const;
private:
void Clear();
private:
std::vector<std::shared_ptr<VarBase>> input_targets_;
std::vector<std::shared_ptr<VarBase>> output_targets_;
std::vector<std::shared_ptr<VarBase>> output_grads_;
std::vector<std::shared_ptr<VarBase>> no_grad_vars_;
platform::Place place_;
detail::BackwardStrategy strategy_;
bool create_graph_;
std::vector<std::shared_ptr<VarBase>> results_;
};
} // namespace imperative
} // namespace paddle

@ -14,6 +14,9 @@
#include "paddle/fluid/imperative/prepared_operator.h"
#include <sstream>
#include "paddle/fluid/imperative/execution_context.h"
#include "paddle/fluid/imperative/infer_shape_context.h"
#include "paddle/fluid/imperative/infer_var_type_context.h"
namespace paddle {
namespace imperative {

@ -0,0 +1,87 @@
// 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.
#pragma once
#include <memory>
#include <utility>
#include <vector>
namespace paddle {
namespace imperative {
class VariableWrapper;
class SavedVariableWrapperList {
public:
SavedVariableWrapperList() : vars_(), is_grad_(false) {}
template <typename... Args>
explicit SavedVariableWrapperList(bool is_grad, Args&&... args)
: vars_(std::forward<Args>(args)...), is_grad_(is_grad) {}
bool IsGrad() const { return is_grad_; }
void SetIsGrad(bool is_grad) { is_grad_ = is_grad; }
const std::vector<std::shared_ptr<VariableWrapper>>& VarList() const {
return vars_;
}
std::vector<std::shared_ptr<VariableWrapper>>* MutableVarList() {
return &vars_;
}
/* Borrow method from std::vector */
size_t size() const { return vars_.size(); }
bool empty() const { return vars_.empty(); }
template <typename... ARGS>
void emplace_back(ARGS&&... args) {
vars_.emplace_back(std::forward<ARGS>(args)...);
}
using Iterator = std::vector<std::shared_ptr<VariableWrapper>>::iterator;
using ConstIterator =
std::vector<std::shared_ptr<VariableWrapper>>::const_iterator;
Iterator begin() { return vars_.begin(); }
Iterator end() { return vars_.end(); }
ConstIterator begin() const { return vars_.begin(); }
ConstIterator end() const { return vars_.end(); }
std::shared_ptr<VariableWrapper>& operator[](size_t idx) {
return vars_[idx];
}
const std::shared_ptr<VariableWrapper>& operator[](size_t idx) const {
return vars_[idx];
}
operator const std::vector<std::shared_ptr<VariableWrapper>>&() const {
return vars_;
}
private:
std::vector<std::shared_ptr<VariableWrapper>> vars_;
bool is_grad_;
};
} // namespace imperative
} // namespace paddle

@ -117,5 +117,202 @@ TEST(test_add_functor, add_functor) {
#endif
}
static void CopyVar(const framework::Variable& var,
framework::Variable* dst_ptr) {
auto& dst = *dst_ptr;
dst.Clear();
if (var.IsType<framework::LoDTensor>()) {
const auto& src_tensor = var.Get<framework::LoDTensor>();
auto* dst_tensor = dst.GetMutable<framework::LoDTensor>();
framework::TensorCopySync(src_tensor, src_tensor.place(), dst_tensor);
} else {
const auto& src_selected_rows = var.Get<framework::SelectedRows>();
auto* dst_selected_rows = dst.GetMutable<framework::SelectedRows>();
dst_selected_rows->set_rows(src_selected_rows.rows());
dst_selected_rows->set_height(src_selected_rows.height());
framework::TensorCopySync(src_selected_rows.value(),
src_selected_rows.value().place(),
dst_selected_rows->mutable_value());
}
}
static bool IsEqualVar(const framework::Variable& var1,
const framework::Variable& var2) {
if (var1.Type() != var2.Type()) {
return false;
}
framework::Tensor t1, t2;
if (var1.IsType<framework::LoDTensor>()) {
framework::TensorCopySync(var1.Get<framework::LoDTensor>(),
platform::CPUPlace(), &t1);
framework::TensorCopySync(var2.Get<framework::LoDTensor>(),
platform::CPUPlace(), &t2);
} else {
auto& s1 = var1.Get<framework::SelectedRows>();
auto& s2 = var2.Get<framework::SelectedRows>();
if (s1.height() != s2.height()) {
return false;
}
if (s1.rows().size() != s2.rows().size()) {
return false;
}
auto row1_data = s1.rows().data();
auto row2_data = s2.rows().data();
if (std::memcmp(row1_data, row2_data,
s1.rows().size() * sizeof(*row1_data)) != 0) {
return false;
}
framework::TensorCopySync(var1.Get<framework::SelectedRows>().value(),
platform::CPUPlace(), &t1);
framework::TensorCopySync(var2.Get<framework::SelectedRows>().value(),
platform::CPUPlace(), &t2);
}
if (t1.type() != t2.type() || t1.dims() != t2.dims()) {
return false;
}
auto* t1_p = t1.data<void>();
auto* t2_p = t2.data<void>();
return std::memcmp(t1_p, t2_p,
t1.numel() * framework::SizeOfType(t1.type())) == 0;
}
template <typename T>
static framework::Variable RandomTensor(const framework::DDim& dims,
const platform::Place& place,
int low = -10, int high = 10) {
framework::Tensor cpu_tensor;
cpu_tensor.Resize(dims);
auto* ptr = cpu_tensor.mutable_data<T>(platform::CPUPlace());
std::uniform_int_distribution<int> dist(low, high);
std::random_device rd;
std::mt19937 engine(rd());
for (int64_t i = 0; i < cpu_tensor.numel(); ++i) {
ptr[i] = dist(engine);
}
framework::Variable ret;
framework::TensorCopySync(cpu_tensor, place,
ret.GetMutable<framework::LoDTensor>());
return ret;
}
template <typename T>
static framework::Variable RandomSelectedRows(framework::DDim dims,
const platform::Place& place,
int64_t row_number, int low = -10,
int high = 10) {
auto height = dims[0];
dims[0] = row_number;
framework::Variable ret;
auto* sr = ret.GetMutable<framework::SelectedRows>();
auto tensor_var = RandomTensor<T>(dims, place, low, high);
sr->mutable_value()->ShareDataWith(
tensor_var.template Get<framework::LoDTensor>());
sr->set_height(height);
sr->mutable_rows()->resize(row_number);
auto* row_data = sr->mutable_rows()->data();
std::uniform_int_distribution<int64_t> dist(0, height - 1);
std::random_device rd;
std::mt19937 engine(rd());
for (int64_t i = 0; i < dims[0]; ++i) {
row_data[i] = dist(engine);
}
return ret;
}
static std::unique_ptr<GradientAccumulator> CreateAccumulator(
const std::shared_ptr<VariableWrapper>& var, bool sort_gradient) {
if (sort_gradient) {
return std::unique_ptr<GradientAccumulator>(
new SortedGradientAccumulator(var.get()));
} else {
return std::unique_ptr<GradientAccumulator>(
new EagerGradientAccumulator(var.get()));
}
}
static void TestGradientAccumulatorTestUnchangeInput(
const platform::Place& place, bool sort_gradient) {
framework::DDim dim{10, 20};
int64_t maximum_row_number = 100;
std::uniform_int_distribution<int64_t> dist(1, maximum_row_number);
int seed;
{
std::random_device rd;
seed = rd();
}
std::mt19937 engine(seed);
auto create_var = [&](bool use_tensor) {
if (use_tensor) {
return RandomTensor<float>(dim, place);
} else {
return RandomSelectedRows<float>(dim, place, dist(engine));
}
};
std::vector<bool> use_tensors = {false, true};
for (auto use_tensor1 : use_tensors) {
for (auto use_tensor2 : use_tensors) {
auto g_var1 = std::make_shared<VariableWrapper>("g_var1");
g_var1->SetOverridedStopGradient(false);
auto g_accum1 = CreateAccumulator(g_var1, sort_gradient);
g_accum1->IncreaseRefCnt();
g_accum1->IncreaseRefCnt();
auto g_var2 = std::make_shared<VariableWrapper>("g_var2");
g_var2->SetOverridedStopGradient(false);
auto g_accum2 = CreateAccumulator(g_var2, sort_gradient);
g_accum2->IncreaseRefCnt();
g_accum2->IncreaseRefCnt();
auto var1 = create_var(use_tensor1);
auto var_wrapper1_1 = std::make_shared<VariableWrapper>("tmp1_1");
auto var_wrapper2_1 = std::make_shared<VariableWrapper>("tmp2_1");
CopyVar(var1, var_wrapper1_1->MutableVar());
CopyVar(var1, var_wrapper2_1->MutableVar());
auto var2 = create_var(use_tensor2);
auto var_wrapper1_2 = std::make_shared<VariableWrapper>("tmp1_2");
auto var_wrapper2_2 = std::make_shared<VariableWrapper>("tmp2_2");
CopyVar(var2, var_wrapper1_2->MutableVar());
CopyVar(var2, var_wrapper2_2->MutableVar());
g_accum1->Add(var_wrapper1_1, 0, false);
g_accum1->Add(var_wrapper1_2, 1, false);
g_accum2->Add(var_wrapper2_1, 0, true);
g_accum2->Add(var_wrapper2_2, 1, true);
ASSERT_TRUE(IsEqualVar(var_wrapper2_1->Var(), var1));
ASSERT_TRUE(IsEqualVar(var_wrapper2_2->Var(), var2));
ASSERT_TRUE(IsEqualVar(g_var1->Var(), g_var2->Var()));
}
}
}
TEST(test_gradient_accumulator, test_unchange_input) {
for (auto sort_gradient : {false, true}) {
TestGradientAccumulatorTestUnchangeInput(platform::CPUPlace(),
sort_gradient);
#ifdef PADDLE_WITH_CUDA
TestGradientAccumulatorTestUnchangeInput(platform::CUDAPlace(0),
sort_gradient);
#endif
}
}
} // namespace imperative
} // namespace paddle

@ -21,6 +21,9 @@
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/imperative/execution_context.h"
#include "paddle/fluid/imperative/infer_shape_context.h"
#include "paddle/fluid/imperative/infer_var_type_context.h"
#include "paddle/fluid/imperative/layer.h"
namespace imperative = paddle::imperative;
@ -45,7 +48,7 @@ TEST(test_layer, test_runtime_context) {
imperative::NameVarBaseMap outs = {out_pair};
framework::AttributeMap attrs;
auto *ctx = new imperative::RuntimeInferVarTypeContext<imperative::VarBase>(
ins, &outs, attrs);
ins, outs, attrs);
ASSERT_TRUE(ctx->HasVar("vin"));
ASSERT_TRUE(ctx->HasInput("X"));
ASSERT_TRUE(ctx->HasOutput("Out"));
@ -120,11 +123,12 @@ TEST(test_layer, test_debug_string) {
ASSERT_TRUE(res_sr.find("SelectedRows") != std::string::npos);
}
static std::shared_ptr<imperative::OpBase> CreateOpBase(
static std::shared_ptr<imperative::GradOpNode> CreateGradNode(
size_t id, const std::string &type, const imperative::NameVarBaseMap &ins,
const imperative::NameVarBaseMap &outs,
const framework::AttributeMap &attrs, const platform::Place &place) {
auto op = std::make_shared<imperative::OpBase>();
auto node = std::make_shared<imperative::GradOpNode>();
auto *op = &(node->emplace_back());
op->SetId(id);
op->SetPlace(place);
op->SetType(type);
@ -134,7 +138,7 @@ static std::shared_ptr<imperative::OpBase> CreateOpBase(
for (auto &var : pair.second) {
vars.emplace_back(var->SharedVar());
}
op->SetInput(pair.first, vars);
op->SetInput(pair.first, vars, false);
}
for (auto &pair : outs) {
@ -142,10 +146,10 @@ static std::shared_ptr<imperative::OpBase> CreateOpBase(
for (auto &var : pair.second) {
vars.emplace_back(var->SharedVar());
}
op->SetOutput(pair.first, vars);
op->SetOutput(pair.first, vars, false);
}
return op;
return node;
}
TEST(test_layer, test_clear_backward_info) {
@ -163,19 +167,21 @@ TEST(test_layer, test_clear_backward_info) {
framework::AttributeMap concat_att_map;
concat_att_map["axis"] = 1;
auto op = CreateOpBase(0, "mul", ins, outs, concat_att_map, place);
auto preceding_op = CreateOpBase(0, "mul", ins, outs, concat_att_map, place);
op->SetGradPendingOps({preceding_op});
auto node = CreateGradNode(0, "mul", ins, outs, concat_att_map, place);
auto pending_node =
CreateGradNode(0, "mul", ins, outs, concat_att_map, place);
node->InsertGradPendingNode(pending_node);
ASSERT_EQ(node->size(), 1UL);
auto *op = &(node->back());
ASSERT_GT(op->GetInsMap().size(), 0UL);
ASSERT_GT(op->GetOutsMap().size(), 0UL);
ASSERT_GT(op->GradPendingOps().size(), 0UL);
op->ClearBackwardTrace();
ASSERT_EQ(op->GetInsMap().size(), 0UL);
ASSERT_EQ(op->GetOutsMap().size(), 0UL);
ASSERT_EQ(op->GradPendingOps().size(), 0UL);
}
TEST(test_layer, test_varbase_basic) {

@ -22,6 +22,7 @@
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/imperative/basic_engine.h"
#include "paddle/fluid/imperative/tracer.h"
#include "paddle/fluid/memory/memcpy.h"
@ -148,9 +149,9 @@ TEST(test_tracer, test_track_backward_output) {
framework::AttributeMap mul_attr_map;
mul_attr_map["use_mkldnn"] = false;
tracer.TraceOp("mul", ins, outs, mul_attr_map, place, true);
ASSERT_EQ(x_in->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(y_in->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(vout->GradVarBase()->GradOps().size(), 1UL);
ASSERT_EQ(x_in->GradVarBase()->GradOpNum(), 0UL);
ASSERT_EQ(y_in->GradVarBase()->GradOpNum(), 0UL);
ASSERT_EQ(vout->GradVarBase()->GradOpNum(), 1UL);
}
TEST(test_tracer, test_track_backward_input) {
@ -188,9 +189,9 @@ TEST(test_tracer, test_track_backward_input) {
mul_attr_map["use_mkldnn"] = false;
tracer.TraceOp("mul", ins, outs, mul_attr_map, place, true);
ASSERT_EQ(x_in->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(y_in->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(vout->GradVarBase()->GradOps().size(), 1UL);
ASSERT_EQ(x_in->GradVarBase()->GradOpNum(), 0UL);
ASSERT_EQ(y_in->GradVarBase()->GradOpNum(), 0UL);
ASSERT_EQ(vout->GradVarBase()->GradOpNum(), 1UL);
}
#if defined(PADDLE_WITH_CUDA)
TEST(test_tracer, test_trace_op_with_multi_device_inputs) {
@ -240,9 +241,9 @@ TEST(test_tracer, test_trace_op_with_multi_device_inputs) {
tracer.TraceOp("reduce_sum", reduce_in, reduce_out, reduce_attr_map,
gpu_place, true);
detail::BackwardStrategy back_st;
imperative::Engine* engine = tracer.GetDefaultEngine();
engine->Init(reduce_sum_out.get(), back_st);
engine->Execute();
imperative::BasicEngine engine;
engine.Init(reduce_sum_out.get(), back_st);
engine.Execute();
framework::LoDTensor rlt;
framework::TensorCopySync(vout->Var().Get<framework::LoDTensor>(), place,
@ -346,14 +347,14 @@ TEST(test_tracer, test_var_without_grad_var) {
ASSERT_EQ(out_tensor.data<float>()[i], 20.0);
}
ASSERT_EQ(x_in->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(y_in->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(vout->GradVarBase()->GradOps().size(), 1UL);
ASSERT_EQ(x_in->GradVarBase()->GradOpNum(), 0UL);
ASSERT_EQ(y_in->GradVarBase()->GradOpNum(), 0UL);
ASSERT_EQ(vout->GradVarBase()->GradOpNum(), 1UL);
detail::BackwardStrategy back_st;
imperative::Engine* engine = tracer.GetDefaultEngine();
engine->Init(vout.get(), back_st);
engine->Execute();
imperative::BasicEngine engine;
engine.Init(vout.get(), back_st);
engine.Execute();
// check the grad
framework::LoDTensor x_grad;
@ -382,7 +383,7 @@ static void TestVarOpDestructionMain(const platform::Place& place,
size_t loop_num = 10) {
WeakPtrSet<VariableWrapper> var_wrappers;
WeakPtrSet<VarBase> var_bases;
WeakPtrSet<OpBase> op_bases;
WeakPtrSet<GradOpNode> op_bases;
Tracer tracer;
@ -413,30 +414,31 @@ static void TestVarOpDestructionMain(const platform::Place& place,
NameVarBaseMap{{"Out", {z}}}, framework::AttributeMap{},
place, true);
ASSERT_EQ(z->GradOps().size(), 0UL);
ASSERT_EQ(z->GradVarBase()->GradOps().size(), 1UL);
auto new_op = z->GradVarBase()->GradOps()[0];
ASSERT_EQ(z->GradOpNum(), 0UL);
ASSERT_EQ(z->GradVarBase()->GradOpNum(), 1UL);
auto new_op = z->GradVarBase()->GradNode();
ASSERT_EQ(x->GradOps().size(), 0UL);
ASSERT_EQ(y->GradOps().size(), 0UL);
ASSERT_EQ(x->GradOpNum(), 0UL);
ASSERT_EQ(y->GradOpNum(), 0UL);
std::unordered_set<std::shared_ptr<OpBase>> expected_pending_ops;
std::unordered_set<std::shared_ptr<GradOpNode>> expected_pending_ops;
if (i == 0) {
ASSERT_EQ(x->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(y->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(x->GradVarBase()->GradOpNum(), 0UL);
ASSERT_EQ(y->GradVarBase()->GradOpNum(), 0UL);
} else {
ASSERT_EQ(x->GradVarBase()->GradOps().size(), 1UL);
ASSERT_EQ(y->GradVarBase()->GradOps().size(), 0UL);
ASSERT_EQ(x->GradVarBase()->GradOpNum(), 1UL);
ASSERT_EQ(y->GradVarBase()->GradOpNum(), 0UL);
for (auto& op : x->GradVarBase()->GradOps()) {
expected_pending_ops.emplace(op);
if (x->GradVarBase()->GradNode()) {
expected_pending_ops.emplace(x->GradVarBase()->GradNode());
}
for (auto& op : y->GradVarBase()->GradOps()) {
expected_pending_ops.emplace(op);
if (y->GradVarBase()->GradNode()) {
expected_pending_ops.emplace(y->GradVarBase()->GradNode());
}
std::unordered_set<std::shared_ptr<OpBase>> actual_pending_ops;
for (auto& op : new_op->GradPendingOps()) {
std::unordered_set<std::shared_ptr<GradOpNode>> actual_pending_ops;
for (auto& op : new_op->GradPendingNodes()) {
actual_pending_ops.emplace(op);
}

@ -16,6 +16,7 @@
#include <unordered_set>
#include <utility>
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/imperative/op_base.h"
#include "paddle/fluid/platform/profiler.h"
#include "paddle/fluid/string/string_helper.h"
@ -31,49 +32,6 @@ void SetCurrentTracer(const std::shared_ptr<Tracer>& tracer) {
VLOG(6) << "Set current tracer: " << g_current_tracer;
}
static void ClearNoNeedBufferInputs(OpBase* op) {
auto& inferer = op->Info().NoNeedBufferVarsInferer();
if (!inferer) return;
auto* ins = op->GetMutableInsMap();
const auto& no_need_buffer_slots =
inferer(*ins, op->GetOutsMap(), op->Attrs());
if (no_need_buffer_slots.empty()) return;
for (auto& slot : no_need_buffer_slots) {
auto iter = ins->find(slot);
if (iter == ins->end()) continue;
VLOG(2) << "Clear data buffer of " << slot << " in " << op->Type();
for (auto& each_var : iter->second) {
if (!each_var) continue;
auto& var = each_var->Var();
PADDLE_ENFORCE_EQ(var.IsType<framework::LoDTensor>(), true,
"Only support LoDTensor");
// TODO(zjl): support higher order derivatives
auto new_var = new VariableWrapper(each_var->Name());
auto* new_tensor =
new_var->MutableVar()->GetMutable<framework::LoDTensor>();
auto& old_tensor = var.Get<framework::LoDTensor>();
new_tensor->Resize(old_tensor.dims());
new_tensor->set_lod(old_tensor.lod());
each_var.reset(new_var);
op->AddAllowedEmptyVar(new_var);
}
}
}
static std::vector<std::shared_ptr<OpBase>> CreateGradOpBases(
const framework::OpInfo& info, const std::string& type,
const NameVarBaseMap& in, const NameVarBaseMap& out,
const framework::AttributeMap& attrs) {
if (info.dygraph_grad_op_maker_) {
return info.dygraph_grad_op_maker_(type, in, out, attrs);
} else {
return {};
}
}
static void PassStopGradient(const NameVarBaseMap& outs, bool generate_grad) {
for (const auto& name_pair : outs) {
for (const auto& vb : name_pair.second) {
@ -103,7 +61,7 @@ void Tracer::TraceOp(const std::string& type, const NameVarBaseMap& ins,
}
if (ComputeRequiredGrad(ins, outs, trace_backward)) {
TraceBackward(op_info, type, ins, outs, attrs, place);
CreateGradOpNode(*op, ins, outs, attrs, place);
} else {
VLOG(3) << "No Grad to track for Op: " << type;
}
@ -133,22 +91,5 @@ bool Tracer::ComputeRequiredGrad(const NameVarBaseMap& ins,
return false;
}
void Tracer::TraceBackward(const framework::OpInfo& info,
const std::string& type, const NameVarBaseMap& ins,
const NameVarBaseMap& outs,
const framework::AttributeMap& attrs,
const platform::Place& place) {
auto grad_op_bases = CreateGradOpBases(info, type, ins, outs, attrs);
auto grad_op_num = grad_op_bases.size();
if (grad_op_num == 0) return;
size_t trace_id = GenerateUniqueId();
for (auto& grad_op : grad_op_bases) {
grad_op->SetPlace(place);
grad_op->SetId(trace_id);
ClearNoNeedBufferInputs(grad_op.get());
}
}
} // namespace imperative
} // namespace paddle

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save