如何在 Tensorflow 中进行切片分配

Posted

技术标签:

【中文标题】如何在 Tensorflow 中进行切片分配【英文标题】:How to do slice assignment in Tensorflow 【发布时间】:2017-01-02 14:53:15 【问题描述】:

我发现 Tensorflow 提供了scatter_update() 来为 0 维张量的切片赋值。例如,如果张量T 是三维的,我可以将值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()

但是如何将值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()

有没有 TF 提供的其他功能或者简单的方法来做到这一点?

【问题讨论】:

【参考方案1】:

TF2 的答案:

不幸的是,在 Tensorflow 2 (TF2) 中仍然没有优雅的方式来做到这一点。

我发现最好的方法是取消堆栈分配然后重新堆栈:

x = tf.random.uniform(shape=(5,))
new_val = 7
y = tf.unstack(x)
y[2] = new_val
x_updated = tf.stack(y)

【讨论】:

【参考方案2】:

目前,您可以在 TensorFlow 中对变量进行切片赋值。它没有特定的命名函数,但您可以选择一个切片并在其上调用assign

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

首先,请注意(在查看the documentation 之后)似乎assign 的返回值,即使应用于切片,也始终是对 whole 变量的引用应用更新。

编辑:以下信息要么已弃用,要么不准确,要么总是错误的。事实是 assign 的返回值是一个可以轻松使用的张量,并且已经将依赖项合并到分配中,因此只需评估它或在进一步的操作中使用它将确保它在不需要显式 @987654331 的情况下执行@块。


另外请注意,这只会将赋值操作添加到图中,但不会运行它,除非它被显式执行或设置为其他操作的依赖项。一个好的做法是在 tf.control_dependencies 上下文中使用它:

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

您可以在 TensorFlow 问题 #4638 中阅读更多相关信息。

【讨论】:

我知道这是一个老话题,但我目前正在尝试使用它并且收到一个错误,即未为此操作定义渐变。 (LookupError: No gradient defined for operation 'strided_slice/_assign' (op type: StridedSliceAssign)。你碰巧知道任何解决方法吗?或者类似的“scatter_*”操作会定义渐变? @WayneTreible 赋值操作确实没有梯度。如果您想要替换张量中的某些特定值以进行某些计算,则必须手动构造整个张量。这里有几个例子:using concatenations 和 using a mask。如果这些对您不起作用,也许您可​​以发布一个完整的问题,其中包含有关您的案例和一些代码的更多详细信息(稍后请随时在此处链接)。 嘿,杰德赫萨。我在这里发布了我的问题以及更多信息 -> ***.com/questions/49755316/… 感谢您的建议,与此同时,我将继续研究解决方案。【参考方案3】:

tf.scatter_update 可以修改第一维的张量。如文档中所述,

指数:张量。必须是以下类型之一:int32、int64。 ref 第一维的索引张量。

你可以使用scatter_nd_update 函数来做你想做的事。如下图,我已经测试过了。

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)

【讨论】:

这个答案并没有错,但有一个重要的提示:由于张量不是变量(我知道 OP 正在使用变量),当有人试图使用这种方法来更新张量时,它容易出错:AttributeError: 'Tensor' object has no attribute '_lazy_read'【参考方案4】:

我相信您需要的是ticket #206 中讨论的assign_slice_update不过目前还没有。

更新:现已实施。见 jdehesa 的回答:https://***.com/a/43139565/6531137


assign_slice_update(或scatter_nd())可用之前,您可以构建所需行的块,其中包含您不想修改的值以及要更新的所需值,如下所示:

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

该示例生成一个张量并执行a[i,j,:] = 5。大部分复杂性在于获取我们不想修改的值,a[i,~j,:](否则scatter_update() 将替换这些值)。

如果您想按照您的要求执行T[i,k,:] = a[1,1,:],您需要将前面示例中的5*tf.ones([1, 36]) 替换为tf.gather_nd(a, [[1, 1]])

另一种方法是为 tf.select() 从中创建所需元素的掩码并将其分配回变量,如下所示:

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,:])

它在内存方面的效率可能较低,因为它需要两倍的内存来处理 a 类似 to_update 变量,但您可以轻松修改最后一个示例以从 @987654338 获得梯度保留操作@节点。您可能也有兴趣查看其他 *** 问题:Conditional assignment of tensor values in TensorFlow。

当合适的 TensorFlow 函数可用时,应将这些不雅的扭曲替换为调用正确的 TensorFlow 函数。

【讨论】:

感谢您的详细示例!正如你所说,目前的方式有点不雅。希望assign_slice_update功能早日上线。 tf.select 已在 tensorflow 1 中替换为 tf.where。 有没有办法执行 T[:,k,:] = a[:,1,:] 其中 a 不一定是变量。

以上是关于如何在 Tensorflow 中进行切片分配的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地分配给 TensorFlow 中的张量切片

如何在 TensorFlow 中对数据集进行切片?

如何在 TensorFlow 中对批次进行切片并在每个切片上应用操作

tensorflow通过(不同范围的2d)切片列表更改/分配矩阵元素值

如何在 Tensorflow 中对无维度的张量进行切片

对张量流变量的切片分配