Tensorflow: gradient_override_map no puede anular el gradiente hacia atrás de op tf.stack

Estaba intentando editar el mecanismo de cálculo de gradiente hacia atrás de tf.stack op con tf.RegisterGradient y tf.gradient_override_map , aquí están mis códigos:

 import tensorflow as tf class SynthGradBuilder(object): def __init__(self): self.num_calls = 0 def __call__(self, x, l=1.0): op_name = "SynthGrad%d" % self.num_calls @tf.RegisterGradient(op_name) def _grad_synth(op, grad): return grad[0] g = tf.get_default_graph() with g.gradient_override_map({"stack": op_name}): y = tf.stack([x,x]) self.num_calls += 1 return y GradSys = SynthGradBuilder() 

en otro guion escribi

 import tensorflow as tf from gradient_synthesizer import GradSys x = tf.Variable([1,2]) y = GradSys(x, l=1) z = tf.stack([x,x]) grad = tf.gradients(y, x, grad_ys=[[tf.convert_to_tensor([3, 4]), tf.convert_to_tensor([6, 8])]]) grad_stack = tf.gradients(z, x, grad_ys=[[tf.convert_to_tensor([3, 4]), tf.convert_to_tensor([6, 8])]]) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) print "grad bp: ", sess.run(grad) print "grad_stack: ", sess.run(grad_stack) print "y: ", sess.run(y) 

La salida esperada debe ser:

 grad bp: [3,4]; grad_stack: [3+6, 4+8] = [9, 12]; y: [[1,2], [1,2]]; 

Lo que realmente obtuve del código fue:

mi resultado

lo que indica que los gradientes hacia atrás de tf.stack no fueron reemplazados en absoluto, lo cual fue opuesto a mi expectativa.

No estoy seguro de si tal discrepancia se produjo al utilizar falsamente “stack” como la cadena de tipo de operación tf.stack , llevé a cabo un experimento de la siguiente manera:

mi validación

El primer elemento que describe el tensor y, el “stack: 0” sugiere que el nombre registrado de op tf.stack es “stack”, que también es su cadena de tipo. Así que parece que no es culpa de “stack”.

Estoy en una pérdida para averiguar las causas del problema de mis códigos. Me pregunto si alguien puede ayudarme con eso.

Tl; dr: El código correcto debe ser:

 @tf.RegisterGradient(op_name) def _grad_synth(op, grad): x, y = tf.unstack(grad) return [x, tf.zeros_like(y)] g = tf.get_default_graph() with g.gradient_override_map({"Pack": op_name}): y = tf.stack([x, x]) 

Debido a que esta es una pregunta bastante común, quiero explicar un poco más detalles:

Hay dos problemas principales en su código original:

  1. Uso incorrecto de gradient_override_map :

El nombre de OP real para tf.stack es Pack (no Stack ), por lo que necesita ovrride Pack lugar de Stack :

 `g.gradient_override_map({"Pack": op_name})`. 

Usted puede preguntarse cómo sé el nombre real de OP? Bueno, una forma simple es probar el GraphDef ejecutando el siguiente código:

 with tf.Graph().as_default(): x = tf.constant(0) y = tf.stack([x, x]) print(tf.get_default_graph().as_graph_def()) 
  1. Función de gradiente incorrecta:

Los gradientes originales para Pack son un Unpack simple ( código oficial ). En su caso, todavía necesita desempaquetar los gradientes, pero solo debe propagar la PRIMERA parte:

 @tf.RegisterGradient(op_name) def _grad_synth(op, grad): x, y = tf.unstack(grad) return [x, tf.zeros_like(y)] 

Tenga en cuenta, este código funciona perfectamente para su caso. Sin embargo, si desea admitir cualquier longitud de stack, puede usar una versión un poco más complicada:

 @tf.RegisterGradient(op_name) def _grad_synth(op, grad): x_list = tf.unstack(grad) for i in range(1, len(x_list)): x_list[i] = tf.zeros_like(x_list[i]) return x_list