TensorFlow 2.xのモデルの1つで入力テンソルのスライスにいくつかの値を割り当てたい(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]))
_
)
しかし、ご覧のとおり、書くのは非常に複雑です。そして、これは1D(+バッチサイズ)テンソルの静的な更新数(ここでは1)の場合のみです。
私がやりたいことはもう少し複雑で、_tensor_scatter_nd_add
_で書くのは悪夢になると思います。
トピックに関する現在のQAの多くは変数のケースをカバーしていますが、テンソルはカバーしていません(例 this または this を参照)。言及されている ここ 確かにpytorchがこれをサポートしているので、最近、そのトピックに関するtfメンバーからの応答がないことに驚いています。 この答え は、私にはあまり役立たない種類のマスク生成が必要になるため、本当に役に立ちません。
したがって、問題は次のとおりです。_tensor_scatter_nd_add
_を使用せずに、スライス割り当てを効率的に(計算ごと、メモリごと、およびコードごとに)実行するにはどうすればよいですか。トリックは、これをできるだけ動的にしたいということです。つまり、inputs
の形状は可変である可能性があります。
(好奇心旺盛な人のために、私は翻訳しようとしています このコード in tf)。
この質問は最初に投稿されました GitHubの問題 。
バイナリマスクに基づく別のソリューションを次に示します。
"""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)
結果(TF2.1で)はこんな感じです。
tf.Tensor(
[[1. 0. 1. 0.]
[1. 0. 1. 0.]], shape=(2, 4), dtype=float32)
--------以下は以前の回答です--------
Build()メソッドでtf.Variableを作成する必要があります。また、shape =(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)
追伸tf.numpy_function()の使用やマスク関数の生成など、他の方法もあります。
私はこれでエラーを生成しないようです:
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.
これは回避策のようですが、テストすることをお勧めします
class AddToEven(Model):
def call(self, inputs):
outputs = inputs
zeros = tf.Variable(lambda : tf.zeros_like(outputs), trainable=False)
zeros[:, ::2].assign(zeros[:, ::2]+1)
outputs = outputs + zeros
return outputs
model = AddToEven()
model(tf.zeros([1, 10]))
出力
<tf.Tensor: shape=(1, 10), dtype=float32, numpy=array([[1., 0., 1., 0., 1., 0., 1., 0., 1., 0.]], dtype=float32)>
tF 2.2で変更された作業例
class AddToEvenVar(Layer):
def __init__(self, inp_dim, **kwargs):
super(AddToEvenVar, self).__init__(**kwargs)
self.zeros = tf.Variable(tf.zeros(inp_dim), trainable=False)
self.zeros[:, ::2].assign(self.zeros[:, ::2]+1)
self.zeros = tf.expand_dims(self.zeros, 0)
def call(self, inputs):
return inputs + self.zeros
inp = Input((1,10))
out = AddToEvenVar((1,10))(inp)
out = Conv1D(1, 3, padding='same')(out)
model = Model(inp, out)
model.compile(loss='mse', optimizer='adam')
xx = tf.zeros([100, 1, 10])
model.fit(xx, xx, epochs=10, verbose = 1)