web-dev-qa-db-fra.com

Comment faire une affectation de tranche dans Tensorflow

J'ai constaté que Tensorflow fournit scatter_update() pour attribuer des valeurs à la tranche d'un tenseur dans la dimension 0. Par exemple, si le tenseur T est en trois dimensions, je peux attribuer la valeur v[1, :, :] à T[i, :, :]

a = tf.Variable(tf.zeros([10,36,36]))   
value = np.ones([1,36,36])   
d = tf.scatter_update(a,[0],value)

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    print a.eval()
    sess.run(d)
    print a.eval()

Mais comment assigner les valeurs v[1,1,:] à T[i,j,:]

a = tf.Variable(tf.zeros([10,36,36]))   
value1 = np.random.randn(1,1,36)    
e = tf.scatter_update(a,[0],value1) #Error

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    print a.eval()
    sess.rum(e)
    print a.eval()

Existe-t-il une autre fonction fournie par TF ou un moyen simple de le faire?

18
user270700

Je crois que ce dont vous avez besoin est le assign_slice_update mentionné dans ticket n ° 206Il n'est pas encore disponible, cependant.

UPDATE: Ceci est maintenant implémenté. Voir la réponse de jdehesa: https://stackoverflow.com/a/43139565/6531137


Jusqu'à ce que assign_slice_update (ou scatter_nd()) soit disponible, vous pouvez créer un bloc de la ligne souhaitée contenant les valeurs que vous ne souhaitez pas modifier, ainsi que les valeurs à mettre à jour, comme suit:

import tensorflow as tf

a = tf.Variable(tf.ones([10,36,36]))

i = 3
j = 5

# Gather values inside the a[i,...] block that are not on column j
idx_before = tf.concat(1, [tf.reshape(tf.tile(tf.Variable([i]), [j]), [-1, 1]), tf.reshape(tf.range(j), [-1, 1])])
values_before = tf.gather_nd(a, idx_before)
idx_after = tf.concat(1, [tf.reshape(tf.tile(tf.Variable([i]), [36-j-1]), [-1, 1]), tf.reshape(tf.range(j+1, 36), [-1, 1])])
values_after = tf.gather_nd(a, idx_after)

# Build a subset of tensor `a` with the values that should not be touched and the values to update
block = tf.concat(0, [values_before, 5*tf.ones([1, 36]), values_after])

d = tf.scatter_update(a, i, block)

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    sess.run(d)
    print(a.eval()[3,4:7,:]) # Print a subset of the tensor to verify

L'exemple génère un tenseur de uns et exécute a[i,j,:] = 5. La plus grande partie de la complexité réside dans l'obtention des valeurs que nous ne souhaitons pas modifier, a[i,~j,:] (sinon, scatter_update() remplacera ces valeurs).

Si vous souhaitez exécuter T[i,k,:] = a[1,1,:] comme vous l'avez demandé, vous devez remplacer 5*tf.ones([1, 36]) dans l'exemple précédent par tf.gather_nd(a, [[1, 1]]).

Une autre approche serait de créer un masque pour tf.select() les éléments souhaités à partir de celui-ci et de le réaffecter à la variable, en tant que tel:

import tensorflow as tf

a = tf.Variable(tf.zeros([10,36,36]))

i = tf.Variable([3])
j = tf.Variable([5])

# Build a mask using indices to perform [i,j,:]
atleast_2d = lambda x: tf.reshape(x, [-1, 1])
indices = tf.concat(1, [atleast_2d(tf.tile(i, [36])), atleast_2d(tf.tile(j, [36])), atleast_2d(tf.range(36))])
mask = tf.cast(tf.sparse_to_dense(indices, [10, 36, 36], 1), tf.bool)

to_update = 5*tf.ones_like(a)
out = a.assign( tf.select(mask, to_update, a) ) 

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    sess.run(out)
    print(a.eval()[2:5,5,:])

Il est potentiellement moins efficace en termes de mémoire car il nécessite deux fois plus de mémoire pour gérer la variable a- like to_update, mais vous pouvez facilement modifier ce dernier exemple pour obtenir une opération préservant le dégradé à partir du noeud tf.select(...). Vous pouvez également être intéressé par cette autre question de StackOverflow: Affectation conditionnelle des valeurs de tenseur dans TensorFlow .

Ces contorsions inélégantes doivent être remplacées par un appel à la fonction TensorFlow appropriée dès qu'elle est disponible.

7
Soravux

Actuellement, vous pouvez affecter des tranches à des variables dans TensorFlow. Il n'y a pas de fonction nommée spécifique pour cela, mais vous pouvez sélectionner une tranche et appeler assign dessus:

my_var = my_var[4:8].assign(tf.zeros(4))

Tout d’abord, notez que (après avoir examiné la documentation ), il semble que la valeur de retour de assign , même appliquée à une tranche, est toujours une référence à la variable whole après l’application la mise à jour.

EDIT: Les informations ci-dessous sont obsolètes, imprécises ou toujours fausses. Le fait est que la valeur retournée de assign est un tenseur qui peut être facilement utilisé et intègre déjà la dépendance à l'affectation. Par conséquent, le fait d'évaluer ou de l'utiliser si des opérations ultérieures garantit qu'il sera exécuté sans recourir à un bloc explicite tf.control_dependencies.


Notez également que cela ne fera qu'ajouter l'affectation op au graphe, mais ne l'exécutera que si elle est explicitement exécutée ou définie en tant que dépendance d'une autre opération. Une bonne pratique consiste à l'utiliser dans un contexte tf.control_dependencies:

with tf.control_dependencies([my_var[4:8].assign(tf.zeros(4))]):
    my_var = tf.identity(my_var)

Vous pouvez en savoir plus à ce sujet dans le numéro # 4638 de TensorFlow.

32
jdehesa

Le tf.scatter_update peut modifier le tenseur dans la première dimension. Comme indiqué dans la documentation, 

indices: un tenseur. Doit appartenir à l'un des types suivants: int32, int64. Un tenseur d'indices dans la première dimension de la réf.

Vous pouvez utiliser la fonction scatter_nd_update pour faire ce que vous voulez. Comme indiqué ci-dessous, que j'ai testé.

a = tf.Variable(tf.zeros([10,36,36])) 
value1 = np.random.randn(1,36)
e = tf.scatter_nd_update(a,[[0,1]],value1)
init= tf.global_variables_initializer()
sess.run(init)
print(a.eval())
sess.run(e)
1
X. L