深入理解element-wise add与concat特征图的连接方式

一、参考资料

深度学习之concatenate和elementwise操作(一)
深度学习之concatenate和elementwise操作(二)

二、element-wise相关介绍

1. 对应关系vs对应元素

如果两个元素在两个tensor内占据相同的位置,则认为这个两个元素是对应的,该对应关系由元素的索引确定。

> t1 = torch.tensor([ [1,2], [3,4] ], dtype=torch.float32) 
> t2 = torch.tensor([ [9,8], [7,6] ], dtype=torch.float32)

# Example of the first axis
> print(t1[0])
tensor([1., 2.])
 
# Example of the second axis
> print(t1[0][0])
tensor(1.)

# t1中的1元素,对应t2中的9
> t1[0][0] tensor(1.) 
> t2[0][0] tensor(9.)

2. element-wise的概念

elementwise 操作是指两个相同形状(shape)的张量(tensor),在对应元素上进行逐位运算

  • element-wise 操作相同形状的张量,即张量必须具有相同数量的元素才能执行 element-wise 操作
  • 所有的算数运算,加、减、乘、除都是 element-wise 运算,我们通常看到的张量运算是使用标量值的算数运算。

以下术语都是指 element-wise

  • Element-wise
  • Component-wise
  • Point-wise

3. element-wise add

3.1 算数运算符

利用 +、-、*、/ 算数运算符,进行算数运算。

> t1 = torch.tensor([ [1,2], [3,4] ], dtype=torch.float32) 
> t2 = torch.tensor([ [9,8], [7,6] ], dtype=torch.float32)

> t1 + t2
tensor([[10., 10.],
        [10., 10.]])
        
> print(t1 + 2)
tensor([[3., 4.],
        [5., 6.]])
 
> print(t1 - 2)
tensor([[-1.,  0.],
        [ 1.,  2.]])
 
> print(t1 * 2)
tensor([[2., 4.],
        [6., 8.]])
 
> print(t1 / 2)
tensor([[0.5000, 1.0000],
        [1.5000, 2.0000]])

3.2 内置方法

利用张量内置的方法,进行算数运算。

> t1 = torch.tensor([ [1,2], [3,4] ], dtype=torch.float32) 
> t2 = torch.tensor([ [9,8], [7,6] ], dtype=torch.float32)

> print(t1.add(2))
tensor([[3., 4.],
        [5., 6.]])
 
> print(t1.sub(2))
tensor([[-1.,  0.],
        [ 1.,  2.]])
 
> print(t1.mul(2))
tensor([[2., 4.],
        [6., 8.]])
 
> print(t1.div(2))
tensor([[0.5000, 1.0000],
        [1.5000, 2.0000]])

4. Dot Product

[ a 1 a 2 ⋮ a n − 1 a n ] ⋅ [ b 1 b 2 ⋮ b n − 1 b n ] = [ a 1 b 1 a 2 b 2 ⋮ a n − 1 b n − 1 a n b n ] = a 1 b 1 + a 2 b 2 + ⋯ + a n − 1 b n − 1 + a n b n \left[\begin{array}{c}a_1\\a_2\\\vdots\\a_{n-1}\\a_n\end{array}\right]\cdot\left[\begin{array}{c}b_1\\b_2\\\vdots\\b_{n-1}\\b_n\end{array}\right] = \begin{bmatrix}a_{1} b_{1}\\a_{2} b_{2}\\\vdots\\a_{n-1} b_{n-1}\\a_{n} b_{n}\end{bmatrix} = \mathbf{a}_{1}\mathbf{b}_{1}+\mathbf{a}_{2}\mathbf{b}_{2}+\cdots+\mathbf{a}_{n-1}\mathbf{b}_{n-1}+\mathbf{a}_{n}\mathbf{b}_{n} a1a2an1an b1b2bn1bn = a1b1a2b2an1bn1anbn =a1b1+a2b2++an1bn1+anbn

简化使用求和公式:
a ⋅ b = ∑ i = 1 n a i b i \mathbf{a}\cdot\mathbf{b}=\sum_{i=1}^{n}\mathbf{a}_{i}\mathbf{b}_{i} ab=i=1naibi

import numpy as np
 
