Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support inplace in dygraph eager_fluid state #40400

Merged
merged 61 commits into from
Mar 18, 2022

Conversation

pangyoki
Copy link
Contributor

@pangyoki pangyoki commented Mar 10, 2022

PR types

New features

PR changes

Others

Describe

动态图中间态添加inplace策略。

python-c

实现要点:

  • 返回inplace输出tensor时,应该直接将对应inplace输入tensor的PyObject返回,不需新建一个PyObject对象,否则会导致python端的inplace输入输出的id不一致。
static PyObject * eager_api_exp_(PyObject *self, PyObject *args, PyObject *kwargs)
{
  PyThreadState *tstate = nullptr;
  try
  {
    
    auto& X = GetTensorFromArgs("exp", "X", args, 0, false);
    framework::AttributeMap attrs;
    ConstructAttrMapFromPyArgs("exp", args, 1, PyTuple_GET_SIZE(args) , attrs);
    tstate = PyEval_SaveThread();
    auto out = exp__dygraph_function(X, attrs);
    PyEval_RestoreThread(tstate);
    tstate = nullptr;
    ssize_t arg_id = GetIdxFromCoreOpsInfoMap(core_ops_args_info, "exp", "X");
    ssize_t return_id = GetIdxFromCoreOpsInfoMap(core_ops_returns_info, "exp", "Out");
    return ToPyObject(out, return_id, args, arg_id);
  }
  catch(...) {
    if (tstate) {
      PyEval_RestoreThread(tstate);
    }
    ThrowExceptionToPython(std::current_exception());
    return nullptr;
  }
}

动态图层

实现要点:

  • 自动代码生成的动态图执行流程有变(不影响功能):
    • 原流程:先跑TraceOp,然后生成输入和输出的auto_grad meta,然后构反向。
    • 新流程:因为需要为inplace op做check_inplace的检查,check_inplace检查在执行TraceOp之前比较好。另一方面,check_inplace需要使用输入auto_grad meta信息。所以流程变为:先创建输入的auto_grad meta信息,然后check_inplace,然后执行TraceOp生成输出,然后创建输出的auto_grad meta信息,然后构反向。
  • 实现inplace功能:TraceOp执行时,直接使用输入的EagerVariable替换输出。后续不重新创建输出Tensor,直接使用输入Tensor代替输出。
  • 实现inplace问题:因为EagerVariable内不会改变输入Tensor的meta信息(导致inplace reshape无法改变ddim信息),因此新增了ModifyInplaceInput修改inplace tensor的meta信息。
paddle::experimental::Tensor exp__dygraph_function(paddle::experimental::Tensor& X, const paddle::framework::AttributeMap& attr_map) {

  paddle::platform::RecordEvent dygraph_entrance_record_event("exp dygraph", paddle::platform::TracerEventType::Operator, 1);
  VLOG(3) << "Running Eager Forward Op: exp";
  // Dygraph Forward Pass

  std::map<std::string, std::vector<std::shared_ptr<egr::EagerVariable>>> ins = { { "X", egr::EagerUtils::TrySyncToVars(X) } };

  std::map<std::string, std::vector<std::shared_ptr<egr::EagerVariable>>> outs = { { "Out", ins["X"] } };


  // Prepare Autograd Meta 
  egr::AutogradMeta* p_autograd_X = egr::EagerUtils::nullable_autograd_meta(X);

  bool trace_backward = egr::Controller::Instance().HasGrad();

  bool require_any_grad = egr::EagerUtils::ComputeRequireGrad(trace_backward, p_autograd_X);
  // Check Inplace
  egr::EagerUtils::CheckInplace(X, p_autograd_X, require_any_grad);

  paddle::framework::AttributeMap attrs = attr_map;
  paddle::framework::AttributeMap default_attrs;
  egr::Controller::Instance().GetCurrentTracer()->TraceOp("exp", ins, outs, attrs, 
     egr::Controller::Instance().GetExpectedPlace(),
     &default_attrs, true, {{"X", "Out"}});

  egr::EagerUtils::ModifyInplaceInput(outs["Out"][0], &X);
  X.bump_inplace_version();
  VLOG(3) << "Tensor(" << X.name() << ") uses Inplace Strategy.";

  {
    paddle::platform::RecordEvent node_creation_record_event("exp node_creation", paddle::platform::TracerEventType::Operator, 1);
    p_autograd_X = egr::EagerUtils::autograd_meta(&X);
    if(require_any_grad) {
      VLOG(6) << " Construct Grad for exp "; 
      egr::EagerUtils::PassStopGradient(false, p_autograd_X);
      // Create GradOpNode
      auto grad_node = std::make_shared<GradNodeexp>(1, 1);

      // Set Attributes
      grad_node->SetAttrMap(std::move(attrs));
      grad_node->SetDefaultAttrMap(std::move(default_attrs));

      // Set Tensor Wrappers
      grad_node->SetTensorWrapperOut(X, false);

      grad_node->SetGradOutMeta(p_autograd_X, 0);
      if(p_autograd_X) grad_node->AddEdges(p_autograd_X, 0);
      egr::EagerUtils::SetOutRankWithSlot(p_autograd_X, 0);
      egr::EagerUtils::SetHistory(p_autograd_X, grad_node);
      grad_node->SetGradInMeta(p_autograd_X, 0);
      egr::EagerUtils::CheckAndRetainGrad(X);

    }
  }

  return X;

}

