parameters: (loss,
                      parameter_list=None,
                      no_grad_set=None,
                      callbacks=None,
                      checkpoints=None,
                      distop_context=None)
docstring: :api_attr: Static Graph
  
      This function appends backward part to main_program.
  
      A complete neural network training is made up of forward and backward
      propagation. However, when we configure a network, we only need to
      specify its forward part. This function uses the chain rule to automatically
      generate the backward part according to the forward part.
  
      In most cases, users do not need to invoke this function manually.
      It will be automatically invoked by the optimizer's `minimize` function.
  
      Parameters:
          loss(Tensor): The loss Tensor of the network.
          parameter_list(list[Tensor|str]|tuple[Tensor|str], optional): List/Tuple of Parameters or Parameter.names
                                             that need to be updated by optimizers.
                                             If it is None, all parameters
                                             will be updated.
                                             Default: None.
          no_grad_set(set[Tensor|str], optional): Set of Tensors or Tensor.names in the :ref:`api_guide_Block_en` 0 whose gradients
                                 should be ignored. All Tensors with
                                 `stop_gradient=True` from all blocks will
                                 be automatically added into this set.
                                 If this parameter is not None, the Tensors or Tensor.names in this set will be added to the default set.
                                 Default: None.
          callbacks(list[callable object]|tuple[callable object], optional): List/Tuple of callback functions.
                                                 The callbacks are used for
                                                 doing some custom jobs during
                                                 backward part building. All
                                                 callable objects in it will
                                                 be invoked once each time a
                                                 new gradient operator is added
                                                 into the program. The callable
                                                 object must have two input
                                                 parameters: ``block`` and ``context`` .
                                                 The ``block`` is the :ref:`api_guide_Block_en` which
                                                 the new gradient operator will
                                                 be added to. The ``context`` is a
                                                 map, whose keys are gradient
                                                 Tensor names and values are
                                                 corresponding original :ref:`api_guide_tensor_en` .
                                                 In addition to this, the ``context``
                                                 has another special key-value pair:
                                                 the key is string ``__current_op_desc__``
                                                 and the value is the op_desc of the
                                                 gradient operator who has just
                                                 triggered the callable object.
                                                 Default: None.
  
      Returns:
          list of tuple ( :ref:`api_guide_tensor_en` , :ref:`api_guide_tensor_en` ): Pairs of parameter and its corresponding gradients.
          The key is the parameter and the value is gradient Tensor.
  
      Raises:
          AssertionError: If ``loss`` is not an instance of Tensor.
  
      Examples:
          .. code-block:: python
  
              import paddle
              import paddle.nn.functional as F
  
              paddle.enable_static()
  
              x = paddle.static.data(name='x', shape=[None, 13], dtype='int64')
              y = paddle.static.data(name='y', shape=[None, 1], dtype='float32')
              x_emb = paddle.static.nn.embedding(x, size=[100, 256])
              y_predict = paddle.static.nn.fc(x=x_emb, size=1, activation=None, name='my_fc')
              loss = F.square_error_cost(input=y_predict, label=y)
              avg_loss = paddle.mean(loss)
  
              # Get all weights in main_program, not include bias.
              all_weights = [param for param in paddle.static.default_main_program().block(0).all_parameters() if 'w_' in param.name]
              all_weights_name = [w.name for w in all_weights]
  
              # return all param_grads needed to be updated if parameter_list set default None.
              p_g_list1 = paddle.static.append_backward(loss=avg_loss)
              # output: [(embedding_0.w_0, embedding_0.w_0@GRAD), (my_fc.w_0, my_fc.w_0@GRAD), (my_fc.b_0, my_fc.b_0@GRAD)]
  
              # return the param_grads corresponding to parameter_list that can be list of param (Tensor).
              p_g_list2 = paddle.static.append_backward(loss=avg_loss, parameter_list=all_weights)
              # output: [(embedding_0.w_0, embedding_0.w_0@GRAD), (my_fc.w_0, my_fc.w_0@GRAD)]
  
              # parameter_list can be list of param.name (str).
              p_g_list3 = paddle.static.append_backward(loss=avg_loss, parameter_list=all_weights_name)
              # output: [(embedding_0.w_0, embedding_0.w_0@GRAD), (my_fc.w_0, my_fc.w_0@GRAD)]
  
              # no_grad_set can be set of Tensors that means grad will be cut off from these Tensors.
              p_g_list4 = paddle.static.append_backward(loss=avg_loss, no_grad_set=set([x_emb]))
              # output: [(my_fc.w_0, my_fc.w_0@GRAD), (my_fc.b_0, my_fc.b_0@GRAD)]
  
              # no_grad_set can be set of Tensor.name when the Tensor is created inside layers and can't be specified explicitly.
              p_g_list5 = paddle.static.append_backward(loss=avg_loss, no_grad_set=set(['my_fc.b_0']))
              # output: [(embedding_0.w_0, embedding_0.w_0@GRAD), (my_fc.w_0, my_fc.w_0@GRAD)]
  
              # return [] because all param_grads are filtered by no_grad_set.
              p_g_list6 = paddle.static.append_backward(loss=avg_loss, parameter_list=all_weights, no_grad_set=set(all_weights))
