Effective TensorFlow Chapter 2:理解静态和动态的Tensor类型的形状

本文翻译自: 《Understanding static and dynamic shapes》, 如有侵权请联系删除,仅限于学术交流,请勿商用。如有谬误,请联系指出。


TensorFlow中,tensor有一个在图构建过程中就被决定的静态形状属性, 这个静态形状可以是没有明确加以说明的(underspecified),比如,我们可以定一个具有形状[None, 128]大小的tensor

1
2
import tensorflow as tf
a = tf.placeholder(tf.float32, [None, 128])

这意味着tensor的第一个维度可以是任何尺寸,这个将会在Session.run()中被动态定义。当然,你可以查询一个tensor的静态形状,如:

1
static_shape = a.shape.as_list()  # returns [None, 128]

为了得到一个tensor的动态形状,你可以调用tf.shape操作,这将会返回指定tensor的形状,如:

1
dynamic_shape = tf.shape(a)

tensor的静态形状可以通过方法Tensor_name.set_shape()设定,如:

1
2
a.set_shape([32, 128])  # static shape of a is [32, 128]
a.set_shape([None, 128]) # first dimension of a is determined dynamically

调用tf.reshape()方法,你可以动态地重塑一个tensor的形状,如:

1
a =  tf.reshape(a, [32, 128])

可以定义一个函数,当静态形状的时候返回其静态形状,当静态形状不存在时,返回其动态形状,如:

1
2
3
4
5
6
def get_shape(tensor):
static_shape = tensor.shape.as_list()
dynamic_shape = tf.unstack(tf.shape(tensor))
dims = [s[1] if s[0] is None else s[0]
for s in zip(static_shape, dynamic_shape)]
return dims

现在,如果我们需要将一个三阶的tensor转变为2阶的tensor,通过折叠(collapse)第二维和第三维成一个维度,我们可以通过我们刚才定义的get_shape()方法进行,如:

1
2
3
b = tf.placeholder(tf.float32, [None, 10, 32])
shape = get_shape(b)
b = tf.reshape(b, [shape[0], shape[1] * shape[2]])

注意到无论这个tensor的形状是静态指定的还是动态指定的,这个代码都是有效的。事实上,我们可以写出一个通用的reshape函数,用于折叠任意在列表中的维度(any list of dimensions):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import tensorflow as tf
import numpy as np

def reshape(tensor, dims_list):
shape = get_shape(tensor)
dims_prod = []
for dims in dims_list:
if isinstance(dims, int):
dims_prod.append(shape[dims])
elif all([isinstance(shape[d], int) for d in dims]):
dims_prod.append(np.prod([shape[d] for d in dims]))
else:
dims_prod.append(tf.prod([shape[d] for d in dims]))
tensor = tf.reshape(tensor, dims_prod)
return tensor

然后折叠第二个维度就变得特别简单了。

1
2
b = tf.placeholder(tf.float32, [None, 10, 32])
b = reshape(b, [0, [1, 2]])