tensorflow2.x 维度变换

一、张量的存储和视图(View)概念

张量的视图就是我们理解张量的方式,例如shape 为[2,4,4,3]的张量A,我们从逻辑上可以理解为2 张图片,每张图片4 行4 列,每个位置有RGB 3 个通道的数据;

张量的存储体现在张量在内存上保存为一段连续的内存区域,对于同样的存储,我们可以有不同的理解方式,比如上述A,我们可以在不改变张量的存储下,将张量A 理解为2 个样本,每个样本的特征为长度48 的向量。

在存储数据时,内存并不支持这个维度层级概念,只能以平铺方式按序写入内存,因此这种层级关系需要人为管理,也就是说,每个张量的存储顺序需要人为跟踪。

为了方便表达,我们把张量shape 中相对靠左侧的维度叫做大维度,shape 中相对靠右侧的维度叫做小维度,比如[2,4,4,3]的张量中,图片数量维度与通道数量相比,图片数量叫做大维度,通道数叫做小维度。
例如:在优先写入小维度的设定下,张量 [2,4,4,3] 的内存布局为
在这里插入图片描述

为了能够正确恢复出数据,必须保证张量的存储顺序与新视图的维度顺序一致
例如根据图片数量-行-列-通道初始视图保存的张量,按照图片数量-行-列-通道(𝑏 − ℎ −w − 𝑐)的顺序可以获得合法数据。

二、Reshape 操作

改变视图是神经网络中非常常见的操作,可以通过串联多个Reshape 操作来实现复杂逻辑,
但是在通过Reshape 改变视图时,必须始终记住张量的存储顺序,新视图的维度顺序不能与存储顺序相悖,否则需要通过交换维度操作将存储顺序同步过来。
例如,对于shape 为[4,32,32,3]的图片数据,通过Reshape 操作将shape 调整为[4,1024,3],此时视图的维度顺序为 𝑏 − 𝑝𝑖𝑥𝑒𝑙 − 𝑐,张量的存储顺序为 [𝑏, ℎ, w, 𝑐]。

在 TensorFlow 中,可以通过张量的ndim 和shape 成员属性获得张量的维度数和形状

#matrix为数据,其shape为[5, 28, 28, 3]
matrix = tf.random.normal([5, 28, 28, 3])
matrix_ndim = matrix.ndim #计算数据的维度
matrix_shape = matrix.shape #数据的形状

通过 tf.reshape(x,new_shape)将张量的视图任意的合法改变

#例如将shape为[5, 28, 28, 3]的数据转为shape为[5, 28*28, 3]的数据
matrix1 = tf.reshape(matrix, [5, -1, 3])
print("matrix1_shape:", matrix1.shape)
#例如将shape为[5, 28, 28, 3]的数据转为shape为[5, 28*28*3]的数据
matrix2 = tf.reshape(matrix, [5, -1])
print("matrix2_shape:", matrix2.shape)

当不知道填入什么数字合适时,可以选用 -1 来替代,由python通过其他值进行推算得知具体值
数据在创建时按着初始的维度顺序写入,改变张量的视图仅仅是改变了张量的理解方式,并不需要改变张量的存储顺序,张量只需要满足新视图的元素总量与存储区域大小相等即可

像上面:5 ∗ 28 ∗ 28 * 3 = 5 ∗ 784 ∗ 3 = 5 * 2352 总量没变;且张量的存储顺序始终没有改变

三、交换维度

以[𝑏, ℎ, w, 𝑐]转换到[𝑏, 𝑐, ℎ, w]为例,即[5, 28, 28, 3] -> [5, 3, 28, 28]

通过 tf.transpose(data, perm)函数完成维度交换操作

"""
变换维度
tf.transpose(data, perm)
data:数据
perm:新维度的顺序 List
"""

#[𝑏, ℎ, w, 𝑐]转换到[𝑏, 𝑐, ℎ, w]为例,即[5, 28, 28, 3] -> [5, 3, 28, 28]
matrix3 = tf.transpose(matrix, perm=[0, 3, 1, 2])
matrix3_shape = matrix3.shape
print("matrix3_shape:", matrix3_shape)

