各位同学好,今天我和大家分享一下TensorFlow2.0中有关数学计算的相关操作,主要内容有:
(1) 改变维度:reshape();(2) 维度转置:transpose();(3) 增加维度:expand_dims();(4) 减少维度:squeeze();(5) 广播:broadcasting_to()
那我们开始吧。
1. 维度变换
假设一张图像有四个维度: [ batch, row, width, channel ]
batch:理解为几张图像
row:图像的行
width:图像的列
channel:通道
图像维度的表示形式也有如下几种:
[b,28,28] 图像b有28行28列
[b,28*28] 不考虑行列信息,考虑整个图像的像素
[b,2,14*28] 将图片分成上下两个部分
[b,28,28,1] 增加一个channel通道,和第一种一样,只是增加一个通道
都没有改变原图像的数据点个数,只是换了一种理解方式
1.1 重新塑造维度
改变维度: tf.reshape(tensor, shape)
根据指定的shape改变tensor的维度。不会改变原tensor的维度,需要有变量去接收。改变图像的shape时,需要保证改变后的图像和原图像的数据点个数相同。
# 创建一个四维正态分布tensor
a = tf.random.normal([4,28,28,3])
# 查看形状
a.shape
# 按指定shape改变原形状
tf.reshape(a,[4,28*28,3]).shape
# 如果不想计算28*28可写成-1,-1这一项自动计算
tf.reshape(a,[4,-1,3]).shape
对创建的四维tensor改变形状,指定形状为 [4,28*28,3],这三个轴可以理解为 [图像, 像素, 通道],该变换,抹除了行和列的概念,变成784个像素去考虑,这时图像失去了二维信息。
如果不想计算28*28的值,还可以使用 '-1' 代替计算,函数碰到-1会自动计算两个数的乘积,注意,一个函数中只能出现一个-1,不然计算机不知道怎么去分配。

将原tensor从[4, 28, 28, 3],转换到 [4, 28*28*3],抹除行和列的概念,同时抹除通道的概念。将这种shape理解为[图像, 数据点],即有4幅图像,每幅图像有2352个数据点。若不想计算28*28*3,可以用-1代替计算。
a = tf.random.normal([4,28,28,3])
a.shape # 查看形状
# 抹除通道概念。理解为4个图像,每个图象有2352个数据点
tf.reshape(a,[4,28*28*3]).shape
# 用-1代替计算
tf.reshape(a,[4,-1]).shape

1.2 重新排列维度
转置: tf.transpose(tensor, perm)
tensor代表需要转置的变量,perm代表重新排列尺寸,不指定perm,默认为全部转置。
该方法会直接改变原图像的尺寸,不需要新变量去接收。
# 定义一个四维tensor
a = tf.random.normal([4,3,2,1])
a.shape
# 不传参数,全部转置
tf.transpose(a).shape
# 指定参数,新shape的0维度放原来的0维度,新shape的2维度放原来的3维度
tf.transpose(a,perm=[0,1,3,2]).shape
若不传参数,默认将tensor全部转置,如在矩阵中的行列互换,从三行两列变成两行三列。
若指定参数,perm=[0,1,3,2] 代表重新指定排列的顺序,新shape的第0个维度放原来的第0个维度,新shape的第1个维度放原来的第1个维度,新shape的第2个维度放原来的第3个维度,新shape的第3个维度放原来的第2个维度。也就是说从原来的[b,h,w,c] 变成 [b,h,c,w]

我们再看一例,加深理解。
# 从b维度转到[4,28,28,2]
b = tf.random.normal([28,2,4,28]) # [w,c,b,h]
tf.transpose(b,perm=[2,3,0,1]).shape # [b,h,w,c]

1.3 增加维度
指定位置增加一个维度: tf.expand_dims(tensor, axis)
tensor为需要增加维度的变量;axis为在哪个轴前面增加维度
不改变原始数据,需要有新变量去接收
# 4个班级35个学生8门课
a = tf.ones([4,35,8])
# 在最前面增加一个维度
tf.expand_dims(a,axis=0).shape
# 在指定位置前面添加一个维度,在课目前加一个维度
tf.expand_dims(a,axis=2).shape
axis=0代表指定第0个轴,即班级维度,在前面添加一个轴,理解为学校维度

