web-dev-qa-db-fra.com

Dans Tensorflow, comment utiliser tf.gather () pour la dernière dimension?

J'essaie de rassembler les tranches d'un tenseur en fonction de la dernière dimension pour la connexion partielle entre les couches. Étant donné que la forme du tenseur de sortie est [batch_size, h, w, depth], je souhaite sélectionner des tranches en fonction de la dernière dimension, telles que

# L is intermediate tensor
partL = L[:, :, :, [0,2,3,8]]

Cependant, tf.gather(L, [0, 2,3,8]) ne semble fonctionner que pour la première dimension (non?). Quelqu'un peut-il me dire comment le faire? 

11
YW P Kwon

Il existe un bogue de suivi pour prendre en charge ce cas d'utilisation ici: https://github.com/tensorflow/tensorflow/issues/206

Pour l'instant vous pouvez:

  1. transposez votre matrice de sorte que la dimension à rassembler soit la première (la transposition coûte cher)

  2. remodelez votre tenseur en 1d (remodeler n'est pas cher) et transformez vos index de colonne de rassemblement en une liste d'indices d'éléments individuels à l'indexation linéaire, puis remodelez en arrière

  3. utilisez gather_nd. Vous devrez toujours transformer vos index de colonne en liste d’index d’éléments individuels.
9
Yaroslav Bulatov

À partir de TensorFlow 1.3, tf.gather a un paramètre axis, les différentes solutions de contournement ne sont donc plus nécessaires.

https://www.tensorflow.org/versions/r1.3/api_docs/python/tf/gatherhttps://github.com/tensorflow/tensorflow/issues/11223

16
rryan

Avec Rall_nd vous pouvez maintenant faire ceci comme suit:

cat_idx = tf.concat([tf.range(0, tf.shape(x)[0]), indices_for_dim1], axis=0)
result = tf.gather_nd(matrix, cat_idx)

Aussi, comme rapporté par l'utilisateur Nova dans un fil de discussion référencé par @Yaroslav Bulatov:

x = tf.constant([[1, 2, 3],
                 [4, 5, 6],
                 [7, 8, 9]])
idx = tf.constant([1, 0, 2])
idx_flattened = tf.range(0, x.shape[0]) * x.shape[1] + idx
y = tf.gather(tf.reshape(x, [-1]),  # flatten input
              idx_flattened)  # use flattened indices

with tf.Session(''):
  print y.eval()  # [2 4 9]

Le Gist aplatit le tenseur et utilise l'adressage 1D à pas avec tf.gather (...).

7
Andrei Pokrovsky

Encore une autre solution utilisant tf.unstack (...), tf.gather (...) et tf.stack (..)

Code:

import tensorflow as tf
import numpy as np

shape = [2, 2, 2, 10] 
L = np.arange(np.prod(shape))
L = np.reshape(L, shape)

indices = [0, 2, 3, 8]
axis = -1 # last dimension

def gather_axis(params, indices, axis=0):
    return tf.stack(tf.unstack(tf.gather(tf.unstack(params, axis=axis), indices)), axis=axis)

print(L)
with tf.Session() as sess:
    partL = sess.run(gather_axis(L, indices, axis))
    print(partL)

Résultat:

L = 
[[[[ 0  1  2  3  4  5  6  7  8  9]
   [10 11 12 13 14 15 16 17 18 19]]

  [[20 21 22 23 24 25 26 27 28 29]
   [30 31 32 33 34 35 36 37 38 39]]]


 [[[40 41 42 43 44 45 46 47 48 49]
   [50 51 52 53 54 55 56 57 58 59]]

  [[60 61 62 63 64 65 66 67 68 69]
   [70 71 72 73 74 75 76 77 78 79]]]]

partL = 
[[[[ 0  2  3  8]
   [10 12 13 18]]

  [[20 22 23 28]
   [30 32 33 38]]]


 [[[40 42 43 48]
   [50 52 53 58]]

  [[60 62 63 68]
   [70 72 73 78]]]]
3
Yunseong Hwang

Une version correcte de la réponse de @ Andrei se lirait

cat_idx = tf.stack([tf.range(0, tf.shape(x)[0]), indices_for_dim1], axis=1)
result = tf.gather_nd(matrix, cat_idx)
3
Edward Hughes

Vous pouvez essayer de cette façon, par exemple (dans la plupart des cas en PNL au moins), 

Le paramètre est de forme [batch_size, depth] et les indices sont [i, j, k, n, m] dont la longueur est batch_size. Alors gather_nd peut être utile. 

parameters = tf.constant([
                          [11, 12, 13], 
                          [21, 22, 23], 
                          [31, 32, 33], 
                          [41, 42, 43]])    
targets = tf.constant([2, 1, 0, 1])    
batch_nums = tf.range(0, limit=parameters.get_shape().as_list()[0])     
indices = tf.stack((batch_nums, targets), axis=1) # the axis is the dimension number   
items = tf.gather_nd(parameters, indices)  
# which is what we want: [13, 22, 31, 42]

Cet extrait recherche d'abord la première dimension à travers le numéro de lot, puis extrait l'élément le long de cette dimension par le numéro cible. 

2
lerner

Le tenseur n'a pas la forme de l'attribut, mais la méthode get_shape (). Ci-dessous est exécutable par Python 2.7

import tensorflow as tf
import numpy as np
x = tf.constant([[1, 2, 3],
                 [4, 5, 6],
                 [7, 8, 9]])
idx = tf.constant([1, 0, 2])
idx_flattened = tf.range(0, x.get_shape()[0]) * x.get_shape()[1] + idx
y = tf.gather(tf.reshape(x, [-1]),  # flatten input
              idx_flattened)  # use flattened indices

with tf.Session(''):
  print y.eval()  # [2 4 9]
0
Adwin Jahn

Mise en œuvre 2. de @Yaroslav Bulatov's:

#Your indices
indices = [0, 2, 3, 8]

#Remember for final reshaping
n_indices = tf.shape(indices)[0]

flattened_L = tf.reshape(L, [-1])

#Walk strided over the flattened array
offset = tf.expand_dims(tf.range(0, tf.reduce_prod(tf.shape(L)), tf.shape(L)[-1]), 1)
flattened_indices = tf.reshape(tf.reshape(indices, [-1])+offset, [-1])
selected_rows = tf.gather(flattened_L, flattened_indices)

#Final reshape
partL = tf.reshape(selected_rows, tf.concat(0, [tf.shape(L)[:-1], [n_indices]]))

Crédit à Comment sélectionner les lignes d'un tenseur 3D dans TensorFlow?

0
Sven Dorkenwald