如何有效地分配给 TensorFlow 中的张量切片
How to efficiently assign to a slice of a tensor in TensorFlow
我想在 TensorFlow 2.x 中的一个模型中为输入张量的切片分配一些值(我正在使用 2.2,但准备接受 2.1 的解决方案)。
我正在尝试做的一个非工作模板是:
import tensorflow as tf
from tensorflow.keras.models import Model
class AddToEven(Model):
def call(self, inputs):
outputs = inputs
outputs[:, ::2] += inputs[:, ::2]
return outputs
当然,在构建这个 (AddToEven().build(tf.TensorShape([None, None]))
) 时,我收到以下错误:
TypeError: 'Tensor' object does not support item assignment
我可以通过以下方式实现这个简单的例子:
class AddToEvenScatter(Model):
def call(self, inputs):
batch_size = tf.shape(inputs)[0]
n = tf.shape(inputs)[-1]
update_indices = tf.range(0, n, delta=2)[:, None]
scatter_nd_perm = [1, 0]
inputs_reshaped = tf.transpose(inputs, scatter_nd_perm)
outputs = tf.tensor_scatter_nd_add(
inputs_reshaped,
indices=update_indices,
updates=inputs_reshaped[::2],
)
outputs = tf.transpose(outputs, scatter_nd_perm)
return outputs
(您可以通过以下方式进行完整性检查:
model = AddToEvenScatter()
model.build(tf.TensorShape([None, None]))
model(tf.ones([1, 10]))
)
但是如您所见,编写起来非常复杂。这仅适用于一维(+批量大小)张量上的静态更新次数(此处为 1)。
我想做的有点复杂,我认为用 tensor_scatter_nd_add
编写它会是一场噩梦。
当前关于该主题的许多 QA 都涵盖了变量而不是张量的情况(参见 or )。
提到 here 确实 pytorch 支持这个,所以我很惊讶最近没有看到任何 tf 成员对该主题的回应。
This answer 并没有真正帮助我,因为我需要某种掩码生成,这也会很糟糕。
因此问题是:我怎样才能有效地进行切片分配(计算方面、内存方面和代码方面)w/o tensor_scatter_nd_add
?诀窍是我希望它尽可能动态,这意味着 inputs
的形状可以是可变的。
(对于任何好奇的人,我正在尝试将 this code 翻译成 tf)。
此问题最初发布于 in a GitHub issue。
这似乎没有产生任何错误:
import tensorflow as tf
from tensorflow.keras.models import Model
class AddToEven(Model):
def call(self, inputs):
outputs = inputs
outputs = outputs[:, ::2] + 1
return outputs
# tf.Tensor.__iadd__ does not seem to exist, but tf.Tensor.__add__ does.
这是另一种基于二进制掩码的解决方案。
"""Solution based on binary mask.
- We just add this mask to inputs, instead of multiplying."""
class AddToEven(tf.keras.Model):
def __init__(self):
super(AddToEven, self).__init__()
def build(self, inputshape):
self.built = True # Actually nothing to build with, becuase we don't have any variables or weights here.
@tf.function
def call(self, inputs):
w = inputs.get_shape()[-1]
# 1-d mask generation for w-axis (activate even indices only)
m_w = tf.range(w) # [0, 1, 2,... w-1]
m_w = ((m_w%2)==0) # [True, False, True ,...] with dtype=tf.bool
# Apply 1-d mask to 2-d input
m_w = tf.expand_dims(m_w, axis=0) # just extend dimension as to be (1, W)
m_w = tf.cast(m_w, dtype=inputs.dtype) # in advance, we need to convert dtype
# Here, we just add this (1, W) mask to (H,W) input magically.
outputs = inputs + m_w # This add operation is allowed in both TF and numpy!
return tf.reshape(outputs, inputs.get_shape())
在此处进行完整性检查。
# sanity-check as model
model = AddToEven()
model.build(tf.TensorShape([None, None]))
z = model(tf.zeros([2,4]))
print(z)
结果(使用 TF 2.1)是这样的。
tf.Tensor(
[[1. 0. 1. 0.]
[1. 0. 1. 0.]], shape=(2, 4), dtype=float32)
-------- 下面是之前的回答--------
您需要在 build() 方法中创建 tf.Variable。
它还允许通过 shape=(None,) 动态调整大小。
在下面的代码中,我将输入形状指定为 (None, None).
class AddToEven(tf.keras.Model):
def __init__(self):
super(AddToEven, self).__init__()
def build(self, inputshape):
self.v = tf.Variable(initial_value=tf.zeros((0,0)), shape=(None, None), trainable=False, dtype=tf.float32)
@tf.function
def call(self, inputs):
self.v.assign(inputs)
self.v[:, ::2].assign(self.v[:, ::2] + 1)
return self.v.value()
我用 TF 2.1.0 和 TF1.15 测试了这段代码
# test
add_to_even = AddToEven()
z = add_to_even(tf.zeros((2,4)))
print(z)
结果:
tf.Tensor(
[[1. 0. 1. 0.]
[1. 0. 1. 0.]], shape=(2, 4), dtype=float32)
P.S。还有一些其他的方法,比如使用tf.numpy_function(),或者生成掩码函数。
我想在 TensorFlow 2.x 中的一个模型中为输入张量的切片分配一些值(我正在使用 2.2,但准备接受 2.1 的解决方案)。 我正在尝试做的一个非工作模板是:
import tensorflow as tf
from tensorflow.keras.models import Model
class AddToEven(Model):
def call(self, inputs):
outputs = inputs
outputs[:, ::2] += inputs[:, ::2]
return outputs
当然,在构建这个 (AddToEven().build(tf.TensorShape([None, None]))
) 时,我收到以下错误:
TypeError: 'Tensor' object does not support item assignment
我可以通过以下方式实现这个简单的例子:
class AddToEvenScatter(Model):
def call(self, inputs):
batch_size = tf.shape(inputs)[0]
n = tf.shape(inputs)[-1]
update_indices = tf.range(0, n, delta=2)[:, None]
scatter_nd_perm = [1, 0]
inputs_reshaped = tf.transpose(inputs, scatter_nd_perm)
outputs = tf.tensor_scatter_nd_add(
inputs_reshaped,
indices=update_indices,
updates=inputs_reshaped[::2],
)
outputs = tf.transpose(outputs, scatter_nd_perm)
return outputs
(您可以通过以下方式进行完整性检查:
model = AddToEvenScatter()
model.build(tf.TensorShape([None, None]))
model(tf.ones([1, 10]))
)
但是如您所见,编写起来非常复杂。这仅适用于一维(+批量大小)张量上的静态更新次数(此处为 1)。
我想做的有点复杂,我认为用 tensor_scatter_nd_add
编写它会是一场噩梦。
当前关于该主题的许多 QA 都涵盖了变量而不是张量的情况(参见
因此问题是:我怎样才能有效地进行切片分配(计算方面、内存方面和代码方面)w/o tensor_scatter_nd_add
?诀窍是我希望它尽可能动态,这意味着 inputs
的形状可以是可变的。
(对于任何好奇的人,我正在尝试将 this code 翻译成 tf)。
此问题最初发布于 in a GitHub issue。
这似乎没有产生任何错误:
import tensorflow as tf
from tensorflow.keras.models import Model
class AddToEven(Model):
def call(self, inputs):
outputs = inputs
outputs = outputs[:, ::2] + 1
return outputs
# tf.Tensor.__iadd__ does not seem to exist, but tf.Tensor.__add__ does.
这是另一种基于二进制掩码的解决方案。
"""Solution based on binary mask.
- We just add this mask to inputs, instead of multiplying."""
class AddToEven(tf.keras.Model):
def __init__(self):
super(AddToEven, self).__init__()
def build(self, inputshape):
self.built = True # Actually nothing to build with, becuase we don't have any variables or weights here.
@tf.function
def call(self, inputs):
w = inputs.get_shape()[-1]
# 1-d mask generation for w-axis (activate even indices only)
m_w = tf.range(w) # [0, 1, 2,... w-1]
m_w = ((m_w%2)==0) # [True, False, True ,...] with dtype=tf.bool
# Apply 1-d mask to 2-d input
m_w = tf.expand_dims(m_w, axis=0) # just extend dimension as to be (1, W)
m_w = tf.cast(m_w, dtype=inputs.dtype) # in advance, we need to convert dtype
# Here, we just add this (1, W) mask to (H,W) input magically.
outputs = inputs + m_w # This add operation is allowed in both TF and numpy!
return tf.reshape(outputs, inputs.get_shape())
在此处进行完整性检查。
# sanity-check as model
model = AddToEven()
model.build(tf.TensorShape([None, None]))
z = model(tf.zeros([2,4]))
print(z)
结果(使用 TF 2.1)是这样的。
tf.Tensor(
[[1. 0. 1. 0.]
[1. 0. 1. 0.]], shape=(2, 4), dtype=float32)
-------- 下面是之前的回答--------
您需要在 build() 方法中创建 tf.Variable。 它还允许通过 shape=(None,) 动态调整大小。 在下面的代码中,我将输入形状指定为 (None, None).
class AddToEven(tf.keras.Model):
def __init__(self):
super(AddToEven, self).__init__()
def build(self, inputshape):
self.v = tf.Variable(initial_value=tf.zeros((0,0)), shape=(None, None), trainable=False, dtype=tf.float32)
@tf.function
def call(self, inputs):
self.v.assign(inputs)
self.v[:, ::2].assign(self.v[:, ::2] + 1)
return self.v.value()
我用 TF 2.1.0 和 TF1.15 测试了这段代码
# test
add_to_even = AddToEven()
z = add_to_even(tf.zeros((2,4)))
print(z)
结果:
tf.Tensor(
[[1. 0. 1. 0.]
[1. 0. 1. 0.]], shape=(2, 4), dtype=float32)
P.S。还有一些其他的方法,比如使用tf.numpy_function(),或者生成掩码函数。