目录
神经网络搭建过程中,维度变换是最重要的操作之一,通过切换数据形式,从而满足不同场景的运算需求。本文对所有维度处理的方式进行了整理。
1. 张量变形(reshape)
在总数据量不变的情况下,将其变换为不同的组合形式,且将此数据平铺时排列顺序不改变
举个例子:
>>> import tensorflow as tf
>>> x = tf.range(24) # 生成一组连续整数向量
>>> x = tf.reshape(x, [2, 3, 4]) # 改变x的视图,得到3D张量
>>> x
<tf.Tensor: id=11, shape=(2, 3, 4), dtype=int32, numpy=
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])>
>>> y = tf.reshape(x, [3, 2, 2, 2]) # 将x变形为4D张量
>>> y
<tf.Tensor: id=13, shape=(3, 2, 2, 2), dtype=int32, numpy=
array([[[[ 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∗3∗4=3∗2∗2∗22*3*4 = 3*2*2*22∗3∗4=3∗2∗2∗2,总量没变;且张量的存储顺序始终没有改变,数据仍是按照0,1,2,...,230,1,2,...,230,1,2,...,23的顺序保存
2. 维度增减
2.1 维度增加(expand_dims)
增加一个长度为 1 的维度相当于给原有的数据添加一个新维度的概念,因为维度长度为 1,所以数据并没有改变
举个例子:
>>> x = tf.random.normal([5, 5]) # 建立正态随机数据
>>> x
<tf.Tensor: id=19, shape=(5, 5), dtype=float32, numpy=
array([[ 0.4133979 , 1.0672395 , 2.5727544 , -0.0975351 , 0.73670894],
[-1.429659 , -0.3630027 , -0.11651681, -1.108655 , 1.494843 ],
[ 2.884822 , -0.3095457 , -0.6037164 , 2.190964 , -0.38418463],
[ 1.1569734 , 0.1284009 , -0.24746007, 0.17708912, 0.4348358 ],
[ 0.06045027, -0.9997665 , -0.43566772, -0.56748384, -1.4990594 ]],
dtype=float32)>
>>> y = tf.expand_dims(x, axis=2) # 在特定位置增加维度
>>> y
<tf.Tensor: id=21, shape=(5, 5, 1), dtype=float32, numpy=
array([[[ 0.4133979 ],
[ 1.0672395 ],
[ 2.5727544 ],
[-0.0975351 ],
[ 0.73670894]],
[[-1.429659 ],
[-0.3630027 ],
[-0.11651681],
[-1.108655 ],
[ 1.494843 ]],
[[ 2.884822 ],
[-0.3095457 ],
[-0.6037164 ],
[ 2.190964 ],
[-0.38418463]],
[[ 1.1569734 ],
[ 0.1284009 ],
[-0.24746007],
[ 0.17708912],
[ 0.4348358 ]],
[[ 0.06045027],
[-0.9997665 ],
[-0.43566772],
[-0.56748384],
[-1.4990594 ]]], dtype=float32)>
插入一个新维度后,数据的存储顺序并没有变化,只改变了数据的视图
2.2 维度删除(squeeze)
是增加维度的逆操作,只能删除长度为 1 的维度,也不会改变张量的存储
举个例子(接上一节数据):
>>> z = tf.squeeze(y, axis=2)
>>> z
<tf.Tensor: id=22, shape=(5, 5), dtype=float32, numpy=
array([[ 0.4133979 , 1.0672395 , 2.5727544 , -0.0975351 , 0.73670894],
[-1.429659 , -0.3630027 , -0.11651681, -1.108655 , 1.494843 ],
[ 2.884822 , -0.3095457 , -0.6037164 , 2.190964 , -0.38418463],
[ 1.1569734 , 0.1284009 , -0.24746007, 0.17708912, 0.4348358 ],
[ 0.06045027, -0.9997665 , -0.43566772, -0.56748384, -1.4990594 ]],
dtype=float32)>
如果不指定维度参数 axis,那么它会默认删除所有长度为 1 的维度,例如:
>>> w = tf.squeeze(y)
>>> w
<tf.Tensor: id=23, shape=(5, 5), dtype=float32, numpy=
array([[ 0.4133979 , 1.0672395 , 2.5727544 , -0.0975351 , 0.73670894],
[-1.429659 , -0.3630027 , -0.11651681, -1.108655 , 1.494843 ],
[ 2.884822 , -0.3095457 , -0.6037164 , 2.190964 , -0.38418463],
[ 1.1569734 , 0.1284009 , -0.24746007, 0.17708912, 0.4348358 ],
[ 0.06045027, -0.9997665 , -0.43566772, -0.56748384, -1.4990594 ]],
dtype=float32)>
3. 维度交换(transpose)
前面的操作都不会影响张量的存储,而交换维度操作可以改变张量的存储顺序,同时也改变了张量的视图
举个例子:
>>> x = tf.random.normal([2, 3])
>>> x
<tf.Tensor: id=44, shape=(2, 3), dtype=float32, numpy=
array([[ 1.5357795 , -1.3758559 , 0.6500945 ],
[-0.6790089 , 0.04826049, 0.8853354 ]], dtype=float32)>
>>> y = tf.transpose(x, perm=[1, 0]) # 将0维度与1维度互换
>>> y
<tf.Tensor: id=46, shape=(3, 2), dtype=float32, numpy=
array([[ 1.5357795 , -0.6790089 ],
[-1.3758559 , 0.04826049],
[ 0.6500945 , 0.8853354 ]], dtype=float32)>
4. 张量合并
将多个张量在某个维度上合并为一个张量,可以使用拼接和堆叠操作实现,拼接操作并不会产生新的维度,仅在现有的维度上合并,而堆叠会创建新维度。
4.1 张量拼接(concat)
>>> import tensorflow as tf
>>> a = tf.constant([[1, 2, 3], [4, 5, 6]])
>>> b = tf.constant([[7, 8, 9], [10, 11, 12]])
>>> c1 = tf.concat([a, b], 0)
>>> c1
<tf.Tensor: id=3, shape=(4, 3), dtype=int32, numpy=
array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])>
>>> c2 = tf.concat([a, b], 1)
>>> c2
<tf.Tensor: id=7, shape=(2, 6), dtype=int32, numpy=
array([[ 1, 2, 3, 7, 8, 9],
[ 4, 5, 6, 10, 11, 12]])>
4.2 张量堆叠(stack)
>>> z1 = tf.stack([a, b]) # axis默认为 0
>>> z1
<tf.Tensor: id=12, shape=(2, 2, 3), dtype=int32, numpy=
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])>
>>> z2 = tf.stack([a, b],axis=-1)
>>> z2
<tf.Tensor: id=13, shape=(2, 3, 2), dtype=int32, numpy=
array([[[ 1, 7],
[ 2, 8],
[ 3, 9]],
[[ 4, 10],
[ 5, 11],
[ 6, 12]]])>
5. 张量分割(split)
>>> x = tf.random.normal([10,35,8])
>>> result1 = tf.split(x, num_or_size_splits=10, axis=0)
>>> len(result1)
10
>>> result1[0].shape
TensorShape([1, 35, 8])
>>> result2 = tf.split(x, num_or_size_splits=[4,2,2,2], axis=0)
>>> len(result2)
4
>>> result2[1].shape
TensorShape([2, 35, 8])