def build_train(make_obs_ph, q_func, num_actions, optimizer, grad_norm_clipping=None, gamma=<NUM_LIT:1.0>,<EOL>double_q=True, scope="<STR_LIT>", reuse=None, param_noise=False, param_noise_filter_func=None):
if param_noise:<EOL><INDENT>act_f = build_act_with_param_noise(make_obs_ph, q_func, num_actions, scope=scope, reuse=reuse,<EOL>param_noise_filter_func=param_noise_filter_func)<EOL><DEDENT>else:<EOL><INDENT>act_f = build_act(make_obs_ph, q_func, num_actions, scope=scope, reuse=reuse)<EOL><DEDENT>with tf.variable_scope(scope, reuse=reuse):<EOL><INDENT>obs_t_input = make_obs_ph("<STR_LIT>")<EOL>act_t_ph = tf.placeholder(tf.int32, [None], name="<STR_LIT:action>")<EOL>rew_t_ph = tf.placeholder(tf.float32, [None], name="<STR_LIT>")<EOL>obs_tp1_input = make_obs_ph("<STR_LIT>")<EOL>done_mask_ph = tf.placeholder(tf.float32, [None], name="<STR_LIT>")<EOL>importance_weights_ph = tf.placeholder(tf.float32, [None], name="<STR_LIT>")<EOL>q_t = q_func(obs_t_input.get(), num_actions, scope="<STR_LIT>", reuse=True)  <EOL>q_func_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=tf.get_variable_scope().name + "<STR_LIT>")<EOL>q_tp1 = q_func(obs_tp1_input.get(), num_actions, scope="<STR_LIT>")<EOL>target_q_func_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=tf.get_variable_scope().name + "<STR_LIT>")<EOL>q_t_selected = tf.reduce_sum(q_t * tf.one_hot(act_t_ph, num_actions), <NUM_LIT:1>)<EOL>if double_q:<EOL><INDENT>q_tp1_using_online_net = q_func(obs_tp1_input.get(), num_actions, scope="<STR_LIT>", reuse=True)<EOL>q_tp1_best_using_online_net = tf.argmax(q_tp1_using_online_net, <NUM_LIT:1>)<EOL>q_tp1_best = tf.reduce_sum(q_tp1 * tf.one_hot(q_tp1_best_using_online_net, num_actions), <NUM_LIT:1>)<EOL><DEDENT>else:<EOL><INDENT>q_tp1_best = tf.reduce_max(q_tp1, <NUM_LIT:1>)<EOL><DEDENT>q_tp1_best_masked = (<NUM_LIT:1.0> - done_mask_ph) * q_tp1_best<EOL>q_t_selected_target = rew_t_ph + gamma * q_tp1_best_masked<EOL>td_error = q_t_selected - tf.stop_gradient(q_t_selected_target)<EOL>errors = U.huber_loss(td_error)<EOL>weighted_error = tf.reduce_mean(importance_weights_ph * errors)<EOL>if grad_norm_clipping is not None:<EOL><INDENT>gradients = optimizer.compute_gradients(weighted_error, var_list=q_func_vars)<EOL>for i, (grad, var) in enumerate(gradients):<EOL><INDENT>if grad is not None:<EOL><INDENT>gradients[i] = (tf.clip_by_norm(grad, grad_norm_clipping), var)<EOL><DEDENT><DEDENT>optimize_expr = optimizer.apply_gradients(gradients)<EOL><DEDENT>else:<EOL><INDENT>optimize_expr = optimizer.minimize(weighted_error, var_list=q_func_vars)<EOL><DEDENT>update_target_expr = []<EOL>for var, var_target in zip(sorted(q_func_vars, key=lambda v: v.name),<EOL>sorted(target_q_func_vars, key=lambda v: v.name)):<EOL><INDENT>update_target_expr.append(var_target.assign(var))<EOL><DEDENT>update_target_expr = tf.group(*update_target_expr)<EOL>train = U.function(<EOL>inputs=[<EOL>obs_t_input,<EOL>act_t_ph,<EOL>rew_t_ph,<EOL>obs_tp1_input,<EOL>done_mask_ph,<EOL>importance_weights_ph<EOL>],<EOL>outputs=td_error,<EOL>updates=[optimize_expr]<EOL>)<EOL>update_target = U.function([], [], updates=[update_target_expr])<EOL>q_values = U.function([obs_t_input], q_t)<EOL>return act_f, train, update_target, {'<STR_LIT>': q_values}<EOL><DEDENT>
Creates the train function:

    Parameters
    ----------
    make_obs_ph: str -> tf.placeholder or TfInput
        a function that takes a name and creates a placeholder of input with that name
    q_func: (tf.Variable, int, str, bool) -> tf.Variable
        the model that takes the following inputs:
            observation_in: object
                the output of observation placeholder
            num_actions: int
                number of actions
            scope: str
            reuse: bool
                should be passed to outer variable scope
        and returns a tensor of shape (batch_size, num_actions) with values of every action.
    num_actions: int
        number of actions
    reuse: bool
        whether or not to reuse the graph variables
    optimizer: tf.train.Optimizer
        optimizer to use for the Q-learning objective.
    grad_norm_clipping: float or None
        clip gradient norms to this value. If None no clipping is performed.
    gamma: float
        discount rate.
    double_q: bool
        if true will use Double Q Learning (https://arxiv.org/abs/1509.06461).
        In general it is a good idea to keep it enabled.
    scope: str or VariableScope
        optional scope for variable_scope.
    reuse: bool or None
        whether or not the variables should be reused. To be able to reuse the scope must be given.
    param_noise: bool
        whether or not to use parameter space noise (https://arxiv.org/abs/1706.01905)
    param_noise_filter_func: tf.Variable -> bool
        function that decides whether or not a variable should be perturbed. Only applicable
        if param_noise is True. If set to None, default_param_noise_filter is used by default.

    Returns
    -------
    act: (tf.Variable, bool, float) -> tf.Variable
        function to select and action given observation.
`       See the top of the file for details.
    train: (object, np.array, np.array, object, np.array, np.array) -> np.array
        optimize the error in Bellman's equation.
`       See the top of the file for details.
    update_target: () -> ()
        copy the parameters from optimized Q function to the target Q function.
`       See the top of the file for details.
    debug: {str: function}
        a bunch of functions to print debug data like q_values.