如何理解 TensorFlow 中的静态形状和动态形状?
Posted
技术标签:
【中文标题】如何理解 TensorFlow 中的静态形状和动态形状?【英文标题】:How to understand static shape and dynamic shape in TensorFlow? 【发布时间】:2016-09-02 22:25:55 【问题描述】:在TensorFlow FAQ 中,它说:
在 TensorFlow 中,张量同时具有静态(推断)形状和 动态(真实)形状。静态形状可以使用 tf.Tensor.get_shape() 方法:这个形状是从 用于创建张量的操作,可能是部分操作 完全的。如果静态形状未完全定义,则动态形状 可以通过评估 tf.shape(t) 来确定张量 t 的大小。
但我仍然不能完全理解静态形状和动态形状之间的关系。有没有例子表明它们之间的差异?谢谢。
【问题讨论】:
【参考方案1】:有时张量的形状取决于在运行时计算的值。我们来看下面的例子,其中x
被定义为一个有四个元素的tf.placeholder()
向量:
x = tf.placeholder(tf.int32, shape=[4])
print x.get_shape()
# ==> '(4,)'
x.get_shape()
的值是x
的静态形状,(4,
) 表示它是一个长度为 4 的向量。现在让我们将tf.unique()
操作应用于x
y, _ = tf.unique(x)
print y.get_shape()
# ==> '(?,)'
(?,)
表示y
是一个未知长度的向量。为什么不为人知? tf.unique(x)
返回来自x
的唯一值,而x
的值是未知的,因为它是tf.placeholder()
,所以在你输入它之前它没有值。让我们看看如果你输入两个不同的值会发生什么:
sess = tf.Session()
print sess.run(y, feed_dict=x: [0, 1, 2, 3]).shape
# ==> '(4,)'
print sess.run(y, feed_dict=x: [0, 0, 0, 0]).shape
# ==> '(1,)'
希望这清楚地表明张量可以具有不同的静态和动态形状。动态形状始终是完全定义的——它没有?
尺寸——但静态形状可以不那么具体。这就是让 TensorFlow 支持像 tf.unique()
和 tf.dynamic_partition()
这样的操作的原因,它们可以具有可变大小的输出,并用于高级应用程序。
最后,tf.shape()
op 可用于获取张量的动态形状并将其用于 TensorFlow 计算:
z = tf.shape(y)
print sess.run(z, feed_dict=x: [0, 1, 2, 3])
# ==> [4]
print sess.run(z, feed_dict=x: [0, 0, 0, 0])
# ==> [1]
这是显示两者的示意图:
【讨论】:
我可以使用带有可学习层的动态形状吗?如果我使用较小的输入,权重会怎样? 通常需要静态知道可学习参数的形状,但输入可以具有可变的批量大小。【参考方案2】:在上面的答案中定义得很好,投票赞成。我经历了更多的观察,所以我想分享一下。
tf.Tensor.get_shape(),可用于使用创建它的操作来推断输出,这意味着我们可以在不使用 sess.run()(运行操作)的情况下推断它,正如名称所暗示的,静态形状. 例如,
c=tf.random_uniform([1,3,1,1])
是一个 tf.Tensor,我们想在代码中的任何一步知道它的形状,在运行图之前,所以我们可以使用
c.get_shape()
tf.Tensor.get_shape 不能动态(sess.run())的原因是因为输出类型 TensorShape 代替 tf.tensor,输出 TensorShape 限制了 sess.run() 的使用。
sess.run(c.get_shape())
如果我们这样做,我们会收到一个错误,即 TensorShape 的类型无效,它必须是张量/操作或字符串。
另一方面,动态形状需要通过 sess.run() 运行操作来获取形状
sess.run(tf.shape(c))
输出:数组([1, 3, 1, 1])
或
sess.run(c).shape
(1, 3, 1, 1) # 元组
希望它有助于澄清 tensorflow 概念。
【讨论】:
【参考方案3】:Tensorflow 2.0 兼容答案:提及 mrry 在他的答案中指定的代码,在 Tensorflow Version 2.x (> 2.0)
中,为了社区的利益。
# Installing the Tensorflow Version 2.1
!pip install tensorflow==2.1
# If we don't Disable the Eager Execution, usage of Placeholder results in RunTimeError
tf.compat.v1.disable_eager_execution()
x = tf.compat.v1.placeholder(tf.int32, shape=[4])
print(x.get_shape())
# ==> 4
y, _ = tf.unique(x)
print(y.get_shape())
# ==> (None,)
sess = tf.compat.v1.Session()
print(sess.run(y, feed_dict=x: [0, 1, 2, 3]).shape)
# ==> '(4,)'
print(sess.run(y, feed_dict=x: [0, 0, 0, 0]).shape)
# ==> '(1,)'
z = tf.shape(y)
print(sess.run(z, feed_dict=x: [0, 1, 2, 3]))
# ==> [4]
print(sess.run(z, feed_dict=x: [0, 0, 0, 0]))
# ==> [1]
【讨论】:
有没有办法在 tensorflow 2.0 和 tf.keras中有 inferred shape 和 dynamic shape >以上是关于如何理解 TensorFlow 中的静态形状和动态形状?的主要内容,如果未能解决你的问题,请参考以下文章
tensorflow中张量(tensor)的属性——维数(阶)形状和数据类型
TensorFlow SparseCategoricalCrossentropy 是如何工作的?