¿Es posible minimizar una función de pérdida cambiando solo algunos elementos de una variable? En otras palabras, si tengo una variable X
de longitud 2, ¿cómo puedo minimizar mi función de pérdida cambiando X[0]
y manteniendo constante X[1]
?
Con suerte, este código que he intentado describirá mi problema:
import tensorflow as tf import tensorflow.contrib.opt as opt X = tf.Variable([1.0, 2.0]) X0 = tf.Variable([3.0]) Y = tf.constant([2.0, -3.0]) scatter = tf.scatter_update(X, [0], X0) with tf.control_dependencies([scatter]): loss = tf.reduce_sum(tf.squared_difference(X, Y)) opt = opt.ScipyOptimizerInterface(loss, [X0]) init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) opt.minimize(sess) print("X: {}".format(X.eval())) print("X0: {}".format(X0.eval()))
que salidas:
INFO:tensorflow:Optimization terminated with: Message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL' Objective function value: 26.000000 Number of iterations: 0 Number of functions evaluations: 1 X: [3. 2.] X0: [3.]
donde me gustaría encontrar el valor óptimo de X0 = 2
y por lo tanto X = [2, 2]
editar
Motivación para hacer esto: me gustaría importar un gráfico/modelo entrenado y luego ajustar varios elementos de algunas de las variables dependiendo de algunos datos nuevos que tengo.
Puede usar este truco para restringir el cálculo del gradiente a un índice:
import tensorflow as tf import tensorflow.contrib.opt as opt X = tf.Variable([1.0, 2.0]) part_X = tf.scatter_nd([[0]], [X[0]], [2]) X_2 = part_X + tf.stop_gradient(-part_X + X) Y = tf.constant([2.0, -3.0]) loss = tf.reduce_sum(tf.squared_difference(X_2, Y)) opt = opt.ScipyOptimizerInterface(loss, [X]) init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) opt.minimize(sess) print("X: {}".format(X.eval()))
part_X
se convierte en el valor que desea cambiar en un vector one-hot de la misma forma que X. part_X + tf.stop_gradient(-part_X + X)
es lo mismo que X en el pase hacia adelante, ya que part_X - part_X
es 0. Sin embargo en el paso hacia atrás, tf.stop_gradient
evita todos los cálculos de gradiente innecesarios.
No estoy seguro de si es posible con la interfaz del optimizador SciPy, pero al usar una de las subclases regulares de tf.train.Optimizer
puede hacer algo así llamando primero a compute_gradients
, luego enmascarando los gradientes y luego llamando a apply_gradients
, en lugar de llamar minimize
(que, como dicen los documentos, básicamente llama a los anteriores).
import tensorflow as tf X = tf.Variable([3.0, 2.0]) # Select updatable parameters X_mask = tf.constant([True, False], dtype=tf.bool) Y = tf.constant([2.0, -3.0]) loss = tf.reduce_sum(tf.squared_difference(X, Y)) opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) # Get gradients and mask them ((X_grad, _),) = opt.compute_gradients(loss, var_list=[X]) X_grad_masked = X_grad * tf.cast(X_mask, dtype=X_grad.dtype) # Apply masked gradients train_step = opt.apply_gradients([(X_grad_masked, X)]) init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) for i in range(10): _, X_val = sess.run([train_step, X]) print("Step {}: X = {}".format(i, X_val)) print("Final X = {}".format(X.eval()))
Producción:
Step 0: X = [ 2.79999995 2. ] Step 1: X = [ 2.63999987 2. ] Step 2: X = [ 2.51199985 2. ] Step 3: X = [ 2.40959978 2. ] Step 4: X = [ 2.32767987 2. ] Step 5: X = [ 2.26214385 2. ] Step 6: X = [ 2.20971513 2. ] Step 7: X = [ 2.16777205 2. ] Step 8: X = [ 2.13421774 2. ] Step 9: X = [ 2.10737419 2. ] Final X = [ 2.10737419 2. ]
Esto debería ser bastante fácil de hacer utilizando el parámetro var_list
de la función de minimize
.
trainable_var = X[0] train_op = tf.train.GradientDescentOptimizer(learning_rate=1e-3).minimize(loss, var_list=[trainable_var])
Debe tener en cuenta que, por convención, todas las variables entrenables se agregan a la colección predeterminada de GraphKeys.TRAINABLE_VARIABLES
, por lo que puede obtener una lista de todas las variables entrenables usando:
all_trainable_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)
Esta es solo una lista de variables que puede manipular como mejor le parezca y usar como el parámetro var_list
.
Como tangente a su pregunta, si alguna vez desea llevar la personalización del proceso de optimización un paso más allá, también puede calcular los gradientes manualmente usando grads = tf.gradients(loss, var_list)
manipular los gradientes como mejor le parezca, luego llame a tf.train.GradientDescentOptimizer(...).apply_gradients(grads_and_vars_as_list_of_tuples)
. Debajo del capó, minimizar solo está haciendo estos dos pasos por usted.
También tenga en cuenta que es perfectamente libre de crear diferentes optimizadores para diferentes colecciones de variables. Podría crear un optimizador SGD con una tasa de aprendizaje 1e-4 para algunas variables y otro optimizador Adam con una tasa de aprendizaje 1e-2 para otro conjunto de variables. No es que haya ningún caso de uso específico para esto, solo estoy señalando la flexibilidad que ahora tiene.