这篇教程是翻译 Morgan 写的TensorFlow教程,CoderPai 已经授权翻译。
TensorFlow: Shapes and dynamic dimensions
这篇教程主要去理解一些关于Tensorflow的维度形状概念,希望在后续的教程中能帮你节约调试程序的时间 :) 。
那么什么是张量呢?
简单的说,张量就是一个拥有 n 维度的数组,并且其中的值都拥有相同的类型,比如整型,浮点型,布尔型等等。
张量可以用我们所说的形状来描述:我们用列表(或元祖)来描述我们的张量的每个维度的大小,例如:
1. 一个 n 维度的张量可以表示为:(D_0, D_1, D_2, …, D_n-1)
2. 一个 W x H 大小的张量(一般我们成为矩阵):(W, H)
3. 一个尺度是 W 的张量(一般我们称之为向量):(W, )
4. 一个简单的标量(或者与之等价的):()或者(1, )
注意: D_*,W,H 都是整型。
关于一维张量的一些注意:我们不可能通过观察 TF 中的矢量形状来确定矢量是行向量还是列向量。实际上这并不重要。有关更多信息,请查看这个[Stackoverflow](Difference between numpy.array shape (R, 1) and (R,))关于Numpy堆栈溢出的回答(这大致与TensorFlow表示法相同):
在 TF 中,一个张量如下表述:
“`
my_tensor = tf.constant(0., shape=[6,3,7])
print(my_tensor) # -> Tensor(“Const_1:0”, shape=(6, 3, 7), dtype=float32)
“`
我们可以发现,一个张量应该包含如下内容:
* 一个名字,它用于键值对的存储,用于后续的检索:Const: 0
* 一个形状描述, 描述数据的每一维度的元素个数:(6,3,7)
* 数据类型,比如 float32
现在,我们来谈这篇文章的重点:TensorFlow中张量的两种形状!一种是静态形状,另一种是动态形状。
静态形状和动态形状
静态形状
静态维度是指当你在创建一个张量或者由操作推导出一个张量时,这个张量的维度是确定的。它是一个元祖或者列表。
TensorFlow将尽最大努力去猜测不同张量的形状(在不同操作之间),但是它不会总是能够做到这一点。特别是如果您开始用未知维度定义的占位符执行操作(例如,当你使用批操作时)。
要在代码中使用静态形状(访问/更改),你将使用Tensor本身的不同函数,并在名称中使用下划线:
注意:静态形状是非常有用的,特别是当你调试的时候,你可以使用 `print` 函数将张量形状进行打印,从而判断自己的设计是否正确。
动态形状
当你在运行你的图时,动态形状才是真正用到的。这种形状是一种描述原始张量在执行过程中的一种张量。
如果你定义了一个没有标明具体维度的占位符,即用`None`表示维度,那么当你将值输入到占位符时,这些无维度就是一个具体的值,并且任何一个依赖这个占位符的变量,都将使用这个值。
如果在代码中操作动态形状(访问/更改),你将使用主作用域中的不同函数,并且名称中没有下划线:
注意:当你要处理一些动态维度时,那么使用动态形状是非常方便的。
一个真实的使用案例:RNN
让我们需要去构建一个RNN时,我们将输入一个动态的形状,这个形状将去处理任何长度的输入数据。
在训练阶段,我们将使用动态占位符来定义批处理大小 `batch_size`
,然后我们使用 TensorFlow API 来创建一个 LSTM 网络。那么,你最终将得到如下这样的:
你现在需要去初始化一个状态,比如 `init_state = cell.zero_state(batch_size, tf.float32) … `。
但是,批处理大小 `batch_size` 具体应该等于多少呢?而且我们想要它是一个动态的数值,那么我们应该怎么选择呢?如果你阅读源码,那么就会发现 TensorFlow 允许很多不同的数据类型,如下:
“`
Args:
batch_size: int, float, or unit Tensor representing the batch size.
“`
`int`和`float`不能被使用,因为当你定义你的图时,你实际上是不知道 `batch_size` 的具体大小是多少的。这很重要。
最有意思的地方是最后一种类型: `unit Tensor representing the batch size` ,如果你在看原始文档,那么你会发现 `unit Tensor` 就是一个零维度的张量,也就是说,它就是一个标量。那么,你是怎么得到标量张量呢?
如果你在静态形状上尝试得到,你们如下:
“`
my_tensor = tf.placeholder(tf.float32)
batch_size = my_tensor.get_shape()[0] # Dimension(None)
print(batch_size)
# -> ?
“`
`batch_size` 将返回 `Dimension(None)` 类型,即输出是 `?` 。这个类型只能用作占位符的维度。
其实,你实际想做的是在图中使用这个动态参数 `batch_size` ,那么你必须使用动态形状,如下:
“`
my_tensor = tf.placeholder(tf.float32)
batch_size = tf.shape(my_tensor)[0] # <tf.Tensor ‘strided_slice:0’ shape=() dtype=int32>
print(batch_size)
# -> Tensor(“strided_slice:0”, shape=(), dtype=int32)
“`
你看,此时 `batch_size` 在 TensorFlow 中是零维度的张量类型来描述的。
总结
* 在调试时,使用静态形状
* 除了调试,都是用动态形状,尤其是当你在吹未定义的维度时。
*注意:在使用RNN API时,TensorFlow非常在意将初始状态设置为 `zero_state` ,但是为什么需要手动定义这种方式?不解。*
你可能希望在运行图时,自己控制 `init_state` 的初始化状态。 那么你实际上可以使用 `feed_dict` 来为图中的任何变量提供初始变量,如下:
“`
sess.run(encoder_final_state, feed_dict={
my_placeholder: new_input,
init_state: previous_encoder_final_state
})
“`
现在,你可以动手写一下了。So easy!
————-
References:
https://github.com/metaflow-ai/blog/blob/master/tf-shape/TensorFlow%20faq
https://blog.metaflow.fr/shapes-and-dynamic-dimensions-in-tensorflow-7b1fe79be363#.r2ub6gs0n
—————————————————————————————————————————
如果觉得内容有用,帮助多多分享哦 :)
微信公众号:coerpai
—————————————————————————————————————————