1
votes

I have a situation where I take a slice of a tf tensor, I convert it to numpy, I do some calculations and finally I want to place this slice back where it came from i.e. the tf tensor. To be specific this is all part of a mini-batch generation / alteration procedure.

mini_batch.shape

would produce the following tensor shape

TensorShape([#samples, 640, 1152, 3])

the np_slice that I process comes from the tensor above

np_slice = mini_batch[sample_index][:, :, 2].numpy()

and I try to re-insert it as such

mini_batch[sample_index][:, :, 2] = tf.convert_to_tensor(np_slice, dtype=tf.float32)

note that np_slice has shape (640, 1152) i.e. a one channel image

As I understand tf does not allow this kind of assignment, hence my error

TypeError: 'tensorflow.python.framework.ops.EagerTensor' object does not support item assignment

It would appear that I need to make use of tf.tensor_scatter_nd_update

This is what I tried so far but it does not work as I require

indices = tf.constant([[sample_index]])
updates = tf.convert_to_tensor(np_slice, dtype=tf.float32)
mini_batch = tf.tensor_scatter_nd_update(mini_batch, indices, updates)

Which produces the following

tensorflow.python.framework.errors_impl.InvalidArgumentError: Outer dimensions of indices and update must match. Indices shape: [1,1], updates shape:[640,1152] [Op:TensorScatterUpdate]
2

2 Answers

0
votes

Tensorflow Tensors are immutable (docs):

All tensors are immutable like Python numbers and strings: you can never update the contents of a tensor, only create a new one.

You could use a Tensorflow Variable or do the slicing and the calculations in a numpy array and convert it to a Tensor at the end:

mini_batch_np=mini_batch.numpy()
np_slice = mini_batch_np[sample_index][:, :, 2]

#some calculations with np_slice

mini_batch_np[sample_index][:, :, 2] = np_slice

#convert back to tensor
mini_batch=tf.convert_to_tensor(mini_batch_np, dtype=tf.float32)
0
votes

Yes, daveh's solution can help if you don't worry about the gradient back-propagation flow being truncated.

I offer an alternative solution, where you can assign value to EagerTensor with some trick about tf.concat.

import tensorflow as tf

# data simulate
samples=10
dim1=10
dim2=20
dim3=3
mini_batch=tf.random.uniform([samples, dim1, dim2, dim3])


# the new value you want to assign. 
# In this solution there is no need to convert to numpy.
np_slice = mini_batch[5, :, :, 2]

# the sample/dim index of the tensor you want to update
# note I use tf.convert_to_tensor if you want to assign numpy value
sample_index_to_replace=5
dim3_idx_to_replace=2

sample_tensor_to_replace = tf.concat(
    [
    mini_batch[sample_index_to_replace, :, :, :dim3_idx_to_replace],
    tf.convert_to_tensor(np_slice)[:,:, None],
    mini_batch[sample_index_to_replace, :, : , dim3_idx_to_replace+1:]
    ],
    axis=-1
)

mini_batch_new = tf.concat(
    [
        mini_batch[:sample_index_to_replace][:, :, :],
        tf.convert_to_tensor(sample_tensor_to_replace)[None, :, :, :],
        mini_batch[sample_index_to_replace+1:][:, :, :],
    ],
    axis=0,
)

tf.print(mini_batch_new)
# in this case, no value is modified
tf.debugging.assert_equal(mini_batch, mini_batch_new)