Tensorflow2.0之合并与分割

Tensorflow之合并与分割

合并

合并是指将多个张量在某个维度上合并为一个张量。以某学校班级成绩册数据为例,设张量A 保存了某学校1-4 号班级的成绩册,每个班级35 个学生,共8 门科目,则张量A的shape 为:[4,35,8];同样的方式,张量B 保存了剩下的6 个班级的成绩册,shape 为[6,35,8]。通过合并2 个成绩册,便可得到学校所有班级的成绩册张量C,shape 应为[10,35,8]。这就是张量合并的意义所在。张量的合并可以使用拼接(Concatenate)和堆叠(Stack)操作实现,拼接并不会产生新的维度,而堆叠会创建新维度。选择使用拼接还是堆叠操作来合并张量,取决于具体的场景是否需要创建新维度

拼接

在TensorFlow 中,可以通过tf.concat(tensors, axis),其中tensors 保存了所有需要合并的张量List,axis 指定需要合并的维度。回到上面的例子,这里班级维度索引号为0,即axis=0,合并张量A,B 如下:

In [1]:
a = tf.random.normal([4,35,8]) # 模拟成绩册A
b = tf.random.normal([6,35,8]) # 模拟成绩册B
tf.concat([a,b],axis=0) # 合并成绩册
Out[1]:
<tf.Tensor: id=13, shape=(10, 35, 8), dtype=float32, numpy=
array([[[ 1.95299834e-01, 6.87859178e-01, -5.80048323e-01, ...,
1.29430830e+00, 2.56610274e-01, -1.27798581e+00],
[ 4.29753691e-01, 9.11329567e-01, -4.47975427e-01, ...,

除了可以在班级维度上进行合并,还可以在其他维度上合并张量。考虑张量A 保存了所有班级所有学生的前4 门科目成绩,shape 为[10,35,4],张量B 保存了剩下的4 门科目成绩,shape 为[10,35,4],则可以合并shape 为[10,35,8]的总成绩册张量:

In [2]:
a = tf.random.normal([10,35,4])
b = tf.random.normal([10,35,4])
tf.concat([a,b],axis=2) # 在科目维度拼接
Out[2]:
<tf.Tensor: id=28, shape=(10, 35, 8), dtype=float32, numpy=array([[[-5.13509691e-01, -1.79707789e+00, 6.50747120e-01, ...,
2.58447856e-01, 8.47878829e-02, 4.13468748e-01],
[-1.17108583e+00, 1.93961406e+00, 1.27830813e-02, ...,

合并操作可以在任意的维度上进行,唯一的约束是非合并维度的长度必须一致。比如shape为[4,32,8]和shape 为[6,35,8]的张量则不能直接在班级维度上进行合并,因为学生数维度的长度并不一致,一个为32,另一个为35:

In [3]:
a = tf.random.normal([4,32,8])
b = tf.random.normal([6,35,8])
tf.concat([a,b],axis=0) # 非法拼接
Out[3]:
InvalidArgumentError: ConcatOp : Dimensions of inputs should match: shape[0]
= [4,32,8] vs. shape[1] = [6,35,8] [Op:ConcatV2] name: concat

堆叠

堆叠 tf.concat 直接在现有维度上面合并数据,并不会创建新的维度。如果在合并数据时,希望创建一个新的维度,则需要使用tf.stack 操作。考虑张量A 保存了某个班级的成绩册,shape 为[35,8],张量B 保存了另一个班级的成绩册,shape 为[35,8]。合并这2 个班级的数据时,需要创建一个新维度,定义为班级维度,新维度可以选择放置在任意位置,一般根据大小维度的经验法则,将较大概念的班级维度放置在学生维度之前,则合并后的张量的新shape 应为[2,35,8]。

使用 tf.stack(tensors, axis)可以合并多个张量tensors,其中axis 指定插入新维度的位置,axis 的用法与tf.expand_dims 的一致,当axis ≥ 0时,在axis 之前插入;当axis < 0时,在axis 之后插入新维度。例如shape 为[𝑏, 𝑐, ℎ, 𝑤]的张量,在不同位置通过stack 操作插入新维度,axis 参数对应的插入位置设置如图所示:

在这里插入图片描述
堆叠方式合并这2 个班级成绩册如下:

In [4]:
a = tf.random.normal([35,8])
b = tf.random.normal([35,8])
tf.stack([a,b],axis=0) # 堆叠合并为2 个班级
Out[4]:
<tf.Tensor: id=55, shape=(2, 35, 8), dtype=float32, numpy=
array([[[ 3.68728966e-01, -8.54765773e-01, -4.77824420e-01,
-3.83714020e-01, -1.73216307e+00, 2.03872994e-02,
2.63810277e+00, -1.12998331e+00],

同样可以选择在其他位置插入新维度,如在最末尾插入:

In [5]:
a = tf.random.normal([35,8])
b = tf.random.normal([35,8])
tf.stack([a,b],axis=-1) # 在末尾插入班级维度
Out[5]:
<tf.Tensor: id=69, shape=(35, 8, 2), dtype=float32, numpy=
array([[[ 0.3456724 , -1.7037214 ],
[ 0.41140947, -1.1554345 ],
[ 1.8998919 , 0.56994915],

此时班级的维度在axis=2 轴上面,理解时也需要按着最新的维度顺序去理解数据。若选择使用tf.concat 上述成绩单,则可以合并为:

In [6]:
a = tf.random.normal([35,8])
b = tf.random.normal([35,8])
tf.concat([a,b],axis=0) # 拼接方式合并,没有2 个班级的概念
Out[6]:
<tf.Tensor: id=108, shape=(70, 8), dtype=float32, numpy=
array([[-0.5516891 , -1.5031327 , -0.35369992, 0.31304857, 0.13965549,
0.6696881 , -0.50115544, 0.15550546],
[ 0.8622069 , 1.0188094 , 0.18977325, 0.6353301 , 0.05809061,

tf.concat 也可以顺利合并数据,但是在理解时,需要按着前35 个学生来自第一个班级,后35 个学生来自第二个班级的方式。在这里,明显通过tf.stack 方式创建新维度的方式更合理,得到的shape 为[2,35,8]的张量也更容易理解。

tf.stack 也需要满足张量堆叠合并条件,它需要所有合并的张量shape 完全一致才可合并。我们来看张量shape 不一致时进行堆叠合并会发生的错误:

In [7]:
a = tf.random.normal([35,4])
b = tf.random.normal([35,8])
tf.stack([a,b],axis=-1) # 非法堆叠操作
Out[7]:InvalidArgumentError: Shapes of all inputs must match: values[0].shape       [35,4] != values[1].shape = [35,8] [Op:Pack] name: stack

上述操作尝试合并shape 为[35,4]和[35,8]的2 个张量,由于2 者形状不一致,无法完成合并操作。

分割

合并操作的逆过程就是分割,将一个张量分拆为多个张量。继续考虑成绩册的例子,我们得到整个学校的成绩册张量,shape 为[10,35,8],现在需要将数据在班级维度切割为10 个张量,每个张量保存了对应班级的成绩册。

通过 tf.split(x, axis, num_or_size_splits)可以完成张量的分割操作,其中
❑ x:待分割张量
❑ axis:分割的维度索引号
❑ num_or_size_splits:切割方案。当num_or_size_splits 为单个数值时,如10,表示切割
为10 份;当num_or_size_splits 为List 时,每个元素表示每份的长度,如[2,4,2,2]表示切割为4 份,每份的长度分别为2,4,2,2现在我们将总成绩册张量切割为10 份:

In [8]:
x = tf.random.normal([10,35,8])
# 等长切割
result = tf.split(x,axis=0,num_or_size_splits=10)
len(result)
Out[8]: 10

可以查看切割后的某个张量的形状,它应是某个班级的所有成绩册数据,shape 为[35,8]之类:

In [9]: result[0]
Out[9]: <tf.Tensor: id=136, shape=(1, 35, 8), dtype=float32, numpy=
array([[[-1.7786729 , 0.2970506 , 0.02983334, 1.3970423 ,
1.315918 , -0.79110134, -0.8501629 , -1.5549672 ],
[ 0.5398711 , 0.21478991, -0.08685189, 0.7730989 ,

可以看到,切割后的班级shape 为[1,35,8],保留了班级维度,这一点需要注意。我们进行不等长的切割:将数据切割为4 份,每份长度分别为[4,2,2,2]:

In [10]: x = tf.random.normal([10,35,8])
# 自定义长度的切割
result = tf.split(x,axis=0,num_or_size_splits=[4,2,2,2])
len(result)
Out[10]: 4

查看第一个张量的shape,根据我们的切割方案,它应该包含了4 个班级的成绩册:

In [10]: result[0]
Out[10]: <tf.Tensor: id=155, shape=(4, 35, 8), dtype=float32, numpy=
array([[[-6.95693314e-01, 3.01393479e-01, 1.33964568e-01, ...,

查看切割后的张量的形状:

In [12]: result[0]
Out[12]: <tf.Tensor: id=166, shape=(35, 8), dtype=float32, numpy=
array([[-0.2034383 , 1.1851563 , 0.25327438, -0.10160723, 2.094969 ,
-0.8571669 , -0.48985648, 0.55798006],

可以看到,通过tf.unstack 切割后,shape 变为[35,8],即班级维度消失了,这也是与tf.split区别之处。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值