通过tf.transpose完成维度交换后,张量的存储顺序已经改变,视图也随之改变,后续的所有操作必须基于新的存续顺序进行

四、增删维度

1. 增加维度
增加一个长度为1 的维度即为给原有的数据增加一个新维度,维度长度为1,故数据并不需要改变,仅仅是改变数据的理解方式,因此它其实可以理解为改变视图的一种特殊方式
通过 tf.expand_dims(data, axis)可在指定的axis 轴前可以插入一个新的维度

"""
增加维度
tf.expand_dims(data, axis)
data:数据
axis:axis为正数时,表示在axis维度之前插入一个新维度;为负时,表示在axis前维度之后插入一个新的维度
"""
matrix4 = tf.expand_dims(matrix, axis= 1)
matrix4_shape = matrix4.shape#[5, 1, 28, 28, 3]

matrix5 = tf.expand_dims(matrix, axis= -1)
matrix5_shape = matrix5.shape#[5, 28, 28, 3, 1]

需要注意的是,tf.expand_dims 的axis 为正时,表示在当前维度之前插入一个新维度;为负时,表示当前维度之后插入一个新的维度。以[𝑏, ℎ, w, 𝑐]张量为例,不同axis 参数的实际插入位置如图所示:
在这里插入图片描述

2. 删除维度

增加维度的逆操作,与增加维度一样,删除维度只能删除长度为1 的维度,也不会改变张量的存储。

可以通过 tf.squeeze(data, axis)函数, axis 参数为待删除的维度的索引号
matrix6 = tf.squeeze(matrix5)#matrix5.shpae=[5, 28, 28, 3, 1]
matrix6_shape = matrix6.shape#matrix6.shpae=[5, 28, 28, 3]

matrix7 = tf.squeeze(matrix4, axis=1)#matrix4_shape: (5, 1, 28, 28, 3)
matrix7_shape = matrix7.shape#matrix7_shape: (5, 28, 28, 3)

如果不指定维度参数 axis,即 tf.squeeze(data),那么会默认删除所有长度为1的维度

五、数据复制

tf.tile(x, multiples)函数完成数据在指定维度上的复制操作,multiples 分别指定了每个维度上面的复制倍数

例如数据的shape为(5, 28, 28, 3),其维度为4, 则multiples的维度也应该为4
若multiples=[2, 1, 1, 1],即表示为在第0维度上复制一遍数据

matrix8 = tf.tile(matrix, multiples=[2, 2, 1, 1])
matrix8_shape = matrix8.shape#matrix8_shape: (10, 56, 28, 3)

六、Broadcasting(自动扩展)

Broadcasting 也叫广播机制(自动扩展也许更合适),它是一种轻量级张量复制的手段,在逻辑上扩展张量数据的形状,但是只要在需要时才会执行实际存储复制操作。对于大部分场景,Broadcasting 机制都能通过优化手段避免实际复制数据而完成逻辑运算,相对于tf.tile 函数,减少了大量计算代价

Broadcasting 机制的核心思想是普适性,即同一份数据能普遍适合于其他位置。在验证普适性之前, 需要将张量shape 靠右对齐,然后进行普适性判断:对于长度为1 的维度,默认这个数据普遍适合于当前维度的其他位置;对于不存在的维度,则在增加新维度后默认当前数据也是普适性于新维度的,从而可以扩展为更多维度数、其他长度的张量形状。

在这里插入图片描述
在这里插入图片描述
在 c 维度上,张量已经有2 个特征数据,新shape 对应维度长度为c(𝑐 ≠ 2,比如c=3),那么当前维度上的这2 个特征无法普适到其他长度,故不满足普适性原则,无法应用 Broadcasting 机制,将会触发错误

在进行张量运算时,有些运算可以在处理不同shape 的张量时,会隐式自动调用 Broadcasting 机制,如+,-,*,/等运算等,将参与运算的张量Broadcasting 成一个公共shape,再进行
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值