np1 = np.array([4, 6])
np2 = np.array([[-3], [7]])
print(np.dot(np1, np2)
 
 
##  [30]
# 4*-3 + 6*7 = 42*12 = 30

5. element-wise product

element-wise product 实际上是两个相同形状的张量进行逐元素相乘。

import numpy as np
 
np1 = np.array([4, 6])
np2 = np.array([-3, 7])
print(np2 * np1)
 
# [-12  42]
 
 
import numpy as np
 
np1 = np.array([4, 6])
print(np1 * 3)
 
# [12 18]

三、broadcasting广播机制

1. 引言

如果标量值2是0阶张量,意味着它没有形状,张量 t1 是一个形状为 2x2 的2阶张量。如下所示:

> a = 2
> t1 = torch.tensor([ [1,2], [3,4] ], dtype=torch.float32) 

由于 element-wise 只能操作相同形状的张量,为了解决两个张量形状不一致的问题,引入了 broadcasting广播机制,使得两个不同形状的张量可以进行算数运算。

2. broadcasting广播机制

将低阶张量通过 broadcasting 广播机制进行变换,以匹配高阶张量的形状,从而能够进行 element-wise 操作。broadcasting广播是一个比基本 element-wise 操作更先进的话题

> np.broadcast_to(2, t1.shape)
array([[2, 2],
        [2, 2]])

将标量值2转换成一个2阶张量,该张量的形状与t1一样,形状匹配和 element-wise 方面的规则有了相同的形状。

> t1 + 2 
tensor([[3., 4.], 
       [5., 6.]])

实际上的操作:

> t1 + torch.tensor(
    np.broadcast_to(2, t1.shape)
    ,dtype=torch.float32
)
tensor([[3., 4.],
        [5., 6.]])

3. 复杂的例子

t1 = torch.tensor([
    [1,1],
    [1,1]
], dtype=torch.float32)
 
t2 = torch.tensor([2,4], dtype=torch.float32)

# t1 + t2 ???????
 
> t1.shape
torch.Size([2, 2])
 
> t2.shape
torch.Size([2])

使用numpy包的 broadcast_to() 函数检查 broadcast 转换。

> np.broadcast_to(t2.numpy(), t1.shape)
array([[2., 4.],
        [2., 4.]], dtype=float32)
 
> t1 + t2
tensor([[3., 5.],
        [3., 5.]])

broadcasting之后,这两个张量之间的加法运算是相同形状张量之间的 element-wise 运算。

四、concatenation operation

1. concatenation operation的概念

concatenation operation 是两个张量之间的拼接。

import numpy as np
 
a = np.array([[1, 2], [3, 4]])
print(a.shape)
 
 
b = np.array([[5, 6]])
print(b.shape)
 
np.concatenate((a, b))
 
c= np.concatenate((a, b))
print(c)
 
print(c.shape)

# 输出
Out[1]: (2, 2)
Out[2]: (1, 2)
Out[3]: 
array([[1, 2],
       [3, 4],
       [5, 6]])
Out[4]: (3, 2)

2. feature map的几种连接形式

2.1 concatenate

concatenate方式先卷积,再拼接。先对每个通道进行卷积,然后合并通道。

concatenate方式是通道数的合并,描述图像本身的特征数(通道数)增加,而每一特征下的信息没有增加。

Z c o n c a t = ∑ i = 1 c X i ∗ K i + ∑ i = 1 c Y i ∗ K i + c Z_{\mathrm{concat}}=\sum_{i=1}^cX_i*K_i+\sum_{i=1}^cY_i*K_{i+c} Zconcat=i=1cXiKi+i=1cYiKi+c

2.2 element-wise add

add方式先相加,再卷积。先将对应的特征图进行相加,然后卷积。对应通道的特征图语义信息类似,对应的特征图共享一个卷积核。

add方式使得描述图像的每一维度下的特征信息量增多,但描述图像的维度本身并没有增加,显然对最终的图像分类是有意义的。
Z a d d = ∑ i = 1 c ( X i + Y i ) ∗ K i = ∑ i = 1 c X i ∗ K i + ∑ i = 1 c Y i ∗ K i \begin{aligned}Z_{add}=\sum_{i=1}^c\left(X_i+Y_i\right)*K_i=\sum_{i=1}^cX_i*K_i+\sum_{i=1}^cY_i*K_i\end{aligned} Zadd=i=1c(Xi+Yi)Ki=i=1cXiKi+i=1cYiKi

2.3 总结

  • 通过add操作会得到新的特征,这个新的特征可以反映原始特征的一些特性,但是原始特征的一些信息也会由于add方式造成损失;concatenate是将原始特征直接拼接,让网络去学习应该如何融合特征,这个过程中信息不会损失。

  • concat的计算量较大,在明确原始特征的关系的情况下,使用add操作融合可以节省计算代价。

猜你喜欢

转载自blog.csdn.net/m0_37605642/article/details/132747021