提交 4a3fd67d 编写于 作者: T Travis CI

Deploy to GitHub Pages: 6a5cf28a

上级 49f9dc86
# Backward Building
## Motivation
In Neural Network, most models are solved by the backpropagation algorithm(known as **BP**) at present. Technically, BP calculates the gradient of the loss function, then propagates it back through the networks following the chain rule. However, when configuring the model structure, users do not need to define the backward part. So a mechanism is required by the framework which can complete the model's backward part automatically according to the given forward part.
When implementing a specific `op`, the developer is also asked to implement its backward version, called `grad_op`. A `grad_op` takes gradients of its corresponding `op`'s outputs, and calculate gradients of the `op`'s inputs. During the building of a model's backward part, the framework creates each forward `op`'s `grad_op`, and then string them together in reverse order of forwarding part. In this way, gradients spread from the end to the beginning of the model, in another word, from the loss to parameters.
## Challenges
The motivation of backward building is apparent. However, implementation it correctly is not so easy. In the **Fluid** design, a deep learning model is described by `Program`, `Block`, `Op` and `Variable`. The `Block` itself can be nested. It means that the `op`s and `variable`s are scattered across different blocks rather than all be gathered in a single graph. Our backward building algorithm shall visit blocks in recursive order and be able to insert `grad_op`s and new created `variable`s into the right place.
## Usage
Although the whole algorithm is comprised of many functions, only one is exposed as API:
```python
def append_backward(loss, parameter_list=None, no_grad_set=None):
"""
Append backward part to main_program
Args:
loss(Variable): The variable generated by the cost function.
parameter_list(list): Parameters that need to be updated by optimizers.
If None, it means all parameters need to be updated.
no_grad_set(set): Variables that have no gradients in Block 0.
If None, the set will be generated inside the function and
contains all variables with `step_gradient=True` from all blocks.
Return:
(list[Variable]): list of (parameters, gradients) pair.
"""
```
By invoking this API, the framework appends backward part of the program where the `loss` is. It takes three arguments. `loss` means the final loss value. It must be a scalar and is usually the output of the loss layer. It is also where the gradient generated and backpropagation starts. `parameter_list` marks all parameters needs updating. If it's `None`, all parameter will be updated by optimizers. `no_grad_set` marks variables without gradient. if all outputs of some `grad_op` are in `no_grad_set`, the `grad_op` will not be run.
This API will be invoked automatically before optimizer building.
As a result, in most cases, users do not need to invoke the API by themselves to append backward part.
## Implementation
The implementation of backward building algorithm is in `backward.py` file. The whole algorithm can be divided into two independent parts: creating `grad_op`s and creating new variables.
### Creating `grad_op`s
The creating of `grad_op`s is implemented by:
```python
def _append_backward_ops_(target,
block,
target_block,
no_grad_dict,
grad_to_var):
"""
Create all grad ops, and insert them into given block
Args:
target(Variable): the target variable of forward pass
block(Block): the block where forward ops are
target_block(Block): the block which is going to hold new generated grad ops
no_grad_dict(dict):
key(int) block index
val(set) a set of varibale names. These varibales have no gradient
grad_to_var(dict)(output argument):
key(str): grad variable name
val(str): corresponding forward variable name
"""
```
Given a `block`, the function will traverses all `op`s in this block in reverse order, gets corresponding `grad_op` from the C++ core via `core.get_grad_op_desc()`, then append it to `target_block`.
However, some specific `op`(e.g. `while_op`, `if_else_op`) can hold its own sub-block. For these sub-blocks contains `op`s as well, the `grad_op` creating should be recursive.
During the reverse traversal, we check each `op` whether it has an attribute named `sub_block`. If so, it means there is a sub-block and we need to deal with it first. After creating a new block whose father is the one in `op`'s attribute, we invoke `_append_backward_ops_()` recursively, assigning the new block to parameter `target_block` and the one in `op`'s attribute to `block`. The *pseudo-code* shows this process:
```
******* pseudo-code ********
for op in reversed(block.ops):
if op has an attribute named 'sub_block':
Get the sub-block(`s_block`) from op's attribute.
Create a new block(`grad_s_block`), whose father is `s_block`.
Invoke _append_backward_ops_(), with `block=s_block` and `target_block=grad_s_block`
Invoke `core.get_grad_op_desc()` to get op's grad_op.
Insert name correspondings between variables and their gradients of the grad_op to grad_to_var
Assign grad_s_block to grad_op as it's 'sub_block' attribute.
Append grad_op to current target_block.
```
The first invoking of `_append_backward_ops_()` is initiated by `append_backward()`, in which parameters `block` and `target_block` are all assigned with root block(the block with index 0).
### Corner Cases of `grad_op` Creating
In the previous section, we show the regular process of `grad_op` creating. However, in some corner cases, the conventional algorithm is not enough to get the correct result and appending handling is required. These additional processes run after the algorithm mentioned above and do some special adjusts on its output `grad_op`s.
#### Shared Variables
If a variable is read by more than one `op` in the forward pass, its gradient is likely to be written by more than one `grad_op`s in the next backward pass. To make the gradient result being the sum of all `grad_op`s' outputs instead of the last running one, we assign each output with a temporary variable and then add a `sum_op` to add them up.
For the debug convenience, if the final gradient name is `w@GRAD`, it's corresponding temporary variables will be named as `w@GRAD@RENAME@0`, `w@GRAD@RENAME@1`...
See function `_addup_repetitive_outputs_` in `backward.py` for implementation details.
#### No Gradient Variables
In our framework, variables can be marked as *no_gradient*, it means that the gradient of this variable is unnecessary and can be considered as zero in model training. Apparently, when all the outputs of some `grad_op` are marked as *no_gradient*, the `grad_op` itself can be skipped in backward pass.
But these unnecessary gradients still need to be creating and initialized by something, otherwise following `grad_op`s who take these gradients as inputs take the risk of using uninitialized memory. In our code, we employ `fill_zeros_like_op` to initialize them as all zeros.
This features are implemented in function `_remove_no_grad_branch_`. It checks new created `grad_op`s one-by-one, removes whose outputs are all in `no_grad_set` or inserts `fill_zeros_like_op` when its necessary. We can get the `no_grad_set` from the `_append_backward_ops_` argument `no_grad_dict` or generate it on the fly by scanning all variables' `no_gradient` attribute(True or False).
### Creating Backward Variables
Up to now, we have completed all creating and adjusting jobs of `grad_op`s. However, backward variables have not been created. Now they are only represented by `grad_op`'s input and output arguments. The backward variable creating job will be done by:
```python
def _append_backward_vars_(block,
start_op_idx,
grad_to_var,
grad_info_map):
"""
Create new variables required by backward pass.
Args:
block(Block): the block where new variables will be created
start_op_idx(int): Only variables required by ops in block.ops[start_op_idx : ] will be created
grad_to_var(dict):
key(str): grad variable name
val(str): corresponding forward variable name
In most cases, this dict is generated by _append_backward_ops_()
grad_info_map(dict)(output argument):
key(str): forward variable name
val(tuple): a tuple of (str, int), str is the corresponding grad name, int is the block index
"""
```
Given a `block`, this function traverses all the `grad_op`s in it(The argument `start_op_idx` indicates where the grad_op sequence starts.) and creates all the uncreated outputs. The *pseudo-code* shows this process:
```
for op in block.ops[start_op_idx : ]:
if op has an attribute named 'sub_block':
Get the sub-block(`s_block`) from op's attribute.
Invoke _append_backward_vars_(), with `block=s_block`
for var_name in op.all_output_names():
if block.has_var_recursive(var_name) or var_name is the name of empty variable:
continue
create a new variable named 'var_name' in block
if grad_to_var.has_key(var_name):
set grad_info_map[grad_to_var[var_name]] as a tuple of (var_name. block)
do op's var type inference
do op's shape inference
```
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Backward Building &mdash; PaddlePaddle documentation</title>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="index" title="Index"
href="../genindex.html"/>
<link rel="search" title="Search" href="../search.html"/>
<link rel="top" title="PaddlePaddle documentation" href="../index.html"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/perfect-scrollbar/0.6.14/css/perfect-scrollbar.min.css" type="text/css" />
<link rel="stylesheet" href="../_static/css/override.css" type="text/css" />
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?b9a314ab40d04d805655aab1deee08ba";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<script src="../_static/js/modernizr.min.js"></script>
</head>
<body class="wy-body-for-nav" role="document">
<header class="site-header">
<div class="site-logo">
<a href="/"><img src="../_static/images/PP_w.png"></a>
</div>
<div class="site-nav-links">
<div class="site-menu">
<a class="fork-on-github" href="https://github.com/PaddlePaddle/Paddle" target="_blank"><i class="fa fa-github"></i>Fork me on Github</a>
<div class="language-switcher dropdown">
<a type="button" data-toggle="dropdown">
<span>English</span>
<i class="fa fa-angle-up"></i>
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu">
<li><a href="/doc_cn">中文</a></li>
<li><a href="/doc">English</a></li>
</ul>
</div>
<ul class="site-page-links">
<li><a href="/">Home</a></li>
</ul>
</div>
<div class="doc-module">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../getstarted/index_en.html">GET STARTED</a></li>
<li class="toctree-l1"><a class="reference internal" href="../howto/index_en.html">HOW TO</a></li>
<li class="toctree-l1"><a class="reference internal" href="../api/index_en.html">API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../mobile/index_en.html">MOBILE</a></li>
</ul>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
</div>
</header>
<div class="main-content-wrap">
<nav class="doc-menu-vertical" role="navigation">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../getstarted/index_en.html">GET STARTED</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../getstarted/build_and_install/index_en.html">Install and Build</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../getstarted/build_and_install/pip_install_en.html">Install Using pip</a></li>
<li class="toctree-l3"><a class="reference internal" href="../getstarted/build_and_install/docker_install_en.html">Run in Docker Containers</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/dev/build_en.html">Build using Docker</a></li>
<li class="toctree-l3"><a class="reference internal" href="../getstarted/build_and_install/build_from_source_en.html">Build from Sources</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../howto/index_en.html">HOW TO</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../howto/usage/cmd_parameter/index_en.html">Set Command-line Parameters</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cmd_parameter/use_case_en.html">Use Case</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cmd_parameter/arguments_en.html">Argument Outline</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cmd_parameter/detail_introduction_en.html">Detail Description</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../howto/usage/cluster/cluster_train_en.html">Distributed Training</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/fabric_en.html">fabric</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/openmpi_en.html">openmpi</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/k8s_en.html">kubernetes</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/k8s_aws_en.html">kubernetes on AWS</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../howto/dev/new_layer_en.html">Write New Layers</a></li>
<li class="toctree-l2"><a class="reference internal" href="../howto/dev/contribute_to_paddle_en.html">Contribute Code</a></li>
<li class="toctree-l2"><a class="reference internal" href="../howto/dev/write_docs_en.html">Contribute Documentation</a></li>
<li class="toctree-l2"><a class="reference internal" href="../howto/deep_model/rnn/index_en.html">RNN Models</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../howto/deep_model/rnn/rnn_config_en.html">RNN Configuration</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../howto/optimization/gpu_profiling_en.html">Tune GPU Performance</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../api/index_en.html">API</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../api/v2/model_configs.html">Model Configuration</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/activation.html">Activation</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/layer.html">Layers</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/evaluators.html">Evaluators</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/optimizer.html">Optimizer</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/pooling.html">Pooling</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/networks.html">Networks</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/attr.html">Parameter Attribute</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../api/v2/data.html">Data Reader Interface and DataSets</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/data/data_reader.html">Data Reader Interface</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/data/image.html">Image Interface</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/data/dataset.html">Dataset</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../api/v2/run_logic.html">Training and Inference</a></li>
<li class="toctree-l2"><a class="reference internal" href="../api/v2/fluid.html">Fluid</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/layers.html">Layers</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/data_feeder.html">DataFeeder</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/executor.html">Executor</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/initializer.html">Initializer</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/evaluator.html">Evaluator</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/nets.html">Nets</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/optimizer.html">Optimizer</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/param_attr.html">ParamAttr</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/profiler.html">Profiler</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/regularizer.html">Regularizer</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../mobile/index_en.html">MOBILE</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../mobile/cross_compiling_for_android_en.html">Build PaddlePaddle for Android</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mobile/cross_compiling_for_ios_en.html">PaddlePaddle Compiling Guide for iOS</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mobile/cross_compiling_for_raspberry_en.html">Build PaddlePaddle for Raspberry Pi</a></li>
</ul>
</li>
</ul>
</nav>
<section class="doc-content-wrap">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li>Backward Building</li>
</ul>
</div>
<div class="wy-nav-content" id="doc-content">
<div class="rst-content">
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="backward-building">
<span id="backward-building"></span><h1>Backward Building<a class="headerlink" href="#backward-building" title="Permalink to this headline"></a></h1>
<div class="section" id="motivation">
<span id="motivation"></span><h2>Motivation<a class="headerlink" href="#motivation" title="Permalink to this headline"></a></h2>
<p>In Neural Network, most models are solved by the backpropagation algorithm(known as <strong>BP</strong>) at present. Technically, BP calculates the gradient of the loss function, then propagates it back through the networks following the chain rule. However, when configuring the model structure, users do not need to define the backward part. So a mechanism is required by the framework which can complete the model&#8217;s backward part automatically according to the given forward part.</p>
<p>When implementing a specific <code class="docutils literal"><span class="pre">op</span></code>, the developer is also asked to implement its backward version, called <code class="docutils literal"><span class="pre">grad_op</span></code>. A <code class="docutils literal"><span class="pre">grad_op</span></code> takes gradients of its corresponding <code class="docutils literal"><span class="pre">op</span></code>&#8216;s outputs, and calculate gradients of the <code class="docutils literal"><span class="pre">op</span></code>&#8216;s inputs. During the building of a model&#8217;s backward part, the framework creates each forward <code class="docutils literal"><span class="pre">op</span></code>&#8216;s <code class="docutils literal"><span class="pre">grad_op</span></code>, and then string them together in reverse order of forwarding part. In this way, gradients spread from the end to the beginning of the model, in another word, from the loss to parameters.</p>
</div>
<div class="section" id="challenges">
<span id="challenges"></span><h2>Challenges<a class="headerlink" href="#challenges" title="Permalink to this headline"></a></h2>
<p>The motivation of backward building is apparent. However, implementation it correctly is not so easy. In the <strong>Fluid</strong> design, a deep learning model is described by <code class="docutils literal"><span class="pre">Program</span></code>, <code class="docutils literal"><span class="pre">Block</span></code>, <code class="docutils literal"><span class="pre">Op</span></code> and <code class="docutils literal"><span class="pre">Variable</span></code>. The <code class="docutils literal"><span class="pre">Block</span></code> itself can be nested. It means that the <code class="docutils literal"><span class="pre">op</span></code>s and <code class="docutils literal"><span class="pre">variable</span></code>s are scattered across different blocks rather than all be gathered in a single graph. Our backward building algorithm shall visit blocks in recursive order and be able to insert <code class="docutils literal"><span class="pre">grad_op</span></code>s and new created <code class="docutils literal"><span class="pre">variable</span></code>s into the right place.</p>
</div>
<div class="section" id="usage">
<span id="usage"></span><h2>Usage<a class="headerlink" href="#usage" title="Permalink to this headline"></a></h2>
<p>Although the whole algorithm is comprised of many functions, only one is exposed as API:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">append_backward</span><span class="p">(</span><span class="n">loss</span><span class="p">,</span> <span class="n">parameter_list</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">no_grad_set</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Append backward part to main_program</span>
<span class="sd"> Args:</span>
<span class="sd"> loss(Variable): The variable generated by the cost function.</span>
<span class="sd"> parameter_list(list): Parameters that need to be updated by optimizers.</span>
<span class="sd"> If None, it means all parameters need to be updated.</span>
<span class="sd"> no_grad_set(set): Variables that have no gradients in Block 0. </span>
<span class="sd"> If None, the set will be generated inside the function and </span>
<span class="sd"> contains all variables with `step_gradient=True` from all blocks.</span>
<span class="sd"> </span>
<span class="sd"> Return:</span>
<span class="sd"> (list[Variable]): list of (parameters, gradients) pair.</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>By invoking this API, the framework appends backward part of the program where the <code class="docutils literal"><span class="pre">loss</span></code> is. It takes three arguments. <code class="docutils literal"><span class="pre">loss</span></code> means the final loss value. It must be a scalar and is usually the output of the loss layer. It is also where the gradient generated and backpropagation starts. <code class="docutils literal"><span class="pre">parameter_list</span></code> marks all parameters needs updating. If it&#8217;s <code class="docutils literal"><span class="pre">None</span></code>, all parameter will be updated by optimizers. <code class="docutils literal"><span class="pre">no_grad_set</span></code> marks variables without gradient. if all outputs of some <code class="docutils literal"><span class="pre">grad_op</span></code> are in <code class="docutils literal"><span class="pre">no_grad_set</span></code>, the <code class="docutils literal"><span class="pre">grad_op</span></code> will not be run.</p>
<p>This API will be invoked automatically before optimizer building.
As a result, in most cases, users do not need to invoke the API by themselves to append backward part.</p>
</div>
<div class="section" id="implementation">
<span id="implementation"></span><h2>Implementation<a class="headerlink" href="#implementation" title="Permalink to this headline"></a></h2>
<p>The implementation of backward building algorithm is in <code class="docutils literal"><span class="pre">backward.py</span></code> file. The whole algorithm can be divided into two independent parts: creating <code class="docutils literal"><span class="pre">grad_op</span></code>s and creating new variables.</p>
<div class="section" id="creating-grad-ops">
<span id="creating-grad-ops"></span><h3>Creating <code class="docutils literal"><span class="pre">grad_op</span></code>s<a class="headerlink" href="#creating-grad-ops" title="Permalink to this headline"></a></h3>
<p>The creating of <code class="docutils literal"><span class="pre">grad_op</span></code>s is implemented by:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">_append_backward_ops_</span><span class="p">(</span><span class="n">target</span><span class="p">,</span>
<span class="n">block</span><span class="p">,</span>
<span class="n">target_block</span><span class="p">,</span>
<span class="n">no_grad_dict</span><span class="p">,</span>
<span class="n">grad_to_var</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Create all grad ops, and insert them into given block</span>
<span class="sd"> Args:</span>
<span class="sd"> target(Variable): the target variable of forward pass</span>
<span class="sd"> block(Block): the block where forward ops are</span>
<span class="sd"> target_block(Block): the block which is going to hold new generated grad ops</span>
<span class="sd"> no_grad_dict(dict): </span>
<span class="sd"> key(int) block index</span>
<span class="sd"> val(set) a set of varibale names. These varibales have no gradient</span>
<span class="sd"> grad_to_var(dict)(output argument):</span>
<span class="sd"> key(str): grad variable name</span>
<span class="sd"> val(str): corresponding forward variable name</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>Given a <code class="docutils literal"><span class="pre">block</span></code>, the function will traverses all <code class="docutils literal"><span class="pre">op</span></code>s in this block in reverse order, gets corresponding <code class="docutils literal"><span class="pre">grad_op</span></code> from the C++ core via <code class="docutils literal"><span class="pre">core.get_grad_op_desc()</span></code>, then append it to <code class="docutils literal"><span class="pre">target_block</span></code>.</p>
<p>However, some specific <code class="docutils literal"><span class="pre">op</span></code>(e.g. <code class="docutils literal"><span class="pre">while_op</span></code>, <code class="docutils literal"><span class="pre">if_else_op</span></code>) can hold its own sub-block. For these sub-blocks contains <code class="docutils literal"><span class="pre">op</span></code>s as well, the <code class="docutils literal"><span class="pre">grad_op</span></code> creating should be recursive.</p>
<p>During the reverse traversal, we check each <code class="docutils literal"><span class="pre">op</span></code> whether it has an attribute named <code class="docutils literal"><span class="pre">sub_block</span></code>. If so, it means there is a sub-block and we need to deal with it first. After creating a new block whose father is the one in <code class="docutils literal"><span class="pre">op</span></code>&#8216;s attribute, we invoke <code class="docutils literal"><span class="pre">_append_backward_ops_()</span></code> recursively, assigning the new block to parameter <code class="docutils literal"><span class="pre">target_block</span></code> and the one in <code class="docutils literal"><span class="pre">op</span></code>&#8216;s attribute to <code class="docutils literal"><span class="pre">block</span></code>. The <em>pseudo-code</em> shows this process:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>******* pseudo-code ********
for op in reversed(block.ops):
if op has an attribute named &#39;sub_block&#39;:
Get the sub-block(`s_block`) from op&#39;s attribute.
Create a new block(`grad_s_block`), whose father is `s_block`.
Invoke _append_backward_ops_(), with `block=s_block` and `target_block=grad_s_block`
Invoke `core.get_grad_op_desc()` to get op&#39;s grad_op.
Insert name correspondings between variables and their gradients of the grad_op to grad_to_var
Assign grad_s_block to grad_op as it&#39;s &#39;sub_block&#39; attribute.
Append grad_op to current target_block.
</pre></div>
</div>
<p>The first invoking of <code class="docutils literal"><span class="pre">_append_backward_ops_()</span></code> is initiated by <code class="docutils literal"><span class="pre">append_backward()</span></code>, in which parameters <code class="docutils literal"><span class="pre">block</span></code> and <code class="docutils literal"><span class="pre">target_block</span></code> are all assigned with root block(the block with index 0).</p>
</div>
<div class="section" id="corner-cases-of-grad-op-creating">
<span id="corner-cases-of-grad-op-creating"></span><h3>Corner Cases of <code class="docutils literal"><span class="pre">grad_op</span></code> Creating<a class="headerlink" href="#corner-cases-of-grad-op-creating" title="Permalink to this headline"></a></h3>
<p>In the previous section, we show the regular process of <code class="docutils literal"><span class="pre">grad_op</span></code> creating. However, in some corner cases, the conventional algorithm is not enough to get the correct result and appending handling is required. These additional processes run after the algorithm mentioned above and do some special adjusts on its output <code class="docutils literal"><span class="pre">grad_op</span></code>s.</p>
<div class="section" id="shared-variables">
<span id="shared-variables"></span><h4>Shared Variables<a class="headerlink" href="#shared-variables" title="Permalink to this headline"></a></h4>
<p>If a variable is read by more than one <code class="docutils literal"><span class="pre">op</span></code> in the forward pass, its gradient is likely to be written by more than one <code class="docutils literal"><span class="pre">grad_op</span></code>s in the next backward pass. To make the gradient result being the sum of all <code class="docutils literal"><span class="pre">grad_op</span></code>s&#8217; outputs instead of the last running one, we assign each output with a temporary variable and then add a <code class="docutils literal"><span class="pre">sum_op</span></code> to add them up.</p>
<p>For the debug convenience, if the final gradient name is <code class="docutils literal"><span class="pre">w&#64;GRAD</span></code>, it&#8217;s corresponding temporary variables will be named as <code class="docutils literal"><span class="pre">w&#64;GRAD&#64;RENAME&#64;0</span></code>, <code class="docutils literal"><span class="pre">w&#64;GRAD&#64;RENAME&#64;1</span></code>...</p>
<p>See function <code class="docutils literal"><span class="pre">_addup_repetitive_outputs_</span></code> in <code class="docutils literal"><span class="pre">backward.py</span></code> for implementation details.</p>
</div>
<div class="section" id="no-gradient-variables">
<span id="no-gradient-variables"></span><h4>No Gradient Variables<a class="headerlink" href="#no-gradient-variables" title="Permalink to this headline"></a></h4>
<p>In our framework, variables can be marked as <em>no_gradient</em>, it means that the gradient of this variable is unnecessary and can be considered as zero in model training. Apparently, when all the outputs of some <code class="docutils literal"><span class="pre">grad_op</span></code> are marked as <em>no_gradient</em>, the <code class="docutils literal"><span class="pre">grad_op</span></code> itself can be skipped in backward pass.</p>
<p>But these unnecessary gradients still need to be creating and initialized by something, otherwise following <code class="docutils literal"><span class="pre">grad_op</span></code>s who take these gradients as inputs take the risk of using uninitialized memory. In our code, we employ <code class="docutils literal"><span class="pre">fill_zeros_like_op</span></code> to initialize them as all zeros.</p>
<p>This features are implemented in function <code class="docutils literal"><span class="pre">_remove_no_grad_branch_</span></code>. It checks new created <code class="docutils literal"><span class="pre">grad_op</span></code>s one-by-one, removes whose outputs are all in <code class="docutils literal"><span class="pre">no_grad_set</span></code> or inserts <code class="docutils literal"><span class="pre">fill_zeros_like_op</span></code> when its necessary. We can get the <code class="docutils literal"><span class="pre">no_grad_set</span></code> from the <code class="docutils literal"><span class="pre">_append_backward_ops_</span></code> argument <code class="docutils literal"><span class="pre">no_grad_dict</span></code> or generate it on the fly by scanning all variables&#8217; <code class="docutils literal"><span class="pre">no_gradient</span></code> attribute(True or False).</p>
</div>
</div>
<div class="section" id="creating-backward-variables">
<span id="creating-backward-variables"></span><h3>Creating Backward Variables<a class="headerlink" href="#creating-backward-variables" title="Permalink to this headline"></a></h3>
<p>Up to now, we have completed all creating and adjusting jobs of <code class="docutils literal"><span class="pre">grad_op</span></code>s. However, backward variables have not been created. Now they are only represented by <code class="docutils literal"><span class="pre">grad_op</span></code>&#8216;s input and output arguments. The backward variable creating job will be done by:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">_append_backward_vars_</span><span class="p">(</span><span class="n">block</span><span class="p">,</span>
<span class="n">start_op_idx</span><span class="p">,</span>
<span class="n">grad_to_var</span><span class="p">,</span>
<span class="n">grad_info_map</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Create new variables required by backward pass.</span>
<span class="sd"> Args:</span>
<span class="sd"> block(Block): the block where new variables will be created</span>
<span class="sd"> start_op_idx(int): Only variables required by ops in block.ops[start_op_idx : ] will be created</span>
<span class="sd"> grad_to_var(dict):</span>
<span class="sd"> key(str): grad variable name</span>
<span class="sd"> val(str): corresponding forward variable name</span>
<span class="sd"> In most cases, this dict is generated by _append_backward_ops_()</span>
<span class="sd"> grad_info_map(dict)(output argument):</span>
<span class="sd"> key(str): forward variable name</span>
<span class="sd"> val(tuple): a tuple of (str, int), str is the corresponding grad name, int is the block index</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>Given a <code class="docutils literal"><span class="pre">block</span></code>, this function traverses all the <code class="docutils literal"><span class="pre">grad_op</span></code>s in it(The argument <code class="docutils literal"><span class="pre">start_op_idx</span></code> indicates where the grad_op sequence starts.) and creates all the uncreated outputs. The <em>pseudo-code</em> shows this process:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>for op in block.ops[start_op_idx : ]:
if op has an attribute named &#39;sub_block&#39;:
Get the sub-block(`s_block`) from op&#39;s attribute.
Invoke _append_backward_vars_(), with `block=s_block`
for var_name in op.all_output_names():
if block.has_var_recursive(var_name) or var_name is the name of empty variable:
continue
create a new variable named &#39;var_name&#39; in block
if grad_to_var.has_key(var_name):
set grad_info_map[grad_to_var[var_name]] as a tuple of (var_name. block)
do op&#39;s var type inference
do op&#39;s shape inference
</pre></div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2016, PaddlePaddle developers.
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT:'../',
VERSION:'',
COLLAPSE_INDEX:false,
FILE_SUFFIX:'.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: ".txt",
};
</script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/perfect-scrollbar/0.6.14/js/perfect-scrollbar.jquery.min.js"></script>
<script src="../_static/js/paddle_doc_init.js"></script>
</body>
</html>
\ No newline at end of file
因为 它太大了无法显示 source diff 。你可以改为 查看blob
# Backward Building
## Motivation
In Neural Network, most models are solved by the backpropagation algorithm(known as **BP**) at present. Technically, BP calculates the gradient of the loss function, then propagates it back through the networks following the chain rule. However, when configuring the model structure, users do not need to define the backward part. So a mechanism is required by the framework which can complete the model's backward part automatically according to the given forward part.
When implementing a specific `op`, the developer is also asked to implement its backward version, called `grad_op`. A `grad_op` takes gradients of its corresponding `op`'s outputs, and calculate gradients of the `op`'s inputs. During the building of a model's backward part, the framework creates each forward `op`'s `grad_op`, and then string them together in reverse order of forwarding part. In this way, gradients spread from the end to the beginning of the model, in another word, from the loss to parameters.
## Challenges
The motivation of backward building is apparent. However, implementation it correctly is not so easy. In the **Fluid** design, a deep learning model is described by `Program`, `Block`, `Op` and `Variable`. The `Block` itself can be nested. It means that the `op`s and `variable`s are scattered across different blocks rather than all be gathered in a single graph. Our backward building algorithm shall visit blocks in recursive order and be able to insert `grad_op`s and new created `variable`s into the right place.
## Usage
Although the whole algorithm is comprised of many functions, only one is exposed as API:
```python
def append_backward(loss, parameter_list=None, no_grad_set=None):
"""
Append backward part to main_program
Args:
loss(Variable): The variable generated by the cost function.
parameter_list(list): Parameters that need to be updated by optimizers.
If None, it means all parameters need to be updated.
no_grad_set(set): Variables that have no gradients in Block 0.
If None, the set will be generated inside the function and
contains all variables with `step_gradient=True` from all blocks.
Return:
(list[Variable]): list of (parameters, gradients) pair.
"""
```
By invoking this API, the framework appends backward part of the program where the `loss` is. It takes three arguments. `loss` means the final loss value. It must be a scalar and is usually the output of the loss layer. It is also where the gradient generated and backpropagation starts. `parameter_list` marks all parameters needs updating. If it's `None`, all parameter will be updated by optimizers. `no_grad_set` marks variables without gradient. if all outputs of some `grad_op` are in `no_grad_set`, the `grad_op` will not be run.
This API will be invoked automatically before optimizer building.
As a result, in most cases, users do not need to invoke the API by themselves to append backward part.
## Implementation
The implementation of backward building algorithm is in `backward.py` file. The whole algorithm can be divided into two independent parts: creating `grad_op`s and creating new variables.
### Creating `grad_op`s
The creating of `grad_op`s is implemented by:
```python
def _append_backward_ops_(target,
block,
target_block,
no_grad_dict,
grad_to_var):
"""
Create all grad ops, and insert them into given block
Args:
target(Variable): the target variable of forward pass
block(Block): the block where forward ops are
target_block(Block): the block which is going to hold new generated grad ops
no_grad_dict(dict):
key(int) block index
val(set) a set of varibale names. These varibales have no gradient
grad_to_var(dict)(output argument):
key(str): grad variable name
val(str): corresponding forward variable name
"""
```
Given a `block`, the function will traverses all `op`s in this block in reverse order, gets corresponding `grad_op` from the C++ core via `core.get_grad_op_desc()`, then append it to `target_block`.
However, some specific `op`(e.g. `while_op`, `if_else_op`) can hold its own sub-block. For these sub-blocks contains `op`s as well, the `grad_op` creating should be recursive.
During the reverse traversal, we check each `op` whether it has an attribute named `sub_block`. If so, it means there is a sub-block and we need to deal with it first. After creating a new block whose father is the one in `op`'s attribute, we invoke `_append_backward_ops_()` recursively, assigning the new block to parameter `target_block` and the one in `op`'s attribute to `block`. The *pseudo-code* shows this process:
```
******* pseudo-code ********
for op in reversed(block.ops):
if op has an attribute named 'sub_block':
Get the sub-block(`s_block`) from op's attribute.
Create a new block(`grad_s_block`), whose father is `s_block`.
Invoke _append_backward_ops_(), with `block=s_block` and `target_block=grad_s_block`
Invoke `core.get_grad_op_desc()` to get op's grad_op.
Insert name correspondings between variables and their gradients of the grad_op to grad_to_var
Assign grad_s_block to grad_op as it's 'sub_block' attribute.
Append grad_op to current target_block.
```
The first invoking of `_append_backward_ops_()` is initiated by `append_backward()`, in which parameters `block` and `target_block` are all assigned with root block(the block with index 0).
### Corner Cases of `grad_op` Creating
In the previous section, we show the regular process of `grad_op` creating. However, in some corner cases, the conventional algorithm is not enough to get the correct result and appending handling is required. These additional processes run after the algorithm mentioned above and do some special adjusts on its output `grad_op`s.
#### Shared Variables
If a variable is read by more than one `op` in the forward pass, its gradient is likely to be written by more than one `grad_op`s in the next backward pass. To make the gradient result being the sum of all `grad_op`s' outputs instead of the last running one, we assign each output with a temporary variable and then add a `sum_op` to add them up.
For the debug convenience, if the final gradient name is `w@GRAD`, it's corresponding temporary variables will be named as `w@GRAD@RENAME@0`, `w@GRAD@RENAME@1`...
See function `_addup_repetitive_outputs_` in `backward.py` for implementation details.
#### No Gradient Variables
In our framework, variables can be marked as *no_gradient*, it means that the gradient of this variable is unnecessary and can be considered as zero in model training. Apparently, when all the outputs of some `grad_op` are marked as *no_gradient*, the `grad_op` itself can be skipped in backward pass.
But these unnecessary gradients still need to be creating and initialized by something, otherwise following `grad_op`s who take these gradients as inputs take the risk of using uninitialized memory. In our code, we employ `fill_zeros_like_op` to initialize them as all zeros.
This features are implemented in function `_remove_no_grad_branch_`. It checks new created `grad_op`s one-by-one, removes whose outputs are all in `no_grad_set` or inserts `fill_zeros_like_op` when its necessary. We can get the `no_grad_set` from the `_append_backward_ops_` argument `no_grad_dict` or generate it on the fly by scanning all variables' `no_gradient` attribute(True or False).
### Creating Backward Variables
Up to now, we have completed all creating and adjusting jobs of `grad_op`s. However, backward variables have not been created. Now they are only represented by `grad_op`'s input and output arguments. The backward variable creating job will be done by:
```python
def _append_backward_vars_(block,
start_op_idx,
grad_to_var,
grad_info_map):
"""
Create new variables required by backward pass.
Args:
block(Block): the block where new variables will be created
start_op_idx(int): Only variables required by ops in block.ops[start_op_idx : ] will be created
grad_to_var(dict):
key(str): grad variable name
val(str): corresponding forward variable name
In most cases, this dict is generated by _append_backward_ops_()
grad_info_map(dict)(output argument):
key(str): forward variable name
val(tuple): a tuple of (str, int), str is the corresponding grad name, int is the block index
"""
```
Given a `block`, this function traverses all the `grad_op`s in it(The argument `start_op_idx` indicates where the grad_op sequence starts.) and creates all the uncreated outputs. The *pseudo-code* shows this process:
```
for op in block.ops[start_op_idx : ]:
if op has an attribute named 'sub_block':
Get the sub-block(`s_block`) from op's attribute.
Invoke _append_backward_vars_(), with `block=s_block`
for var_name in op.all_output_names():
if block.has_var_recursive(var_name) or var_name is the name of empty variable:
continue
create a new variable named 'var_name' in block
if grad_to_var.has_key(var_name):
set grad_info_map[grad_to_var[var_name]] as a tuple of (var_name. block)
do op's var type inference
do op's shape inference
```
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Backward Building &mdash; PaddlePaddle 文档</title>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="index" title="索引"
href="../genindex.html"/>
<link rel="search" title="搜索" href="../search.html"/>
<link rel="top" title="PaddlePaddle 文档" href="../index.html"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/perfect-scrollbar/0.6.14/css/perfect-scrollbar.min.css" type="text/css" />
<link rel="stylesheet" href="../_static/css/override.css" type="text/css" />
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?b9a314ab40d04d805655aab1deee08ba";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<script src="../_static/js/modernizr.min.js"></script>
</head>
<body class="wy-body-for-nav" role="document">
<header class="site-header">
<div class="site-logo">
<a href="/"><img src="../_static/images/PP_w.png"></a>
</div>
<div class="site-nav-links">
<div class="site-menu">
<a class="fork-on-github" href="https://github.com/PaddlePaddle/Paddle" target="_blank"><i class="fa fa-github"></i>Fork me on Github</a>
<div class="language-switcher dropdown">
<a type="button" data-toggle="dropdown">
<span>English</span>
<i class="fa fa-angle-up"></i>
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu">
<li><a href="/doc_cn">中文</a></li>
<li><a href="/doc">English</a></li>
</ul>
</div>
<ul class="site-page-links">
<li><a href="/">Home</a></li>
</ul>
</div>
<div class="doc-module">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../getstarted/index_cn.html">新手入门</a></li>
<li class="toctree-l1"><a class="reference internal" href="../howto/index_cn.html">进阶指南</a></li>
<li class="toctree-l1"><a class="reference internal" href="../api/index_cn.html">API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../faq/index_cn.html">FAQ</a></li>
<li class="toctree-l1"><a class="reference internal" href="../mobile/index_cn.html">MOBILE</a></li>
</ul>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
</div>
</header>
<div class="main-content-wrap">
<nav class="doc-menu-vertical" role="navigation">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../getstarted/index_cn.html">新手入门</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../getstarted/build_and_install/index_cn.html">安装与编译</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../getstarted/build_and_install/pip_install_cn.html">使用pip安装</a></li>
<li class="toctree-l3"><a class="reference internal" href="../getstarted/build_and_install/docker_install_cn.html">使用Docker安装运行</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/dev/build_cn.html">用Docker编译和测试PaddlePaddle</a></li>
<li class="toctree-l3"><a class="reference internal" href="../getstarted/build_and_install/build_from_source_cn.html">从源码编译</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../getstarted/concepts/use_concepts_cn.html">基本使用概念</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../howto/index_cn.html">进阶指南</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../howto/usage/cmd_parameter/index_cn.html">设置命令行参数</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cmd_parameter/use_case_cn.html">使用案例</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cmd_parameter/arguments_cn.html">参数概述</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cmd_parameter/detail_introduction_cn.html">细节描述</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../howto/usage/cluster/cluster_train_cn.html">分布式训练</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/fabric_cn.html">fabric集群</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/openmpi_cn.html">openmpi集群</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/k8s_cn.html">kubernetes单机</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/k8s_distributed_cn.html">kubernetes distributed分布式</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/usage/cluster/k8s_aws_cn.html">AWS上运行kubernetes集群训练</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../howto/dev/contribute_to_paddle_cn.html">如何贡献代码</a></li>
<li class="toctree-l2"><a class="reference internal" href="../howto/dev/write_docs_cn.html">如何贡献/修改文档</a></li>
<li class="toctree-l2"><a class="reference internal" href="../howto/deep_model/rnn/index_cn.html">RNN相关模型</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../howto/deep_model/rnn/rnn_config_cn.html">RNN配置</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/deep_model/rnn/recurrent_group_cn.html">Recurrent Group教程</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/deep_model/rnn/hierarchical_layer_cn.html">支持双层序列作为输入的Layer</a></li>
<li class="toctree-l3"><a class="reference internal" href="../howto/deep_model/rnn/hrnn_rnn_api_compare_cn.html">单双层RNN API对比介绍</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../howto/optimization/gpu_profiling_cn.html">GPU性能分析与调优</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../api/index_cn.html">API</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../api/v2/model_configs.html">模型配置</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/activation.html">Activation</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/layer.html">Layers</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/evaluators.html">Evaluators</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/optimizer.html">Optimizer</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/pooling.html">Pooling</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/networks.html">Networks</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/config/attr.html">Parameter Attribute</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../api/v2/data.html">数据访问</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/data/data_reader.html">Data Reader Interface</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/data/image.html">Image Interface</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/data/dataset.html">Dataset</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../api/v2/run_logic.html">训练与应用</a></li>
<li class="toctree-l2"><a class="reference internal" href="../api/v2/fluid.html">Fluid</a><ul>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/layers.html">Layers</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/data_feeder.html">DataFeeder</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/executor.html">Executor</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/initializer.html">Initializer</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/evaluator.html">Evaluator</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/nets.html">Nets</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/optimizer.html">Optimizer</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/param_attr.html">ParamAttr</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/profiler.html">Profiler</a></li>
<li class="toctree-l3"><a class="reference internal" href="../api/v2/fluid/regularizer.html">Regularizer</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../faq/index_cn.html">FAQ</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../faq/build_and_install/index_cn.html">编译安装与单元测试</a></li>
<li class="toctree-l2"><a class="reference internal" href="../faq/model/index_cn.html">模型配置</a></li>
<li class="toctree-l2"><a class="reference internal" href="../faq/parameter/index_cn.html">参数设置</a></li>
<li class="toctree-l2"><a class="reference internal" href="../faq/local/index_cn.html">本地训练与预测</a></li>
<li class="toctree-l2"><a class="reference internal" href="../faq/cluster/index_cn.html">集群训练与预测</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../mobile/index_cn.html">MOBILE</a><ul>
<li class="toctree-l2"><a class="reference internal" href="../mobile/cross_compiling_for_android_cn.html">Android平台编译指南</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mobile/cross_compiling_for_ios_cn.html">iOS平台编译指南</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mobile/cross_compiling_for_raspberry_cn.html">Raspberry Pi平台编译指南</a></li>
</ul>
</li>
</ul>
</nav>
<section class="doc-content-wrap">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li>Backward Building</li>
</ul>
</div>
<div class="wy-nav-content" id="doc-content">
<div class="rst-content">
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="backward-building">
<span id="backward-building"></span><h1>Backward Building<a class="headerlink" href="#backward-building" title="永久链接至标题"></a></h1>
<div class="section" id="motivation">
<span id="motivation"></span><h2>Motivation<a class="headerlink" href="#motivation" title="永久链接至标题"></a></h2>
<p>In Neural Network, most models are solved by the backpropagation algorithm(known as <strong>BP</strong>) at present. Technically, BP calculates the gradient of the loss function, then propagates it back through the networks following the chain rule. However, when configuring the model structure, users do not need to define the backward part. So a mechanism is required by the framework which can complete the model&#8217;s backward part automatically according to the given forward part.</p>
<p>When implementing a specific <code class="docutils literal"><span class="pre">op</span></code>, the developer is also asked to implement its backward version, called <code class="docutils literal"><span class="pre">grad_op</span></code>. A <code class="docutils literal"><span class="pre">grad_op</span></code> takes gradients of its corresponding <code class="docutils literal"><span class="pre">op</span></code>&#8216;s outputs, and calculate gradients of the <code class="docutils literal"><span class="pre">op</span></code>&#8216;s inputs. During the building of a model&#8217;s backward part, the framework creates each forward <code class="docutils literal"><span class="pre">op</span></code>&#8216;s <code class="docutils literal"><span class="pre">grad_op</span></code>, and then string them together in reverse order of forwarding part. In this way, gradients spread from the end to the beginning of the model, in another word, from the loss to parameters.</p>
</div>
<div class="section" id="challenges">
<span id="challenges"></span><h2>Challenges<a class="headerlink" href="#challenges" title="永久链接至标题"></a></h2>
<p>The motivation of backward building is apparent. However, implementation it correctly is not so easy. In the <strong>Fluid</strong> design, a deep learning model is described by <code class="docutils literal"><span class="pre">Program</span></code>, <code class="docutils literal"><span class="pre">Block</span></code>, <code class="docutils literal"><span class="pre">Op</span></code> and <code class="docutils literal"><span class="pre">Variable</span></code>. The <code class="docutils literal"><span class="pre">Block</span></code> itself can be nested. It means that the <code class="docutils literal"><span class="pre">op</span></code>s and <code class="docutils literal"><span class="pre">variable</span></code>s are scattered across different blocks rather than all be gathered in a single graph. Our backward building algorithm shall visit blocks in recursive order and be able to insert <code class="docutils literal"><span class="pre">grad_op</span></code>s and new created <code class="docutils literal"><span class="pre">variable</span></code>s into the right place.</p>
</div>
<div class="section" id="usage">
<span id="usage"></span><h2>Usage<a class="headerlink" href="#usage" title="永久链接至标题"></a></h2>
<p>Although the whole algorithm is comprised of many functions, only one is exposed as API:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">append_backward</span><span class="p">(</span><span class="n">loss</span><span class="p">,</span> <span class="n">parameter_list</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">no_grad_set</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Append backward part to main_program</span>
<span class="sd"> Args:</span>
<span class="sd"> loss(Variable): The variable generated by the cost function.</span>
<span class="sd"> parameter_list(list): Parameters that need to be updated by optimizers.</span>
<span class="sd"> If None, it means all parameters need to be updated.</span>
<span class="sd"> no_grad_set(set): Variables that have no gradients in Block 0. </span>
<span class="sd"> If None, the set will be generated inside the function and </span>
<span class="sd"> contains all variables with `step_gradient=True` from all blocks.</span>
<span class="sd"> </span>
<span class="sd"> Return:</span>
<span class="sd"> (list[Variable]): list of (parameters, gradients) pair.</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>By invoking this API, the framework appends backward part of the program where the <code class="docutils literal"><span class="pre">loss</span></code> is. It takes three arguments. <code class="docutils literal"><span class="pre">loss</span></code> means the final loss value. It must be a scalar and is usually the output of the loss layer. It is also where the gradient generated and backpropagation starts. <code class="docutils literal"><span class="pre">parameter_list</span></code> marks all parameters needs updating. If it&#8217;s <code class="docutils literal"><span class="pre">None</span></code>, all parameter will be updated by optimizers. <code class="docutils literal"><span class="pre">no_grad_set</span></code> marks variables without gradient. if all outputs of some <code class="docutils literal"><span class="pre">grad_op</span></code> are in <code class="docutils literal"><span class="pre">no_grad_set</span></code>, the <code class="docutils literal"><span class="pre">grad_op</span></code> will not be run.</p>
<p>This API will be invoked automatically before optimizer building.
As a result, in most cases, users do not need to invoke the API by themselves to append backward part.</p>
</div>
<div class="section" id="implementation">
<span id="implementation"></span><h2>Implementation<a class="headerlink" href="#implementation" title="永久链接至标题"></a></h2>
<p>The implementation of backward building algorithm is in <code class="docutils literal"><span class="pre">backward.py</span></code> file. The whole algorithm can be divided into two independent parts: creating <code class="docutils literal"><span class="pre">grad_op</span></code>s and creating new variables.</p>
<div class="section" id="creating-grad-ops">
<span id="creating-grad-ops"></span><h3>Creating <code class="docutils literal"><span class="pre">grad_op</span></code>s<a class="headerlink" href="#creating-grad-ops" title="永久链接至标题"></a></h3>
<p>The creating of <code class="docutils literal"><span class="pre">grad_op</span></code>s is implemented by:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">_append_backward_ops_</span><span class="p">(</span><span class="n">target</span><span class="p">,</span>
<span class="n">block</span><span class="p">,</span>
<span class="n">target_block</span><span class="p">,</span>
<span class="n">no_grad_dict</span><span class="p">,</span>
<span class="n">grad_to_var</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Create all grad ops, and insert them into given block</span>
<span class="sd"> Args:</span>
<span class="sd"> target(Variable): the target variable of forward pass</span>
<span class="sd"> block(Block): the block where forward ops are</span>
<span class="sd"> target_block(Block): the block which is going to hold new generated grad ops</span>
<span class="sd"> no_grad_dict(dict): </span>
<span class="sd"> key(int) block index</span>
<span class="sd"> val(set) a set of varibale names. These varibales have no gradient</span>
<span class="sd"> grad_to_var(dict)(output argument):</span>
<span class="sd"> key(str): grad variable name</span>
<span class="sd"> val(str): corresponding forward variable name</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>Given a <code class="docutils literal"><span class="pre">block</span></code>, the function will traverses all <code class="docutils literal"><span class="pre">op</span></code>s in this block in reverse order, gets corresponding <code class="docutils literal"><span class="pre">grad_op</span></code> from the C++ core via <code class="docutils literal"><span class="pre">core.get_grad_op_desc()</span></code>, then append it to <code class="docutils literal"><span class="pre">target_block</span></code>.</p>
<p>However, some specific <code class="docutils literal"><span class="pre">op</span></code>(e.g. <code class="docutils literal"><span class="pre">while_op</span></code>, <code class="docutils literal"><span class="pre">if_else_op</span></code>) can hold its own sub-block. For these sub-blocks contains <code class="docutils literal"><span class="pre">op</span></code>s as well, the <code class="docutils literal"><span class="pre">grad_op</span></code> creating should be recursive.</p>
<p>During the reverse traversal, we check each <code class="docutils literal"><span class="pre">op</span></code> whether it has an attribute named <code class="docutils literal"><span class="pre">sub_block</span></code>. If so, it means there is a sub-block and we need to deal with it first. After creating a new block whose father is the one in <code class="docutils literal"><span class="pre">op</span></code>&#8216;s attribute, we invoke <code class="docutils literal"><span class="pre">_append_backward_ops_()</span></code> recursively, assigning the new block to parameter <code class="docutils literal"><span class="pre">target_block</span></code> and the one in <code class="docutils literal"><span class="pre">op</span></code>&#8216;s attribute to <code class="docutils literal"><span class="pre">block</span></code>. The <em>pseudo-code</em> shows this process:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>******* pseudo-code ********
for op in reversed(block.ops):
if op has an attribute named &#39;sub_block&#39;:
Get the sub-block(`s_block`) from op&#39;s attribute.
Create a new block(`grad_s_block`), whose father is `s_block`.
Invoke _append_backward_ops_(), with `block=s_block` and `target_block=grad_s_block`
Invoke `core.get_grad_op_desc()` to get op&#39;s grad_op.
Insert name correspondings between variables and their gradients of the grad_op to grad_to_var
Assign grad_s_block to grad_op as it&#39;s &#39;sub_block&#39; attribute.
Append grad_op to current target_block.
</pre></div>
</div>
<p>The first invoking of <code class="docutils literal"><span class="pre">_append_backward_ops_()</span></code> is initiated by <code class="docutils literal"><span class="pre">append_backward()</span></code>, in which parameters <code class="docutils literal"><span class="pre">block</span></code> and <code class="docutils literal"><span class="pre">target_block</span></code> are all assigned with root block(the block with index 0).</p>
</div>
<div class="section" id="corner-cases-of-grad-op-creating">
<span id="corner-cases-of-grad-op-creating"></span><h3>Corner Cases of <code class="docutils literal"><span class="pre">grad_op</span></code> Creating<a class="headerlink" href="#corner-cases-of-grad-op-creating" title="永久链接至标题"></a></h3>
<p>In the previous section, we show the regular process of <code class="docutils literal"><span class="pre">grad_op</span></code> creating. However, in some corner cases, the conventional algorithm is not enough to get the correct result and appending handling is required. These additional processes run after the algorithm mentioned above and do some special adjusts on its output <code class="docutils literal"><span class="pre">grad_op</span></code>s.</p>
<div class="section" id="shared-variables">
<span id="shared-variables"></span><h4>Shared Variables<a class="headerlink" href="#shared-variables" title="永久链接至标题"></a></h4>
<p>If a variable is read by more than one <code class="docutils literal"><span class="pre">op</span></code> in the forward pass, its gradient is likely to be written by more than one <code class="docutils literal"><span class="pre">grad_op</span></code>s in the next backward pass. To make the gradient result being the sum of all <code class="docutils literal"><span class="pre">grad_op</span></code>s&#8217; outputs instead of the last running one, we assign each output with a temporary variable and then add a <code class="docutils literal"><span class="pre">sum_op</span></code> to add them up.</p>
<p>For the debug convenience, if the final gradient name is <code class="docutils literal"><span class="pre">w&#64;GRAD</span></code>, it&#8217;s corresponding temporary variables will be named as <code class="docutils literal"><span class="pre">w&#64;GRAD&#64;RENAME&#64;0</span></code>, <code class="docutils literal"><span class="pre">w&#64;GRAD&#64;RENAME&#64;1</span></code>...</p>
<p>See function <code class="docutils literal"><span class="pre">_addup_repetitive_outputs_</span></code> in <code class="docutils literal"><span class="pre">backward.py</span></code> for implementation details.</p>
</div>
<div class="section" id="no-gradient-variables">
<span id="no-gradient-variables"></span><h4>No Gradient Variables<a class="headerlink" href="#no-gradient-variables" title="永久链接至标题"></a></h4>
<p>In our framework, variables can be marked as <em>no_gradient</em>, it means that the gradient of this variable is unnecessary and can be considered as zero in model training. Apparently, when all the outputs of some <code class="docutils literal"><span class="pre">grad_op</span></code> are marked as <em>no_gradient</em>, the <code class="docutils literal"><span class="pre">grad_op</span></code> itself can be skipped in backward pass.</p>
<p>But these unnecessary gradients still need to be creating and initialized by something, otherwise following <code class="docutils literal"><span class="pre">grad_op</span></code>s who take these gradients as inputs take the risk of using uninitialized memory. In our code, we employ <code class="docutils literal"><span class="pre">fill_zeros_like_op</span></code> to initialize them as all zeros.</p>
<p>This features are implemented in function <code class="docutils literal"><span class="pre">_remove_no_grad_branch_</span></code>. It checks new created <code class="docutils literal"><span class="pre">grad_op</span></code>s one-by-one, removes whose outputs are all in <code class="docutils literal"><span class="pre">no_grad_set</span></code> or inserts <code class="docutils literal"><span class="pre">fill_zeros_like_op</span></code> when its necessary. We can get the <code class="docutils literal"><span class="pre">no_grad_set</span></code> from the <code class="docutils literal"><span class="pre">_append_backward_ops_</span></code> argument <code class="docutils literal"><span class="pre">no_grad_dict</span></code> or generate it on the fly by scanning all variables&#8217; <code class="docutils literal"><span class="pre">no_gradient</span></code> attribute(True or False).</p>
</div>
</div>
<div class="section" id="creating-backward-variables">
<span id="creating-backward-variables"></span><h3>Creating Backward Variables<a class="headerlink" href="#creating-backward-variables" title="永久链接至标题"></a></h3>
<p>Up to now, we have completed all creating and adjusting jobs of <code class="docutils literal"><span class="pre">grad_op</span></code>s. However, backward variables have not been created. Now they are only represented by <code class="docutils literal"><span class="pre">grad_op</span></code>&#8216;s input and output arguments. The backward variable creating job will be done by:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">_append_backward_vars_</span><span class="p">(</span><span class="n">block</span><span class="p">,</span>
<span class="n">start_op_idx</span><span class="p">,</span>
<span class="n">grad_to_var</span><span class="p">,</span>
<span class="n">grad_info_map</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Create new variables required by backward pass.</span>
<span class="sd"> Args:</span>
<span class="sd"> block(Block): the block where new variables will be created</span>
<span class="sd"> start_op_idx(int): Only variables required by ops in block.ops[start_op_idx : ] will be created</span>
<span class="sd"> grad_to_var(dict):</span>
<span class="sd"> key(str): grad variable name</span>
<span class="sd"> val(str): corresponding forward variable name</span>
<span class="sd"> In most cases, this dict is generated by _append_backward_ops_()</span>
<span class="sd"> grad_info_map(dict)(output argument):</span>
<span class="sd"> key(str): forward variable name</span>
<span class="sd"> val(tuple): a tuple of (str, int), str is the corresponding grad name, int is the block index</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>Given a <code class="docutils literal"><span class="pre">block</span></code>, this function traverses all the <code class="docutils literal"><span class="pre">grad_op</span></code>s in it(The argument <code class="docutils literal"><span class="pre">start_op_idx</span></code> indicates where the grad_op sequence starts.) and creates all the uncreated outputs. The <em>pseudo-code</em> shows this process:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>for op in block.ops[start_op_idx : ]:
if op has an attribute named &#39;sub_block&#39;:
Get the sub-block(`s_block`) from op&#39;s attribute.
Invoke _append_backward_vars_(), with `block=s_block`
for var_name in op.all_output_names():
if block.has_var_recursive(var_name) or var_name is the name of empty variable:
continue
create a new variable named &#39;var_name&#39; in block
if grad_to_var.has_key(var_name):
set grad_info_map[grad_to_var[var_name]] as a tuple of (var_name. block)
do op&#39;s var type inference
do op&#39;s shape inference
</pre></div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2016, PaddlePaddle developers.
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT:'../',
VERSION:'',
COLLAPSE_INDEX:false,
FILE_SUFFIX:'.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: ".txt",
};
</script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="../_static/translations.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/mathjax/2.7.0/MathJax.js"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/perfect-scrollbar/0.6.14/js/perfect-scrollbar.jquery.min.js"></script>
<script src="../_static/js/paddle_doc_init.js"></script>
</body>
</html>
\ No newline at end of file
因为 它太大了无法显示 source diff 。你可以改为 查看blob
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册