对于Tensor的常见操作无非就是变化一下shape,做个转置,padding一下,取其中固定的维度的信息,增加个维度,减少各维度之类的,具体的分为7个接口,分别如下:
1. tf.reshape
在向量运算时,难免向量的形状跟要求不一致,这个时候需要reshape操作改变向量的形状,该操作只修改各个维度的大小,不修改向量中数据的顺序,比如一个向量a = [1, 2, 3, 4],它的shape=(4, ),如果想要修改成shape=(2, 2),那么a = [[1, 2], [3, 4],而不会变成a = [[1, 3], [2, 4]]。我们经常看到代码中会出现shape = [-1, 2]这种里面出现一个值-1,它表示该维度的值会有其他维度的结果计算得出,shape中可以出现并且仅能出现一个-1值,而且shape中各个维度的数值必须是整数。
# 参数的含义
tf.reshape(tensor, shape, name=None)
# 1. tensor: 要修改维度信息的tensor,必填项
# 2. shape: 修改后各个维度的大小,它的乘积必须与原tensor的各个维度乘积相等,必填项
# 3. name: 给这个op取个名字,一般来说都不用写
a = tf.range(24)
b = tf.reshape(a, shape=[-1, 2])
c = tf.reshape(a, shape=[2, 3, -1])
d = tf.reshape(a, shape=[2, 3, 4])
e = tf.reshape(a, shape=[-1, ])
f = tf.reshape(a, shape=[-1, 7]) # 这条语句会报错,因为如果a的第二个维度为7,那么第一个维度不能是整数
print (b) # Tensor("Reshape:0", shape=(12, 2), dtype=int32)
print (c) # Tensor("Reshape_1:0", shape=(2, 3, 4), dtype=int32) 可以看到c和d的各个维度值一样
print (d) # Tensor("Reshape_2:0", shape=(2, 3, 4), dtype=int32)
print (e) # Tensor("Reshape_3:0", shape=(24,), dtype=int32)
with tf.Session() as sess:
print (sess.run(a))
print (sess.run(b))
==============================================================================
a的值:[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
b的值:
[[ 0 1]
[ 2 3]
[ 4 5]
[ 6 7]
[ 8 9]
[10 11]
[12 13]
[14 15]
[16 17]
[18 19]
[20 21]
[22 23]]
2、 tf.pad
# 常用的参数含义
tf.pad(tensor, paddings, mode="CONSTANT", name=None)
# 1. tensor:要pad的tensor是什么
# 2. paddings:它的shape=[n,2],其中n表示tensor的rank,2表示在每一维度的前侧和后侧各padding多少次
# 3. mode:padding的方式,一共有3中选择,‘CONSTANT’常数, ‘REFLECT’映射, ‘SYMMETRIC’对称
# 4. name: 给这个op取个名字,一般不用设置
# 最常用的padding mode就是‘CONSTANT’,该mode padding的值为0
这里接口给出了官方的注释:
This operation pads a `tensor` according to the `paddings` you specify. `paddings` is an integer tensor with shape `[n, 2]`, where n is the rank of `tensor`. For each dimension D of `input`, `paddings[D, 0]` indicates how many values to add before the contents of `tensor` in that dimension, and `paddings[D, 1]` indicates how many values to add after the contents of `tensor` in that dimension. If `mode` is "REFLECT" then both `paddings[D, 0]` and `paddings[D, 1]` must be no greater than `tensor.dim_size(D) - 1`. If `mode` is "SYMMETRIC" then both `paddings[D, 0]` and `paddings[D, 1]` must be no greater than `tensor.dim_size(D)`. The padded size of each dimension D of the output is: `paddings[D, 0] + tensor.dim_size(D) + paddings[D, 1]`也就是说参数padding的shape=[n, 2], n表示rank,对每一个维度来说,第一个数表示有多少个数要放在这一维之前,第二个数表示有多少个数放在这一维之后。
如果mode=‘REFLECT’,那么这里的padding要求不能高于要padding的那一维度的size-1。
如果mode=‘SYMMETRIC’,那么这里的padding要求不能高于要padding的那一维度的size。
最后padding之后tensor的shape=paddings[D, 0] + tensor.dim_size(D) + paddings[D, 1]
下面举例说明:
# CONSTANT
a = tf.constant([[1, 2], [4, 5]])
b = tf.pad(a, paddings=[[0, 1], [2, 3]],mode='CONSTANT')
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(a.get_shape().as_list()) # 打印[2, 2]
print(b.get_shape().as_list()) # 打印[3, 7],其中3 = 0+2+1; 7 = 2+2+3
print(sess.run(b))
# 打印 b = [[0 0 1 2 0 0 0]
# [0 0 4 5 0 0 0]
# [0 0 0 0 0 0 0]]
# REFLECT
a = tf.constant([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = tf.pad(a, paddings=[[0, 1], [2, 1]],mode='REFLECT')
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(a.get_shape().as_list()) # 打印[3, 3]
print(b.get_shape().as_list()) # 打印[4, 6]
print(sess.run(b))
# 打印 b = [[3 2 1 2 3 2]
# [6 5 4 5 6 5]
# [9 8 7 8 9 8]
# [6 5 4 5 6 5]]
# 过程是对于第一维上面添加0行,下面添加1行,添加的原则是从倒数第二行开始往上,上面倒数第二行是[4,5,6],如果添加1行就添加这个行,如果添加2行,那么就继续往上是[1,2,3]。
# 所以,经过padding [0,1]之后,矩阵变成:
# [[1, 2, 3] [[3, 2, 1, 2, 3]
# [4, 5, 6] 同理在经过padding=[[0,1],[2,1]]中的2之后矩阵变成: [6, 5, 4, 5, 6]
# [7, 8, 9] [9, 8, 7, 8, 9]
# [4, 5, 6]] [6, 5, 4, 5, 6]]
#
# 最后经过padding=[[0,1],[2,1]]的最后一个之后变成:
# [[3 2 1 2 3 2]
# [6 5 4 5 6 5]
# [9 8 7 8 9 8]
# [6 5 4 5 6 5]]
a = tf.constant([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = tf.pad(a, paddings=[[0, 3], [2, 1]],mode='SYMMETRIC')
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(a.get_shape().as_list()) # 打印[3, 3]
print(b.get_shape().as_list()) # 打印[6, 6]
print(sess.run(b))
# 打印 b = [[2 1 1 2 3 3]
# [5 4 4 5 6 6]
# [8 7 7 8 9 9]
# [8 7 7 8 9 9]
# [5 4 4 5 6 6]
# [2 1 1 2 3 3]]
# 方法跟REFLECT映射相似
通过上面的例子可以看出,mode=CONSTANT,就是根据padding的数值添加0,mode=REFLECT和SYMMETRIC的添加数据的方式类似,前者是以最后一行作为镜面,镜面两侧的数据是对应的,后者是没有明显的一行作为镜面,从添加数据的第一行开始于之前的数据形成对应,这也是为什么文档中说明,如果选择REFLECT方式,那么你能添加的最多的行数就是那个维度size-1 (-1是因为有一行做了镜面的作用)。
3. tf.slice
根据官方文档的解释,该接口的功能是:提取tensor的一个slice,这个slice从begin开始,大小是size,所以,begin可以使0,但size最小值是1。最后提取出来的这个slice的shape就等于size。要求begin和size必须是int32或者int64类型。举例说明:
a = tf.constant([[[1, 1, 1], [2, 2, 2]],
[[3, 3, 3], [4, 4, 4]],
[[5, 5, 5], [6, 6, 6]]])
b = tf.slice(a, [1, 0, 0], [1, 1, 3])
c = tf.slice(a, [1, 0, 0], [1, 2, 3])
d = tf.slice(a, [1, 0, 0], [2, 1, 3])
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(b))
print(sess.run(c))
print(sess.run(d))
# 打印:
# b = [[[3 3 3]]]
# c = [[[3 3 3]
# [4 4 4]]]
# c = [[[3 3 3]]
# [[5 5 5]]]
4. tf.transpose
def transpose(a, perm=None, name="transpose"):
其中参数中的perm是英文permute的缩写,意思是交换...的顺序。
所以此接口的含义就是交换Tensor的各个维度的顺序。如果参数perm没设置,则默认是perm=[n-1,...,0],其中n表示tensor的rank
a = tf.constant([[1, 2, 3], [4, 5, 6]])
b = tf.transpose(a)
c = tf.transpose(a, perm=[1, 0])
a1 = tf.constant([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
b1 = tf.transpose(a1, perm=[0, 2, 1])
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(b))
print(sess.run(c))
print(sess.run(b1))
# b = c = [[1 4]
# [2 5]
# [3 6]]
# b1 = [[[ 1 4] b1的第0个维度没变,将第2个维度和第3个维度交换了,
# [ 2 5] 所以,相当于将[[1, 2, 3], [4, 5, 6]]转置,
# [ 3 6]] 将[[7, 8, 9], [10, 11, 12]]转置
#
# [[ 7 10]
# [ 8 11]
# [ 9 12]]]
5. tf.tile
def tile(input, multiples, name=None):
tf.tile表示对input这个tensor每个维度扩展multiples倍数,multiples是一个一维的数组,size等于input的rank,值必须要是整数。
a = tf.constant([[1, 2, 3], [4, 5, 6]])
b = tf.tile(a, [2, 3])
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(b))
# 首先对a的axis=0扩展2倍,变成
# [[1 2 3]
# [4 5 6]
# [1 2 3]
# [4 5 6]]
# 然后在对axis=1扩展3倍,变成
# [[1 2 3 1 2 3 1 2 3]
# [4 5 6 4 5 6 4 5 6]
# [1 2 3 1 2 3 1 2 3]
# [4 5 6 4 5 6 4 5 6]]
6. tf.expand_dims 和 tf.squeeze
这2个接口是一对,expand_dims用于在某个axis插入一个dimension=1的维度,而squeeze就是要删除所有的dimension=1的维度,但如果不是想删除所有dimension=1的维度,那么哪些维度不想删除,就在axis中列出来,举例说明:
a = tf.constant([[1, 2, 3], [4, 5, 6]])
b = tf.expand_dims(a, axis=1)
c = tf.expand_dims(a, axis=0)
d = tf.expand_dims(c, axis=3)
e = tf.squeeze(d)
f = tf.squeeze(d, axis=0)
g = tf.squeeze(d, axis=[0, 3])
print(a.get_shape().as_list()) # [2, 3]
print(b.get_shape().as_list()) # [2, 1, 3]
print(c.get_shape().as_list()) # [1, 2, 3]
print(d.get_shape().as_list()) # [1, 2, 3, 1]
print(e.get_shape().as_list()) # [2, 3]
print(f.get_shape().as_list()) # [2, 3, 1]
print(g.get_shape().as_list()) # [2, 3]