维度的意义
tensorflow中经常对tensor的维度和维度操作感觉特别晕,今天做一个整理。
首先tensor 的维度从外向里依次是第0维,第1维,第2维。。。。
例如
aaa = tf.constant([[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]
最外层的括号里有2个元素[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]和[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]
上面这2个元素分别有3个元素
最里面的括号中有6个元素。所以这个tensor的形状就是[2,3,6],判断一个tensor形状,只需要具体到一个元素就可以了。
获取tensor的维度
获取tensor维度有2种方法
tensor.get_shape(),这个方法返回一个tensorshape 对象。可以通过as_list() 直接转化为一个list
tf.shape(tensor) 这个方法返回一个形状的tensor,只能通过session run的方法获取其中的数值。
aaa = tf.constant([[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]
print(aaa.get_shape().as_list())
with tf.session() as sess:
sess.run(tf.shape(aaa))
在实际使用中可以根据需要自行选取。
然后接下来就是对维度的操作。
对维度的操作,虽然非常多,倒也有规律可循。 维度操作中经常有一个必须的参数,就是对第几维进行操作,
例如
tf.reduce_sum(input,[0])
tf.tile(input,[2])
tf.concat(input,[1])
分别对第0,2,1维操作。所以有这么一个规律:
对第几维操作,第几维就会发生变化
对第几维操作,第几维就会发生变化
对第几维操作,第几维就会发生变化
这个变化有可能是维度的消失,维度长度的变化,维度的插入等。
记住这个规律,就能屡试不爽。下面来看几个命令
tf.expand_dims扩展维度
扩展维度就是在第n个维度括号的外面增加一个维度,即把第n维的变成一个元素。
如果变换之前的维度是[a,b,c ..... ]
那么tf.expand_dims(aaa,[0])) 之后就是[1,a,b,c ..... ]
那么tf.expand_dims(aaa,[1])) 之后就是[a,1,b,c ..... ]
这也符合刚才的规律,对哪个维度使用expand_dims,哪个维度就会插入1维。
例如:tf.expand_dims(aaa,[0]))就是在第0维外面加一个括号
hhh = tf.expand_dims(aaa,[0])
扩展前
[[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]
扩展后
【[[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]】
如果扩展第一维,那么需要第1维外面加一个括号,因为第一维有2个元素,所以2个元素都要加
hhh = tf.expand_dims(aaa,[1])
扩展前
[[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]
扩展后,为了方便了解,新增的维度括号用中文括号【】表示
[【[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]】,【[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]】]
tf.squeeze
去掉长度是1的维度,如果该维度长度不是1,则报错。例如
aaa = [[[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]]
hhh = tf.expand_dims(aaa,[0])
结果
[[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]
这也符合刚才的规律,aaaa原维度[1,2,3,6],新的维度[2,3,6],第0维度被删除。
tf.reduce_sum,tf.reduce_mean,tf.reduce_max,tf.reduce_min
这四个命令都非常类似,都是通过某种方法使维度降低,用法非常基础,就不解释了。
我们来看
aaa = tf.constant([[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]
kkk = tf.tile(aaa,[2,1,1]))
with tf.Session() as sess:
print(sess.run(kkk))
结果
[[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]]
原来形状维[2,3,6] ,结果的形状 [3,6] . 第0维消失了。那怎么样才能让第0维小时呢,通过对第0维的所有元素进行某种操作(例如求和,求平均,求最大,求最小)把第0维长度变成1,然后再把第0维删掉。
tf.tile(input)
把原来的tensor在某些维度上复制若干次。tile命令的参数和之前的几个命令略有不同,第二个参数是分别在每个维度复制几次。
tf.tile(input,[n1,n2,n3.....]) 表示把原tensor在第0维变成原来的n1倍,第1维变成原来的n2倍....。例如
aaa = [[[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]]
hhh = tf.tile(aaa,[2,1,1])
结果
[[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]]
在这段代码中,第0维长度变成了原来的2倍,第1维和第2维长度都无变化。所以最后的结果tensor形状是[4,3,6]。同样符合那个规律
tf.concat
2个tensor进行拼接,用法如下
tf.concat([tensor1,tensor2],axis)
要求2个拼接的tensor在拼接的维度尺寸可以不同,例如]4行9列和4行1列 [4,9] 和 [4,1] 可以在第1维进行拼接。但是非拼接的维度,形状必须相同。4行和5行的就拼不到一块
例如
aaa = [[[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]],[[1,1,1,1,1,1],[2,2,2,2,2,2],[3,3,3,3,3,3]]]]
kkk = tf.tile(aaa,[2,1,1])
lll = tf.concat([aaa,kkk],0)
结果
[[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]
[[1 1 1 1 1 1]
[2 2 2 2 2 2]
[3 3 3 3 3 3]]]
拼接后的形状为[6,3,6] 符合规律