常见的Tensor操作——tf.reshape; tf.pad; tf.slice; tf.transpose; tf.tile; tf.expand_dims; tf.squeeze

对于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]

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值