docstring_summary: :api_attr: Static Graph
docstring_tokens: : api_attr : Static Graph
function: def append_backward(loss,
                      parameter_list=None,
                      no_grad_set=None,
                      callbacks=None,
                      checkpoints=None,
                      distop_context=None):
      """
      :api_attr: Static Graph
  
      This function appends backward part to main_program.
  
      A complete neural network training is made up of forward and backward
      propagation. However, when we configure a network, we only need to
      specify its forward part. This function uses the chain rule to automatically
      generate the backward part according to the forward part.
  
      In most cases, users do not need to invoke this function manually.
      It will be automatically invoked by the optimizer's `minimize` function.
  
      Parameters:
          loss(Tensor): The loss Tensor of the network.
          parameter_list(list[Tensor|str]|tuple[Tensor|str], optional): List/Tuple of Parameters or Parameter.names
                                             that need to be updated by optimizers.
                                             If it is None, all parameters
                                             will be updated.
                                             Default: None.
          no_grad_set(set[Tensor|str], optional): Set of Tensors or Tensor.names in the :ref:`api_guide_Block_en` 0 whose gradients
                                 should be ignored. All Tensors with
                                 `stop_gradient=True` from all blocks will
                                 be automatically added into this set.
                                 If this parameter is not None, the Tensors or Tensor.names in this set will be added to the default set.
                                 Default: None.
          callbacks(list[callable object]|tuple[callable object], optional): List/Tuple of callback functions.
                                                 The callbacks are used for
                                                 doing some custom jobs during
                                                 backward part building. All
                                                 callable objects in it will
                                                 be invoked once each time a
                                                 new gradient operator is added
                                                 into the program. The callable
                                                 object must have two input
                                                 parameters: ``block`` and ``context`` .
                                                 The ``block`` is the :ref:`api_guide_Block_en` which
                                                 the new gradient operator will
                                                 be added to. The ``context`` is a
                                                 map, whose keys are gradient
                                                 Tensor names and values are
                                                 corresponding original :ref:`api_guide_tensor_en` .
                                                 In addition to this, the ``context``
                                                 has another special key-value pair:
                                                 the key is string ``__current_op_desc__``
                                                 and the value is the op_desc of the
                                                 gradient operator who has just
                                                 triggered the callable object.
                                                 Default: None.
  
      Returns:
          list of tuple ( :ref:`api_guide_tensor_en` , :ref:`api_guide_tensor_en` ): Pairs of parameter and its corresponding gradients.
          The key is the parameter and the value is gradient Tensor.
  
      Raises:
          AssertionError: If ``loss`` is not an instance of Tensor.
  
      Examples:
          .. code-block:: python
  
              import paddle
              import paddle.nn.functional as F
  
              paddle.enable_static()
  
              x = paddle.static.data(name='x', shape=[None, 13], dtype='int64')
              y = paddle.static.data(name='y', shape=[None, 1], dtype='float32')
              x_emb = paddle.static.nn.embedding(x, size=[100, 256])
              y_predict = paddle.static.nn.fc(x=x_emb, size=1, activation=None, name='my_fc')
              loss = F.square_error_cost(input=y_predict, label=y)
              avg_loss = paddle.mean(loss)
  
              # Get all weights in main_program, not include bias.
              all_weights = [param for param in paddle.static.default_main_program().block(0).all_parameters() if 'w_' in param.name]
              all_weights_name = [w.name for w in all_weights]
  
              # return all param_grads needed to be updated if parameter_list set default None.
              p_g_list1 = paddle.static.append_backward(loss=avg_loss)
              # output: [(embedding_0.w_0, embedding_0.w_0@GRAD), (my_fc.w_0, my_fc.w_0@GRAD), (my_fc.b_0, my_fc.b_0@GRAD)]
  
              # return the param_grads corresponding to parameter_list that can be list of param (Tensor).
              p_g_list2 = paddle.static.append_backward(loss=avg_loss, parameter_list=all_weights)
              # output: [(embedding_0.w_0, embedding_0.w_0@GRAD), (my_fc.w_0, my_fc.w_0@GRAD)]
  
              # parameter_list can be list of param.name (str).
              p_g_list3 = paddle.static.append_backward(loss=avg_loss, parameter_list=all_weights_name)
              # output: [(embedding_0.w_0, embedding_0.w_0@GRAD), (my_fc.w_0, my_fc.w_0@GRAD)]
  
              # no_grad_set can be set of Tensors that means grad will be cut off from these Tensors.
              p_g_list4 = paddle.static.append_backward(loss=avg_loss, no_grad_set=set([x_emb]))
              # output: [(my_fc.w_0, my_fc.w_0@GRAD), (my_fc.b_0, my_fc.b_0@GRAD)]
  
              # no_grad_set can be set of Tensor.name when the Tensor is created inside layers and can't be specified explicitly.
              p_g_list5 = paddle.static.append_backward(loss=avg_loss, no_grad_set=set(['my_fc.b_0']))
              # output: [(embedding_0.w_0, embedding_0.w_0@GRAD), (my_fc.w_0, my_fc.w_0@GRAD)]
  
              # return [] because all param_grads are filtered by no_grad_set.
              p_g_list6 = paddle.static.append_backward(loss=avg_loss, parameter_list=all_weights, no_grad_set=set(all_weights))
  
      """
      check_type(loss, 'loss', framework.Variable,
                 'paddle.static.append_backward')
  
      if loss.op is None:
          # the loss is from a cloned program. Find loss op manually.
          _find_loss_op_(loss)
  
      loss.op._set_attr(core.op_proto_and_checker_maker.kOpRoleAttrName(),
                        int(core.op_proto_and_checker_maker.OpRole.Forward) |
                        int(core.op_proto_and_checker_maker.OpRole.Loss))
  
      if callbacks is not None:
          check_type(callbacks, 'callbacks', (list, tuple),
                     'paddle.static.append_backward')
  
      program = loss.block.program
      root_block = program.block(0)
      current_block_idx = program.current_block_idx
      current_block = program.block(current_block_idx)
  
      is_in_control_flow = current_block_idx != 0
  
      # Double grad is not supported in sub-block (control flow)
      if not is_in_control_flow:
          # _appending_grad_times used for double grad
          program._appending_grad_times += 1
  
      if no_grad_set is None:
          no_grad_set = set()
      else:
          no_grad_set = _get_no_grad_set_name(copy.copy(no_grad_set))
      no_grad_dict = _get_stop_gradients_(program)
      # no_grad_set only contains vars in block 0
      # Todo(liym27): support vars in sub block
      no_grad_dict[0].update(list(map(_append_grad_suffix_, no_grad_set)))
  
      # Currently it is only to support the optimizer.minimize
      # in a switch branch, which can append_backward in a sub_block.
      # Note: while_loop is in control flow, but it makes no sense to call optimizer in while.
      # Todo: report error when it is in while_loop
      if is_in_control_flow:
          # create grad block if in switch control flow.
          target_grad_block = program._create_block(
              parent_idx=current_block.parent_idx)
          target_grad_block._set_forward_block_idx(current_block_idx)
          # after _create_block, program.current_block changes
      else:
          target_grad_block = root_block
  
      son_parent_block_idx_dict = _get_son_parent_block_idx_dict(
          program, current_block_idx)
  
      block_fwd_op_num_dict = {}  # block_id: fwd_op_num
      for idx in son_parent_block_idx_dict:
          block_fwd_op_num_dict[idx] = program.block(idx).desc.op_size()
  
      grad_to_var = dict()
  
      op_desc = _create_loss_op_desc_(loss)
      target_grad_block.desc.append_op().copy_from(op_desc)
  
      for block_idx in son_parent_block_idx_dict:
          block = program.block(block_idx)
  
          block_no_grad_set = set(
              map(_strip_grad_suffix_, no_grad_dict[block_idx]))
  
          op_path_dict = dict()
          op_path = _find_op_path_(block, [loss], [], block_no_grad_set,
                                   op_path_dict)
  
          no_grad_vars = _find_no_grad_vars(block, op_path, [loss],
                                            block_no_grad_set)
  
          block_no_grad_set.update(no_grad_vars)
          no_grad_dict[block_idx].update(
              list(map(_append_grad_suffix_, block_no_grad_set)))
  
          input_grad_names_set = None
          # For double backward, input_grad_names is used for filtering
          # some non-used gradients op(s).
  
          # TODO(liym27): need a better design.
          # not support double grad in control flow sub-block now.
          if not is_in_control_flow:
              if program._appending_grad_times > 1:
                  input_grad_names_set = set([_append_grad_suffix_(loss.name)])
  
          # TODO: support _append_backward_ops_with_checkpoints_ in
          #  sub-block (control flow)
          is_recompute = False
          if checkpoints != None and \
                  isinstance(checkpoints, list) and \
                  len(checkpoints) > 0:
              is_recompute = True
              program_stat, checkpoint_names, \
                  vars_should_be_hold, \
                  recompute_segments = \
                  _append_backward_ops_with_checkpoints_(
                      root_block,
                      op_path,
                      root_block,
                      no_grad_dict,
                      grad_to_var,
                      checkpoints)
          else:
              _append_backward_ops_(
                  block,  # the block where forward ops are in
                  op_path,
                  target_grad_block,
                  no_grad_dict,
                  grad_to_var,
                  callbacks,
                  input_grad_names_set=input_grad_names_set,
                  op_path_dict=op_path_dict,
                  distop_context=distop_context, )
  
      grad_info_map = dict()
  
      # if in control flow, target_grad_block is a created new block which only contains grad ops,
      # so fwd_op_num is set to 0.
      fwd_op_num = block_fwd_op_num_dict[
          current_block_idx] if not is_in_control_flow else 0
  
      # Because append_backward may be called multiple times,
      # we need rename the internal gradient variables so that they have
      # different names.
      _rename_grad_(target_grad_block, fwd_op_num, grad_to_var, {})
  
      _append_backward_vars_(target_grad_block, fwd_op_num, grad_to_var,
                             grad_info_map)
  
      program.current_block_idx = current_block_idx
      program._sync_with_cpp()
  
      if parameter_list is not None:
          check_type(parameter_list, 'parameter_list', (list, tuple, set),
                     'fluid.backward.append_backward')
          parameters = []
          for i, param in enumerate(parameter_list):
              check_type(param, 'parameter_list[%s]' % i, (framework.Variable,
                                                           six.string_types),
                         'fluid.backward.append_backward')
              if isinstance(param, framework.Variable):
                  parameters.append(param.name)
              elif isinstance(param, six.string_types):
                  parameters.append(param)
      else:
          params = program.global_block().all_parameters()
          parameters = [param.name for param in params if param.trainable]
  
      params_and_grads = []
      op_role_var_attr_name = core.op_proto_and_checker_maker.kOpRoleVarAttrName()
      for param in parameters:
          if cpt.to_text(param) not in grad_info_map:
              continue
          grad_info = grad_info_map[param]
          grad_block = grad_info[1]
          if not grad_block.has_var(grad_info[0]):
              raise ValueError("grad block[{0}] did not have grad var {1}".format(
                  grad_info[1], grad_info[0]))
          # Get the param var from the global block
          param_var = program.global_block().var(param)
          grad_var = grad_block.var(grad_info[0])
          if not is_in_control_flow:
              if loss.block.has_var(grad_info[0]):
                  params_and_grads.append((param_var, grad_var))
              else:
                  params_and_grads.append((param_var, None))
          else:
              params_and_grads.append((param_var, grad_var))
  
      for p, g in params_and_grads:
          if g is None:
              continue
          ops = grad_block.ops if is_in_control_flow else program.global_block(
          ).ops
          for op in reversed(ops):
              assert isinstance(op, framework.Operator)
              if g.name in op.output_arg_names:
                  g.op = op
                  break
  
          if g.op is None:
              raise ValueError("Unexpected branch")
          attr_val = [p.name, g.name]
          if g.op.has_attr(op_role_var_attr_name):
              attr_val.extend(g.op.attr(op_role_var_attr_name))
          g.op._set_attr(op_role_var_attr_name, attr_val)
  
      if is_recompute:
          return params_and_grads, checkpoint_names
      else:
          return params_and_grads