反向检测

  • TensorWrapper中加入snapshot_inplace_version_快照信息。
  • 在执行反向GradNode,从TensorWrapper recoverTensor时,进行inplace_version反向检测。比较snapshot_inplace_version_与Tensor的current_inplace_version_是否一致。

示例

  • inplace操作
with _test_eager_guard():
    a = paddle.rand([2,3])
    a.stop_gradient=False
    b = a * 2
    c = b ** 2
    d = c.exp_()
    print(c.inplace_version)
    # 1
    print(id(c) == id(d))
    # True
    d.sum().backward()
  • stop_gradient=False的叶子节点做inplace操作报错
with _test_eager_guard():
    a = paddle.rand([2,3])
    a.stop_gradient=False
    a.reshape_([-1])

# ValueError: (InvalidArgument) Leaf Var () that doesn't stop gradient can't use inplace strategy.
  • 反向检测报错
with _test_eager_guard():
    a = paddle.rand([2,3])
    a.stop_gradient=False
    b = a * 2
    c = b ** 2
    d = b.exp_()
    c.sum().backward()

# RuntimeError: (PermissionDenied) Tensor '' used in gradient computation has been modified by an inplace operation. Its version is 1 but the expected version is 0. Please fix your code to void calling an inplace operator after using the Tensor which will used in gradient computation.

@paddle-bot-old
Copy link

paddle-bot-old bot commented Mar 10, 2022

✅ This PR's description meets the template requirements!
Please wait for other CI results.

@paddle-bot-old
Copy link

Thanks for your contribution!
Please wait for the result of CI firstly. See Paddle CI Manual for details.

@@ -94,15 +105,52 @@ class TensorWrapper {
intermidiate_tensor_.set_autograd_meta(
std::static_pointer_cast<paddle::experimental::AbstractAutogradMeta>(
p_ab_autograd_meta));
check_inplace_version();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're gonna check inplace version anyway, let's move this function "check_inplace_version" out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in PR #41118

@@ -716,6 +716,15 @@ static PyObject* set_grad_type(TensorObject* self, PyObject* args,
EAGER_CATCH_AND_THROW_RETURN_NULL
}

static PyObject* tensor__inplace_version(TensorObject* self, PyObject* args,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

single underscore "_" in function name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its ok if this method indicate _inplace_version api in python

std::string ins_initializer_with_null = "";
std::string py_arg = "";
int arg_idx = 0;
int input_args_num = 0;
std::string ins_cast_str = "";
std::string view_strategy_str = "";
if (!inplace_map.empty()) {
// change call_api_str for inplace op
call_api_str = "auto out = " + op_type + "__dygraph_function(";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better add "" at the very end of the function name, like "scale_dygraph_function" for inplaced scale

jim19930609
jim19930609 previously approved these changes Mar 18, 2022
std::map<std::string, std::string> inplace_map;
// `sum` op has duplicate input. Don't consider adding inplace strategy
// for `sum` in temporary.
if (op_type != "sum" && infer_inplace) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better store hard-coded op name in a static set

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in PR #41118

JiabinYang
JiabinYang previously approved these changes Mar 18, 2022
Copy link
Contributor

@JiabinYang JiabinYang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@pangyoki pangyoki dismissed stale reviews from JiabinYang and jim19930609 via 9491a06 March 18, 2022 06:27
Copy link
Contributor

@XieYunshen XieYunshen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM for set_tests_properties(test_inplace_eager_fluid PROPERTIES TIMEOUT 120)

@pangyoki pangyoki merged commit 8e61290 into PaddlePaddle:develop Mar 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants