张量的维度伸缩与交换
张量中的轴方向
在 TensorFlow
中,通常情况下,张量具有多个维度,有时需要对维度进行操作,此时需要指明操作的维度,通常用 Axis
(轴)来表示。
对于一维张量而言,它只有一个轴,称为 axis 0
;对于二维张量而言,它有两个轴,一个为 axis 0
(表示行),另一个为 axis 1
(表示列);三维张量有三个轴,分别为 axis 0
(表示深度或样本数量),axis 1
(表示行) 和 axis 2
(表示列)。
张量维度的增加与删除
我们可以利用 tf.expand_dims()
方法来增加张量的维度:
import tensorflow as tf
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
a = tf.random.normal([5, 20, 7]) # 构建三维张量,维度为 5*20*7
print(a.shape) # 输出张量的形状
(5, 20, 7)
a = tf.expand_dims(a, axis=0) # 在第 0 维上添加一个维度
print(a.shape)
(1, 5, 20, 7)
我们也可以利用 tf.concat()
方法对多个张量进行合并:
b = tf.random.normal([1, 5, 20, 7])
c = tf.concat([a, b], axis=0) # 在第 0 维度上合并张量 a 和 b
print(c.shape)
(2, 5, 20, 7)
对于冗余的维度,我们可以利用 tf.squeeze()
方法来删除(降维):
a = tf.squeeze(a, axis=0) # 删除第 0 维
print(a.shape)
(5, 20, 7)
张量的合并、分割与复制
张量合并
有两种方法可以来实现张量的合并,一个是之前提到的 tf.concat()
方法,另一个是 tf.stack()
方法。
tf.concat()
方法实现的拼接操作不会创建新的维度;而 tf.stack()
方法会创建新的维度,并实现张量的拼接。
tf.concat()
方法的具体语法如下:
tf.concat(
values, # 拼接的张量
axis, # 在哪个轴上进行拼接
name='concat' # 操作名称
)
其中,values
是要拼接的张量;axis
是拼接的轴,表示在第几个维度上进行拼接,取值范围为 0 ~ n - 1
,n
为该张量的维度;name
是操作名称。
我们来看一个例子:
t1 = [[1, 2, 3], [4, 5, 6]]
t2 = [[7, 8, 9], [10, 11, 12]]
t3 = tf.concat([t1, t2], 0) # 张量拼接(垂直方向)
print(t3)
tf.Tensor(
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]], shape=(4, 3), dtype=int32)
上述代码实现了垂直方向上的张量拼接,我们也可以令 axis=1
来实现水平方向上的拼接:
t4 = tf.concat([t1, t2], 1) # 张量拼接(水平方向)
print(t4)
tf.Tensor(
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]], shape=(2, 6), dtype=int32)
axis
也可以为负值,-1
表示倒数第一个轴,-2
表示倒数第二个轴,以此类推。
tf.stack(tensors, axis)
方法可以合并多个张量,并且创建新的维度,但需要合并的张量的维度必须相同,如下所示:
m = tf.stack([t1, t2], axis=0)
print(m)
tf.Tensor(
[[[ 1 2 3]
[ 4 5 6]]
[[ 7 8 9]
[10 11 12]]], shape=(2, 2, 3), dtype=int32)
张量分割
tf.split()
方法可以用来实现张量的分割,它的语法如下:
tf.split(
value, # 要分割的张量
num_or_size_splits, # 分割方案
axis=0, # 在哪个轴上进行分割
name='split' # 操作名称
)
其中:
value
表示要分割的张量。num_or_size_splits
表示分割方案,可以是单个数值,当num_or_size_splits
为5
时,表示将张量等分为5
份;也可以是列表,例如[1, 2, 2]
,表示将张量分为3
份(列表的长度),长度分别为1
,2
和2
(列表的元素)。axis
表示要在哪个轴(维度)上进行分割,默认为0
。name
表示操作的名称。
我们来看一个例子:
scores = tf.random.normal([5, 3, 2])
result = tf.split(scores, num_or_size_splits=5) # 将张量等分为 5 份
print(result)
[<tf.Tensor: shape=(1, 3, 2), dtype=float32, numpy=
array([[[ 0.04288249, -0.19643487],
[ 1.1040841 , 0.37403035],
[ 0.2968676 , 0.40126628]]], dtype=float32)>, <tf.Tensor: shape=(1, 3, 2), dtype=float32, numpy=
array([[[ 1.4905819 , 1.072397 ],
[-0.7560552 , 0.63848513],
[-0.26322153, 1.9187034 ]]], dtype=float32)>, <tf.Tensor: shape=(1, 3, 2), dtype=float32, numpy=
array([[[0.2913646 , 0.57758117],
[0.36280274, 1.2159098 ],
[0.07694758, 1.2456622 ]]], dtype=float32)>, <tf.Tensor: shape=(1, 3, 2), dtype=float32, numpy=
array([[[ 0.3552603 , 1.5969112 ],
[-0.60505086, -0.02659673],
[-0.52903736, -1.3919442 ]]], dtype=float32)>, <tf.Tensor: shape=(1, 3, 2), dtype=float32, numpy=
array([[[-0.6123017 , 1.624879 ],
[ 1.9365473 , 1.8205245 ],
[ 0.15557216, 0.03250924]]], dtype=float32)>]
我们再利用列表 [1, 2, 2]
在第 0
个维度上来实现张量的分割:
result_2 = tf.split(scores, num_or_size_splits=[1, 2, 2], axis=0) # 将张量等分为 1 份,2 份,2 份
print(result_2)
[<tf.Tensor: shape=(1, 3, 2), dtype=float32, numpy=
array([[[ 0.04288249, -0.19643487],
[ 1.1040841 , 0.37403035],
[ 0.2968676 , 0.40126628]]], dtype=float32)>, <tf.Tensor: shape=(2, 3, 2), dtype=float32, numpy=
array([[[ 1.4905819 , 1.072397 ],
[-0.7560552 , 0.63848513],
[-0.26322153, 1.9187034 ]],
[[ 0.2913646 , 0.57758117],
[ 0.36280274, 1.2159098 ],
[ 0.07694758, 1.2456622 ]]], dtype=float32)>, <tf.Tensor: shape=(2, 3, 2), dtype=float32, numpy=
array([[[ 0.3552603 , 1.5969112 ],
[-0.60505086, -0.02659673],
[-0.52903736, -1.3919442 ]],
[[-0.6123017 , 1.624879 ],
[ 1.9365473 , 1.8205245 ],
[ 0.15557216, 0.03250924]]], dtype=float32)>]