function_tokens: def append_backward ( loss , parameter_list = None , no_grad_set = None , callbacks = None , checkpoints = None , distop_context = None ) : check_type ( loss , 'loss' , framework . Variable , 'paddle.static.append_backward' ) if loss . op is None : # the loss is from a cloned program. Find loss op manually. _find_loss_op_ ( loss ) loss . op . _set_attr ( core . op_proto_and_checker_maker . kOpRoleAttrName ( ) , int ( core . op_proto_and_checker_maker . OpRole . Forward ) | int ( core . op_proto_and_checker_maker . OpRole . Loss ) ) if callbacks is not None : check_type ( callbacks , 'callbacks' , ( list , tuple ) , 'paddle.static.append_backward' ) program = loss . block . program root_block = program . block ( 0 ) current_block_idx = program . current_block_idx current_block = program . block ( current_block_idx ) is_in_control_flow = current_block_idx != 0 # Double grad is not supported in sub-block (control flow) if not is_in_control_flow : # _appending_grad_times used for double grad program . _appending_grad_times += 1 if no_grad_set is None : no_grad_set = set ( ) else : no_grad_set = _get_no_grad_set_name ( copy . copy ( no_grad_set ) ) no_grad_dict = _get_stop_gradients_ ( program ) # no_grad_set only contains vars in block 0 # Todo(liym27): support vars in sub block no_grad_dict [ 0 ] . update ( list ( map ( _append_grad_suffix_ , no_grad_set ) ) ) # Currently it is only to support the optimizer.minimize # in a switch branch, which can append_backward in a sub_block. # Note: while_loop is in control flow, but it makes no sense to call optimizer in while. # Todo: report error when it is in while_loop if is_in_control_flow : # create grad block if in switch control flow. target_grad_block = program . _create_block ( parent_idx = current_block . parent_idx ) target_grad_block . _set_forward_block_idx ( current_block_idx ) # after _create_block, program.current_block changes else : target_grad_block = root_block son_parent_block_idx_dict = _get_son_parent_block_idx_dict ( program , current_block_idx ) block_fwd_op_num_dict = { } # block_id: fwd_op_num for idx in son_parent_block_idx_dict : block_fwd_op_num_dict [ idx ] = program . block ( idx ) . desc . op_size ( ) grad_to_var = dict ( ) op_desc = _create_loss_op_desc_ ( loss ) target_grad_block . desc . append_op ( ) . copy_from ( op_desc ) for block_idx in son_parent_block_idx_dict : block = program . block ( block_idx ) block_no_grad_set = set ( map ( _strip_grad_suffix_ , no_grad_dict [ block_idx ] ) ) op_path_dict = dict ( ) op_path = _find_op_path_ ( block , [ loss ] , [ ] , block_no_grad_set , op_path_dict ) no_grad_vars = _find_no_grad_vars ( block , op_path , [ loss ] , block_no_grad_set ) block_no_grad_set . update ( no_grad_vars ) no_grad_dict [ block_idx ] . update ( list ( map ( _append_grad_suffix_ , block_no_grad_set ) ) ) input_grad_names_set = None # For double backward, input_grad_names is used for filtering # some non-used gradients op(s). # TODO(liym27): need a better design. # not support double grad in control flow sub-block now. if not is_in_control_flow : if program . _appending_grad_times > 1 : input_grad_names_set = set ( [ _append_grad_suffix_ ( loss . name ) ] ) # TODO: support _append_backward_ops_with_checkpoints_ in #  sub-block (control flow) is_recompute = False if checkpoints != None and isinstance ( checkpoints , list ) and len ( checkpoints ) > 0 : is_recompute = True program_stat , checkpoint_names , vars_should_be_hold , recompute_segments = _append_backward_ops_with_checkpoints_ ( root_block , op_path , root_block , no_grad_dict , grad_to_var , checkpoints ) else : _append_backward_ops_ ( block , # the block where forward ops are in op_path , target_grad_block , no_grad_dict , grad_to_var , callbacks , input_grad_names_set = input_grad_names_set , op_path_dict = op_path_dict , distop_context = distop_context , ) grad_info_map = dict ( ) # if in control flow, target_grad_block is a created new block which only contains grad ops, # so fwd_op_num is set to 0. fwd_op_num = block_fwd_op_num_dict [ current_block_idx ] if not is_in_control_flow else 0 # Because append_backward may be called multiple times, # we need rename the internal gradient variables so that they have # different names. _rename_grad_ ( target_grad_block , fwd_op_num , grad_to_var , { } ) _append_backward_vars_ ( target_grad_block , fwd_op_num , grad_to_var , grad_info_map ) program . current_block_idx = current_block_idx program . _sync_with_cpp ( ) if parameter_list is not None : check_type ( parameter_list , 'parameter_list' , ( list , tuple , set ) , 'fluid.backward.append_backward' ) parameters = [ ] for i , param in enumerate ( parameter_list ) : check_type ( param , 'parameter_list[%s]' % i , ( framework . Variable , six . string_types ) , 'fluid.backward.append_backward' ) if isinstance ( param , framework . Variable ) : parameters . append ( param . name ) elif isinstance ( param , six . string_types ) : parameters . append ( param ) else : params = program . global_block ( ) . all_parameters ( ) parameters = [ param . name for param in params if param . trainable ] params_and_grads = [ ] op_role_var_attr_name = core . op_proto_and_checker_maker . kOpRoleVarAttrName ( ) for param in parameters : if cpt . to_text ( param ) not in grad_info_map : continue grad_info = grad_info_map [ param ] grad_block = grad_info [ 1 ] if not grad_block . has_var ( grad_info [ 0 ] ) : raise ValueError ( "grad block[{0}] did not have grad var {1}" . format ( grad_info [ 1 ] , grad_info [ 0 ] ) ) # Get the param var from the global block param_var = program . global_block ( ) . var ( param ) grad_var = grad_block . var ( grad_info [ 0 ] ) if not is_in_control_flow : if loss . block . has_var ( grad_info [ 0 ] ) : params_and_grads . append ( ( param_var , grad_var ) ) else : params_and_grads . append ( ( param_var , None ) ) else : params_and_grads . append ( ( param_var , grad_var ) ) for p , g in params_and_grads : if g is None : continue ops = grad_block . ops if is_in_control_flow else program . global_block ( ) . ops for op in reversed ( ops ) : assert isinstance ( op , framework . Operator ) if g . name in op . output_arg_names : g . op = op break if g . op is None : raise ValueError ( "Unexpected branch" ) attr_val = [ p . name , g . name ] if g . op . has_attr ( op_role_var_attr_name ) : attr_val . extend ( g . op . attr ( op_role_var_attr_name ) ) g . op . _set_attr ( op_role_var_attr_name , attr_val ) if is_recompute : return params_and_grads , checkpoint_names else : return params_and_grads