From bd91e1e03f14eb78f9d824056141a3bcd199806a Mon Sep 17 00:00:00 2001 From: liym27 <33742067+liym27@users.noreply.github.com> Date: Wed, 9 Sep 2020 21:13:16 +0800 Subject: [PATCH] [Dy2Stat] Add error doc and debugging doc in Chinese (#2562) --- .../dygraph_to_static/debugging_cn.md | 192 ++++++++++++++++++ .../dygraph_to_static/error_handling_cn.md | 147 ++++++++++++++ .../dygraph_to_static/index_cn.rst | 7 + 3 files changed, 346 insertions(+) create mode 100644 doc/fluid/advanced_guide/dygraph_to_static/debugging_cn.md create mode 100644 doc/fluid/advanced_guide/dygraph_to_static/error_handling_cn.md diff --git a/doc/fluid/advanced_guide/dygraph_to_static/debugging_cn.md b/doc/fluid/advanced_guide/dygraph_to_static/debugging_cn.md new file mode 100644 index 000000000..36586337b --- /dev/null +++ b/doc/fluid/advanced_guide/dygraph_to_static/debugging_cn.md @@ -0,0 +1,192 @@ +# 调试方法 + +本节内容将介绍动态图转静态图(下文简称动转静)推荐的几种调试方法。 + +注意:请确保转换前的动态图代码能够成功运行,建议使用[paddle.jit.ProgramTranslator().enable(False)](../../api_cn/dygraph_cn/ProgramTranslator_cn.html#enable)关闭动转静功能,直接运行动态图,如下: +```python +import paddle +import numpy as np +paddle.disable_static() +# 关闭动转静动能 +paddle.jit.ProgramTranslator().enable(False) + +@paddle.jit.to_static +def func(x): + x = paddle.to_tensor(x) + if x > 3: + x = x - 1 + return x + +func(np.ones([3, 2])) +``` + +## 断点调试 +使用动转静功能时,您可以使用断点调试代码。 +例如,在代码中,调用`pdb.set_trace()`: +```Python +import pdb + +@paddle.jit.to_static +def func(x): + x = paddle.to_tensor(x) + pdb.set_trace() + if x > 3: + x = x - 1 + return x +``` +执行以下代码,将会在转化后的静态图代码中使用调试器: +```Python +func(np.ones([3, 2])) +``` + +运行结果: +```bash +> /tmp/tmpR809hf.py(6)func() +-> def true_fn_0(x): +(Pdb) n +> /tmp/tmpR809hf.py(6)func() +-> def false_fn_0(x): +... +``` + +如果您想在原始的动态图代码中使用调试器,请先调用[`paddle.jit.ProgramTranslator().enable(False)`](../../api_cn/dygraph_cn/ProgramTranslator_cn.html#enable),如下: +```python +paddle.jit.ProgramTranslator().enable(False) +func(np.ones([3, 2])) +``` +运行结果: +```bash +> (10)func() +-> if x > 3: +... + +``` + +## 打印转换后的代码 +您可以打印转换后的静态图代码,有2种方法: + +1. 使用被装饰函数的`code` 属性 + ```Python + @paddle.jit.to_static + def func(x): + x = paddle.to_tensor(x) + if x > 3: + x = x - 1 + return x + + print(func.code) + ``` + 运行结果: + + ```bash + + def func(x): + x = fluid.layers.assign(x) + + def true_fn_0(x): + x = x - 1 + return x + + def false_fn_0(x): + return x + x = fluid.dygraph.dygraph_to_static.convert_operators.convert_ifelse(x > + 3, true_fn_0, false_fn_0, (x,), (x,), (x,)) + return x + ``` + +2. 使用`set_code_level(level)`或环境变量`TRANSLATOR_CODE_LEVEL=level` + + 通过调用`set_code_level`或设置环境变量`TRANSLATOR_CODE_LEVEL`,可以在log中查看转换后的代码 +```python +@paddle.jit.to_static +def func(x): + x = paddle.to_tensor(x) + if x > 3: + x = x - 1 + return x + +paddle.jit.set_code_level() # 也可设置 os.environ["TRANSLATOR_CODE_LEVEL"] = '100',效果相同 +func(np.ones([1])) +``` + 运行结果: + +```bash +2020-XX-XX 00:00:00,980-INFO: After the level 100 ast transformer: 'All Transformers', the transformed code: +def func(x): + x = fluid.layers.assign(x) + + def true_fn_0(x): + x = x - 1 + return x + + def false_fn_0(x): + return x + x = fluid.dygraph.dygraph_to_static.convert_operators.convert_ifelse(x > + 3, true_fn_0, false_fn_0, (x,), (x,), (x,)) + return x +``` + `set_code_level` 函数可以设置查看不同的AST Transformer转化后的代码,详情请见[set_code_level]()。 + +## 使用 `print` +`print` 函数可以用来查看变量,该函数在动转静中会被转化。当仅打印Paddle Tensor时,实际运行时会被转换为Paddle算子[Print](../../api_cn/layers_cn/Print_cn.html),否则仍然运行`print`。 +```python +@paddle.jit.to_static +def func(x): + x = paddle.to_tensor(x) + # 打印x,x是Paddle Tensor,实际运行时会运行Paddle Print(x) + print(x) + # 打印注释,非Paddle Tensor,实际运行时仍运行print + print("Here call print function.") + if len(x) > 3: + x = x - 1 + else: + x = paddle.ones(shape=[1]) + return x +func(np.ones([1])) +``` + +运行结果: +```bash +Variable: assign_0.tmp_0 + - lod: {} + - place: CPUPlace + - shape: [1] + - layout: NCHW + - dtype: double + - data: [1] +Here call print function. +``` + +## 日志打印 +ProgramTranslator在日志中记录了额外的调试信息,以帮助您了解动转静过程中函数是否被成功转换。 +您可以调用`paddle.jit.set_verbosity(level)` 或设置环境变量`TRANSLATOR_VERBOSITY=level`来设置日志详细等级,并查看不同等级的日志信息。目前,`level`可以取值0-3: +- 0: 无日志 +- 1: 包括了动转静转化流程的信息,如转换前的源码、转换的可调用对象 +- 2: 包括以上信息,还包括更详细函数转化日志 +- 3: 包括以上信息,以及更详细的动转静日志 + + +可以在代码运行前调用`paddle.jit.set_verbosity()`: +```python +paddle.jit.set_verbosity(3) +``` +或者设置环境变量`TRANSLATOR_VERBOSITY`: +```python +import os +os.environ["TRANSLATOR_VERBOSITY"] = '3' +``` + +运行结果: +```bash +2020-XX-XX 00:00:00,123-Level 1: Source code: +@paddle.jit.to_static +def func(x): + x = paddle.to_tensor(x) + if len(x) > 3: + x = x - 1 + else: + x = paddle.ones(shape=[1]) + return x + +2020-XX-XX 00:00:00,152-Level 1: Convert callable object: convert . +``` diff --git a/doc/fluid/advanced_guide/dygraph_to_static/error_handling_cn.md b/doc/fluid/advanced_guide/dygraph_to_static/error_handling_cn.md new file mode 100644 index 000000000..be835d1b2 --- /dev/null +++ b/doc/fluid/advanced_guide/dygraph_to_static/error_handling_cn.md @@ -0,0 +1,147 @@ +# 报错信息处理 + +本节内容将介绍使用动态图转静态图(下文简称动转静)功能发生异常时,[ProgramTranslator](./program_translator_cn.html)对报错信息做的处理,以帮助您更好地理解动转静报错信息。使用动转静功能运行动态图代码时,内部可以分为2个步骤:动态图代码转换成静态图代码,运行静态图代码。接下来将分别介绍这2个步骤中的异常报错情况。 + +## 动转静过程中的异常 +在动态图代码转换成静态图代码的过程中,如果ProgramTranslator无法转换一个函数时,将会显示警告信息,并尝试直接运行该函数。 +如下代码中,函数`inner_func` 在调用前被转换成静态图代码,当`x = inner_func(data)`调用该函数时,不能重复转换,会给出警告信息: +```python +import paddle +import numpy as np + +paddle.disable_static() + +@paddle.jit.to_static +def func(): + def inner_func(x): + x_tensor = paddle.to_tensor(x) + return x_tensor + data = np.ones([3]).astype("int32") + x = inner_func(data) + return x +func() +``` +ProgramTranslator打印的警告信息如下: +```bash +WARNING: doesn't have to be transformed to static function because it has been transformed before, it will be run as-is. +``` +## 运行转换后的代码报错 +如果在动转静后的静态图代码中发生异常,ProgramTranslator会捕获该异常,增强异常报错信息,将静态图代码报错行映射到转换前的动态图代码,并重新抛出该异常。 +重新抛出的异常具有以下特点: + +- 隐藏了部分对用户无用的动转静过程调用栈; +- 转换前的代码会给出提示:"In User Code:"; +- 报错信息中包含了转换前的原始动态图代码; + +例如,运行以下代码,在静态图构建时,即编译期会抛出异常: +```python +import paddle +import numpy as np + +paddle.disable_static() + +@paddle.jit.to_static +def func(x): + x = paddle.to_tensor(x) + x = paddle.reshape(x, shape=[-1, -1]) + return x + +func(np.ones([3, 2])) +``` +运行结果: +```bash +Traceback (most recent call last): + in () + func(np.ones([3, 2])) + File "paddle/fluid/dygraph/dygraph_to_static/program_translator.py", line 332, in __call__ + raise new_exception +AssertionError: In user code: + + File "", line 7, in func + x = fluid.layers.reshape(x, shape=[-1, -1]) + File "paddle/fluid/layers/nn.py", line 6193, in reshape + attrs["shape"] = get_attr_shape(shape) + File "paddle/fluid/layers/nn.py", line 6169, in get_attr_shape + "be -1. But received shape[%d] is also -1." % dim_idx) + AssertionError: Only one dimension value of 'shape' in reshape can be -1. But received shape[1] is also -1. +``` + +上述报错信息可以分为3点: + +1. 报错栈中,涉及代码转换过程的信息栈默认会被隐藏,不对用户展示,以避免给用户带来困扰。 +2. ProgramTranslator处理后的报错信息中,会包含提示"In user code:",表示之后的报错栈中,包含动转静前的动态图代码,即用户写的代码: + ```bash + AssertionError: In user code: + + File "", line 7, in func + x = fluid.layers.reshape(x, shape=[-1, -1]) + File "paddle/fluid/layers/nn.py", line 6193, in reshape + attrs["shape"] = get_attr_shape(shape) + File "paddle/fluid/layers/nn.py", line 6169, in get_attr_shape + "be -1. But received shape[%d] is also -1." % dim_idx) + ``` + 其中,`File "", line 7, in func` 是转换前的代码位置信息,`x = fluid.layers.reshape(x, shape=[-1, -1])` 是转换前的代码。 +3. 新的异常中,包含原始报错中的的报错信息,如下: + ```bash + AssertionError: Only one dimension value of 'shape' in reshape can be -1. But received shape[1] is also -1. + ``` + +运行以下代码,在静态图运行时,即运行期会抛出异常: +```Python +@paddle.jit.to_static +def func(x): + x = paddle.to_tensor(x) + two = paddle.fill_constant(shape=[1], value=2, dtype="int32") + x = paddle.reshape(x, shape=[1, two]) + return x + +func(np.ones([3]).astype("int32")) +``` +运行结果: +```bash +Traceback (most recent call last): + File "", line 10, in () + func(np.ones([3]).astype("int32")) + File "paddle/fluid/dygraph/dygraph_to_static/program_translator.py", line 332, in __call__ + raise new_exception + +EnforceNotMet: In user code: + + File "", line 7, in func + x = paddle.reshape(x, shape=[1, two]) + File "paddle/tensor/manipulation.py", line 1347, in reshape + return paddle.fluid.layers.reshape(x=x, shape=shape, name=name) + File "paddle/fluid/layers/nn.py", line 6209, in reshape + "XShape": x_shape}) + File "paddle/fluid/layer_helper.py", line 43, in append_op + return self.main_program.current_block().append_op(*args, **kwargs) + File "paddle/fluid/framework.py", line 2880, in append_op + attrs=kwargs.get("attrs", None)) + File "paddle/fluid/framework.py", line 1977, in __init__ + for frame in traceback.extract_stack(): + +-------------------------------------- +C++ Traceback (most recent call last): +-------------------------------------- +0 paddle::imperative::Tracer::TraceOp(std::string const&, paddle::imperative::NameVarBaseMap const&, paddle::imperative::NameVarBaseMap const&, paddle::framework::AttributeMap, paddle::platform::Place const&, bool) +1 paddle::imperative::OpBase::Run(paddle::framework::OperatorBase const&, paddle::imperative::NameVarBaseMap const&, paddle::imperative::NameVarBaseMap const&, paddle::framework::AttributeMap const&, paddle::platform::Place const&) +2 paddle::imperative::PreparedOp::Run(paddle::imperative::NameVarBaseMap const&, paddle::imperative::NameVarBaseMap const&, paddle::framework::AttributeMap const&) +3 std::_Function_handler >::operator()(char const*, char const*, int) const::{lambda(paddle::framework::ExecutionContext const&)#1}>::_M_invoke(std::_Any_data const&, paddle::framework::ExecutionContext const&) +4 paddle::operators::RunProgramOpKernel::Compute(paddle::framework::ExecutionContext const&) const +5 paddle::framework::Executor::RunPartialPreparedContext(paddle::framework::ExecutorPrepareContext*, paddle::framework::Scope*, long, long, bool, bool, bool) +6 paddle::framework::OperatorBase::Run(paddle::framework::Scope const&, paddle::platform::Place const&) +7 paddle::framework::OperatorWithKernel::RunImpl(paddle::framework::Scope const&, paddle::platform::Place const&) const +8 paddle::framework::OperatorWithKernel::RunImpl(paddle::framework::Scope const&, paddle::platform::Place const&, paddle::framework::RuntimeContext*) const +9 paddle::operators::ReshapeKernel::operator()(paddle::framework::ExecutionContext const&) const +10 paddle::operators::ReshapeOp::ValidateShape(std::vector >, paddle::framework::DDim const&) +11 paddle::platform::EnforceNotMet::EnforceNotMet(std::string const&, char const*, int) +12 paddle::platform::GetCurrentTraceBackString() + +---------------------- +Error Message Summary: +---------------------- +InvalidArgumentError: The 'shape' in ReshapeOp is invalid. The input tensor X'size must be equal to the capacity of 'shape'. But received X's shape = [3], X's size = 3, 'shape' is [1, 2], the capacity of 'shape' is 2. + [Hint: Expected capacity == in_size, but received capacity:2 != in_size:3.] (at /paddle/paddle/fluid/operators/reshape_op.cc:206) + [operator < reshape2 > error] [operator < run_program > error] +``` +上述异常中,除了隐藏部分报错栈、报错定位到转换前的动态图代码外,报错信息中包含了C++报错栈`C++ Traceback`和`Error Message Summary`,这是Paddle的C++端异常信息,经处理后在Python的异常信息中显示。 diff --git a/doc/fluid/advanced_guide/dygraph_to_static/index_cn.rst b/doc/fluid/advanced_guide/dygraph_to_static/index_cn.rst index 40ab04d3e..cd4ee8e11 100644 --- a/doc/fluid/advanced_guide/dygraph_to_static/index_cn.rst +++ b/doc/fluid/advanced_guide/dygraph_to_static/index_cn.rst @@ -6,8 +6,15 @@ - `支持语法列表 `_ :介绍了动态图转静态图支持的语法以及罗列不支持的语法写法 +- `报错信息处理 `_ :介绍了动态图转静态图支持的报错信息处理方法 + +- `调试方法 `_ :介绍了动态图转静态图支持的调试方法 + + .. toctree:: :hidden: grammar_list_cn.rst program_translator_cn.rst + error_handling_cn.md + debugging_cn.md -- GitLab