如果轴axis为整数时,在指定轴前面增加一个新维度;若轴axis为负数时,在指定轴后面增加一个新维度。
b = tf.zeros([4,35,8]) # 下标为(-3,-2,-1)
# 理解为,在学生维度35后面增加一个维度
tf.expand_dims(b,axis=-2).shape
# 理解为,在课目维度8后面增加一个维度
tf.expand_dims(b,axis=-1).shape

1.4 减少维度
删除当前shape为1的维度: tf.squeeze(tensor, axis)
tensor代表需要减少维度的变量;axis代表指定删除哪一个轴的维度,如果存在多个shape为1的轴,不指定axis时,默认删除所有axis为1的轴。axis指定的轴必须是存在的且该轴的shape为1
不改变原数据,需要有新变量接收
# 创建一个五维tensor
a = tf.ones([1,2,1,1,3])
a.shape
# 不指定轴,默认删除所有维度为1的轴
tf.squeeze(a).shape
# 指定删除第0个轴
tf.squeeze(a,axis=0).shape
# 删除倒数第2个轴
tf.squeeze(a,axis=-2).shape

2. 广播
利用广播将原始tensor成倍增加: tf.broadcasting_to(tensor, shape)
tensor代表需要扩张的变量,shape代表需要扩张成什么样的
是一种张量维度扩张的手段,某一个维度重复n多次,但是没有真正的复制一个数据。是一种优化手段,没有复制数据,但是呈现出数据已经被扩张。
不会改变原数据,需要有变量来接收
举个例子说明一下:
计算时,如果a维度和b维度不一致时,在相应的维度上添加一个维度
a.shape = [4,16,16,32] 大维度 b.shape = [32] 小维度
先将小维度和大维度的右端对齐,如果小维度在大维度的相应维度上没有维度,就插入一个维度
b.shape = [1,1,1,32]
把插入的一维度扩张成相同的size,最终b通过broadcasting会输出
b.shape = [4,16,16,32]
broadcasting没有复制数据,把它理解成已经复制了,用于优化计算
小维度某个轴的shape为1时可以扩张成大维度对应轴的shape,如果为其他shape不可以扩张
如在计算a维度和b维度,a.shape = [4,32,14,14],b.shape = [2,32,14,14],在轴axis=0处,b的shape为2,不可以进行扩张,如果是1可以进行扩张,因此,这两个tensor不可以运算。
下面,我们看一下代码
#(1)不调用函数自动计算
# 定义一个四维tensor
a = tf.random.normal([4,32,32,3])
# 大维度加小维度,先对小维度扩张
(a + tf.random.normal([32,32,1])).shape
# 由于轴axis=1对应的shape不同且不为1,不能扩张
(a + tf.random.normal1([1,4,1,1])).shape # 不能计算
在计算时,我们选择可以不用调用广播的函数,计算时,计算机会自动执行广播的方法,对小维度扩张。因此,shape为[32,32,1]被扩张成[1,32,32,1],再变成[4,32,32,3],然后这两个tensor就可以进行计算了。
而第二个计算,shape为[1,4,1,1],由于小维度的axis=1对应的shape为4,大维度shape为32,无法进行广播扩张,只有shape是1时才能扩张。

我们也可以调用广播函数tf.broadcast_to(input, shape)进行扩张
# 定义2个需要计算的tensor
a = tf.random.normal([4,32,32,3])
b = tf.random.normal([32,32,1])
# 利用广播函数将b扩张成a,利用新变量x接收扩张后的结果
x = tf.broadcast_to(b,a.shape)
x.shape
对变量a和b计算前,需要将a和b的维度变成一样的,因此需要将小维度的b扩张成和变量a一样的维度。又因为广播函数不会改变原值,所以用新变量x来接收广播完成后的b。


862

被折叠的 条评论
为什么被折叠?



