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

Posted

技术标签:

【中文标题】Tensorflow 2 - tf.slice 及其 NumPy 切片语法不兼容的行为【英文标题】:Tensorflow 2 - tf.slice and its NumPy slice syntax incompatible behavior 【发布时间】:2021-07-26 09:40:12 【问题描述】:

问题

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

背景

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

使用 tf. 执行类似 NumPy 的张量切片。切片。

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

或者,您可以使用更 Pythonic 的语法。请注意,张量切片在开始-停止范围内均匀分布。

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: \nY\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: \nS\n")

# Values to set
V = tf.ones(shape=tf.shape(S), dtype=TYPE)
print(f"Values to set V: \nV\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: \nS\n")

# Values to set
V = tf.ones(shape=tf.shape(S), dtype=TYPE)
print(f"Values to set V: \nV\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)>

【问题讨论】:

【参考方案1】:

据我了解,上述行为是意料之中的或至少不是错误。正如错误所说,在tf. TensorEagerTensor 用于急切执行)没有属性称为assign,但tf. Variable 中有。通常,tf. slice 返回 张量 作为其输出,因此它不具有 assign 属性。

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

但是当我们对np 进行切片并使用它来修改原始tf. Variable 时,它可以无缝工作。


可能的解决方案

一种解决方法是使用tf.strided_slice 而不是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 时,它会返回一个可赋值对象。


代码

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

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)>

材料

tf.Variable - tf.Tensor。

【讨论】:

以上是关于Tensorflow 2 - tf.slice 及其 NumPy 切片语法不兼容的行为的主要内容,如果未能解决你的问题,请参考以下文章

了解 tensorflow 中 tf.slice 的参数以及为啥我不能更改它

tf.slice()函数详解(极详细)

像在 numpy 中一样使用 tf.slice 检测越界切片

在 Tensorflow 中删除张量的维度

tensorrt不支持:tf.unpack,tf.slice,tf.tile,tf.expand_dims,tf.fill,tf.cast,tf.floor_div,tf.range

TF 2.0 打印张量值