Cortando um tensor com um tensor de índices e tf.gather

Sep 27 2020

Estou tentando fatiar um tensor com um tensor de índices. Para este propósito estou tentando usar tf.gather. No entanto, estou tendo dificuldade em entender a documentação e não faço com que funcione como eu esperava:

Eu tenho dois tensores. Um activationstensor com a forma de [1,240,4]e um idstensor com a forma [1,1,120]. Eu quero cortar a segunda dimensão do activationstensor com os índices fornecidos na terceira dimensão do idstensor:

downsampled_activations = tf.gather(activations, ids, axis=1)

Dei a axis=1opção, pois esse é o eixo do activationstensor que desejo fatiar.

No entanto, isso não renderiza o resultado esperado e apenas me dá o seguinte erro:

tensorflow.python.framework.errors_impl.InvalidArgumentError: indices[0,0,1] = 1 is not in [0, 1)

Eu tentei várias combinações das opções axise batch_dims, mas sem sucesso até agora e a documentação realmente não me ajuda no meu caminho. Qualquer pessoa que se importe em explicar os parâmetros com mais detalhes ou no exemplo acima seria muito útil!

Editar: os IDs são pré-calculados antes do tempo de execução e chegam por meio de um pipeline de entrada como:

features = tf.io.parse_single_example(
            serialized_example,
            features={ 'featureIDs': tf.io.FixedLenFeature([], tf.string)}

Eles são então remodelados para o formato anterior:

feature_ids_raw = tf.decode_raw(features['featureIDs'], tf.int32)
feature_ids_shape = tf.stack([batch_size, (num_neighbours * 4)])
feature_ids = tf.reshape(feature_ids_raw, feature_ids_shape)
feature_ids = tf.expand_dims(feature_ids, 0)

Depois eles têm a forma mencionada anteriormente ( batch_size = 1e num_neighbours = 30-> [1,1,120]) e eu quero usá-los para fatiar o activationstensor.

Edit2: Eu gostaria que a saída fosse [1,120,4]. (Portanto, gostaria de reunir as entradas ao longo da segunda dimensão do activationstensor de acordo com os IDs armazenados em meu idstensor.)

Respostas

1 DaveIdito Oct 02 2020 at 02:29

tf.gatherReúna fatias do paramseixo de axisacordo com os índices.

Admitindo-se que a documentação não seja a mais expressiva, e a ênfase deve ser colocada nas fatias (já que você indexa fatias a partir dos axiselementos e não, que é o que suponho que você erroneamente considerou isso).

Vamos dar um exemplo muito menor:

activations_small = tf.convert_to_tensor([[[1, 2, 3, 4], [11, 22, 33, 44]]])
print(activations_small.shape) # [1, 2, 4]

Vamos imaginar este tensor:

    XX 4  XX 44 XX XX
  XX  3 XX  33 X  XX
XXX 2 XX   22XX  XX
X-----X-----+X  XX
|  1  |  11 | XX
+-----+-----+X

tf.gather(activations1, [0, 0], axis=1) retornará

<tf.Tensor: shape=(1, 2, 4), dtype=int32, numpy=
array([[[1, 2, 3, 4],
        [1, 2, 3, 4]]], dtype=int32)>

O que tf.gatherfiz foi olhar do eixo 1 e pegar o índice 0 (ofc, duas vezes ie [0, 0]). Se você fosse correr tf.gather(activations1, [0, 0, 0, 0, 0], axis=1).shape, você conseguiria TensorShape([1, 5, 4]).

Seu erro Agora, vamos tentar acionar o erro que você está obtendo.

tf.gather(activations1, [0, 2], axis=1)

InvalidArgumentError: indices [1] = 2 não está em [0, 2) [Op: GatherV2]

O que aconteceu aqui foi que, quando tf.gatherolhamos da perspectiva do eixo 1, não há nenhum item (coluna, se preferir) com índice = 2.

Eu acho que é isso que a documentação está sugerindo por

param:<indices>O índice Tensor. Deve ser um dos seguintes tipos: int32, int64. Deve estar no intervalo [0, params.shape [eixo]).

Sua (potencial) solução

Pelas dimensões indicese do resultado esperado de sua pergunta, não tenho certeza se o acima foi muito óbvio para você.

tf.gather(activations, indices=[0, 1, 2, 3], axis=2)ou qualquer coisa com índices dentro da faixa de índices em [0, activations.shape[2])ie [0, 4)funcionaria. Qualquer outra coisa geraria o erro que você está obtendo.

Há uma resposta literal abaixo, caso esse seja o resultado esperado.

1 TouYou Oct 02 2020 at 02:28

Você pode usar :

downsampled_activations =tf.gather(activations , tf.squeeze(ids) ,axis = 1)
downsampled_activations.shape #  [1,120,4]

Na maioria dos casos, o método tf.gather precisa de índices 1d, e isso está certo no seu caso, em vez de índices com 3d (1,1,120), um 1d é suficiente (120,). O método tf.gather irá olhar para o eixo (= 1) e retornar o elemento em cada índice fornecido pelo tensor de índices.