Tensorflow 2 - tf.slice 及其 NumPy 切片语法不兼容行为

Tensorflow 2 - tf.slice and its NumPy slice syntax incompatible behavior

问题

请确认以下是否符合设计和预期,或者 tf. slice 的问题,或者 tf. slice 的使用错误。如有错误,请指出如何更正。

背景

Introduction to tensor slicing - Extract tensor slices 表示类似 Numpy 的切片语法是 tf. slice.

的替代方法

Perform NumPy-like tensor slicing using tf. slice.

t1 = tf.constant([0, 1, 2, 3, 4, 5, 6, 7])
print(tf.slice(t1,
               begin=[1],
               size=[3]))

Alternatively, you can use a more Pythonic syntax. Note that tensor slices are evenly spaced over a start-stop range.

print(t1[1:4])

问题

更新深橙色区域。

TYPE = tf.int32
N = 4
D = 5
shape = (N,D)

# Target to update
Y = tf.Variable(
    initial_value=tf.reshape(tf.range(N*D,dtype=TYPE), shape=shape),
    trainable=True
)
print(f"Target Y: \n{Y}\n")
---
Target Y: 
<tf.Variable 'Variable:0' shape=(4, 5) dtype=int32, numpy=
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]], dtype=int32)>

tf. slice 无效。

# --------------------------------------------------------------------------------
# Slice region in the target to be updated
# --------------------------------------------------------------------------------
S = tf.slice(      # Error "EagerTensor' object has no attribute 'assign'"
    Y,
    begin=[0,1],   # Coordinate (n,d) as the start point
    size=[3,2]     # Shape (3,2) -> (n+3, n+2) as the end point
)
print(f"Slice to update S: \n{S}\n")

# Values to set
V = tf.ones(shape=tf.shape(S), dtype=TYPE)
print(f"Values to set V: \n{V}\n")

# Assing V to S region of T
S.assign(V)
---
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-17-e5692b1750c8> in <module>
     24 
     25 # Assing V to S region of T
---> 26 S.assign(V)

AttributeError: 'tensorflow.python.framework.ops.EagerTensor' object has no attribute 'assign'

但是,切片语法 有效。

S = Y[
    0:3,           # From coordinate (n=0,d),   slice rows (0,1,2)  or 'size'=3 -> shape (3,?)
    1:3            # From coordinate (n=0,d=1), slice columns (1,2) or 'size'=2 -> shape (3,2)
]                  
print(f"Slice to update S: \n{S}\n")

# Values to set
V = tf.ones(shape=tf.shape(S), dtype=TYPE)
print(f"Values to set V: \n{V}\n")

# Assing V to S region of T
S.assign(V)
---
<tf.Variable 'UnreadVariable' shape=(4, 5) dtype=int32, numpy=
array([[ 0,  1,  1,  3,  4],
       [ 5,  1,  1,  8,  9],
       [10,  1,  1, 13, 14],
       [15, 16, 17, 18, 19]], dtype=int32)>

据我了解,上述行为 是预期的 或至少 不是错误 。如错误所述,没有名为 assign in tf. Tensor (EagerTensor for eager execution) but there is in tf. Variable. And generally, tf. slice returns a tensor as its output and thus it doesn't possess assign 属性的 属性。

AttributeError: 'tensorflow.python.framework.ops.EagerTensor' object has no attribute 'assign' 

但是当我们像切片一样np并用它来修改原始tf. Variable时,它会无缝工作。


可能的解决方案

解决方法是使用 tf.strided_slice instead of tf.slice。如果我们查看它的源代码,我们会看到,它带有 var 参数,它是一个对应于 input_

的变量
@tf_export("strided_slice")
@dispatch.add_dispatch_support
def strided_slice(input_,
                  begin,
                  end,
                  ..........
                  var=None,
                  name=None):

当我们为var传递一个基本对应于input_的参数时,它会调用其中定义的assign function

def assign(val, name=None):
      """Closure that holds all the arguments to create an assignment."""

      if var is None:
        raise ValueError("Sliced assignment is only supported for variables")
      else:
        if name is None:
          name = parent_name + "_assign"

        return var._strided_slice_assign(
            begin=begin,
            end=end,
            strides=strides,
            value=val,
            name=name,
            begin_mask=begin_mask,
            end_mask=end_mask,
            ellipsis_mask=ellipsis_mask,
            new_axis_mask=new_axis_mask,
            shrink_axis_mask=shrink_axis_mask)

因此,当我们在 tf.strided_slice 中传递 var 时,它将 return 一个可分配的对象。


代码

这里是完整的工作代码供参考。

import tensorflow as tf 
print(tf.__version__)

TYPE = tf.int32
N = 4
D = 5
shape = (N,D)

# Target to update
Y = tf.Variable(
    initial_value=tf.reshape(tf.range(N*D,dtype=TYPE), shape=shape),
    trainable=True
)
Y

2.4.1
<tf.Variable 'Variable:0' shape=(4, 5) dtype=int32, numpy=
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]], dtype=int32)>

现在,我们使用 tf.stried_slice 而不是 tf.slice

S = tf.strided_slice(
    Y, 
    begin = [0, 1],
    end   = [3, 3],
    var   = Y,
    name  ='slice_op'
)
S

<tf.Tensor: shape=(3, 2), dtype=int32, numpy=
array([[ 1,  2],
       [ 6,  7],
       [11, 12]], dtype=int32)>

更新没有归因错误的变量。

# Values to set
V = tf.ones(shape=tf.shape(S), dtype=TYPE)
print(V)
print()

# Assing V to S region of T
S.assign(V)

tf.Tensor(
[[1 1]
 [1 1]
 [1 1]], shape=(3, 2), dtype=int32)

<tf.Variable 'UnreadVariable' shape=(4, 5) dtype=int32, numpy=
array([[ 0,  1,  1,  3,  4],
       [ 5,  1,  1,  8,  9],
       [10,  1,  1, 13, 14],
       [15, 16, 17, 18, 19]], dtype=int32)>

像切片一样使用np

# slicing 
S = Y[
    0:3,           
    1:3            
]                  
S
<tf.Tensor: shape=(3, 2), dtype=int32, numpy=
array([[ 1,  2],
       [ 6,  7],
       [11, 12]], dtype=int32)>

# Values to set
V = tf.ones(shape=tf.shape(S), dtype=TYPE)
print(V)
# Assing V to S region of T
S.assign(V)
tf.Tensor(
[[1 1]
 [1 1]
 [1 1]], shape=(3, 2), dtype=int32)
<tf.Variable 'UnreadVariable' shape=(4, 5) dtype=int32, numpy=
array([[ 0,  1,  1,  3,  4],
       [ 5,  1,  1,  8,  9],
       [10,  1,  1, 13, 14],
       [15, 16, 17, 18, 19]], dtype=int32)>

材质

  • .