Cortando um tensor com um tensor de índices e tf.gather
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
tf.gatherReúna fatias doparamseixo deaxisacordo 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.
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.