『Transformer』为什么1*1卷积可以替代全连接层?

为什么1*1卷积可以替代全连接层?


起源

来自Transformer

事情起源于同学的一个疑惑,他在阅读Transformer论文时,看到作者在前馈神经网络部分写有这么一句话:

Another way of describing this is as two convolutions with kernel size 1.

于是他向我问道“为什么全连接层可以用1*1卷积层代替呢?”

对卷积的理解尚不深刻的我被问住了,所以我立马开始搜索资料以解决这一问题。

下面将此问题相关的内容分析整理出来,以供复习。


解决

按照我之前的理解,对于一张 5 ∗ 5 5*5 55的原始图像进行 1 ∗ 1 1*1 11的卷积操作,就是对原图像的每个元素乘以一个卷积核参数得到 5 ∗ 5 5*5 55的特征图,那这不就是直接逐元素乘以常数嘛喂!怎么可能代替全连接呢?!

之所以会有这个误会,是因为我们平常所说的 1 ∗ 1 1*1 11卷积其实省略了一个重要的东西,实际上应为 1 ∗ 1 ∗ 输 入 通 道 数 1*1*输入通道数 11 卷积。

更广泛来说,当我们对K个通道的输入进行 n ∗ n n*n nn卷积时,我们需要一个shape为 [ n , n , k ] [n, n, k] [n,n,k]的kernel。

Kernel示意图

举个例子,对于一张 1 ∗ 1 1*1 11的图像,它拥有RGB三个通道,我们想要执行 1 ∗ 1 1*1 11的卷积操作,那么我们kernel的shape应为 [ 1 , 1 , 3 ] [1, 1, 3] [1,1,3]

假设卷积核 k e r n e l = ( k 1 , k 2 , k 3 ) kernel = (k_1, k_2, k_3) kernel=(k1,k2,k3),同一空间位置不同通道的输入从上到下依次是 x 1 , x 2 , x 3 x_1, x_2, x_3 x1,x2,x3,那么输出特征图上对应位置应为 k 1 x 1 + k 2 x 2 + k 3 x 3 k_1x_1 + k_2x_2 + k_3x_3 k1x1+k2x2+k3x3

所以说, 1 ∗ 1 1*1 11卷积操作是在每个像素位置上,不同feature channels的线性叠加,其目的是保留原有图像平面结构的基础上,调整通道数(即depth),从而完成升维或降维的功能。

1*1卷积 不同通道的线性叠加

理解了这一点之后,就可以明白为什么 1 ∗ 1 1*1 11卷积操作等价于一个全连接层了。

依旧举例说明,假如现在有一层全连接网络,输入层维度为3,输出层维度为2,具体参数如下:

W = ( 0 1 1 2 3 5 ) ∈ R 2 × 3 W = \begin{pmatrix} 0 & 1 & 1 \\ 2 & 3 & 5 \\ \end{pmatrix} \in R^{2 \times 3} W=(021315)R2×3

b = ( 8 13 ) ∈ R 2 b = \begin{pmatrix} 8 \\ 13 \\ \end{pmatrix} \in R^2 b=(813)R2

则可知网络 f ( x ) = R e L U ( W ⋅ x + b ) f(x) = ReLU(W\cdot x + b) f(x)=ReLU(Wx+b),其中 x ∈ R 3 x \in R^3 xR3

此时我们将维度为3的输入展开为 [ 1 , 1 , 3 ] [1, 1, 3] [1,1,3],同样地将维度为2的输出展开为 [ 1 , 1 , 2 ] [1, 1, 2] [1,1,2],从卷积的角度可以看成是输入是空间维度为 1 ∗ 1 1*1 11的3个通道的特征图,输出是空间维度为 1 ∗ 1 1*1 11的2个通道的特征图。

对于空间维度 1 ∗ 1 1*1 11的3通道输入,我们需要用 [ 1 , 1 , 3 ] [1, 1, 3] [1,1,3]的kernel,计算得到 1 ∗ 1 1*1 11的输出特征图,那么使用两个这样的kernel便得到了两个输出通道,即 [ 1 , 1 , 2 ] [1, 1, 2] [1,1,2]

假设每一个kernel的卷积核参数如下所示:

K 1 = ( 0    1    1 ) K 2 = ( 2    3    5 ) K_1 = (0 \ \ 1 \ \ 1 )\\ K_2 = (2 \ \ 3 \ \ 5) K1=(0  1  1)K2=(2  3  5)

可以在 1 ∗ 1 1*1 11卷积操作的基础上添加ReLU函数,那么有如下公式:

f ( x ) = R e L U ( ( K 1 ⋅ x K 2 ⋅ x ) + ( b 1 b 2 ) ) f(x) = ReLU\left(\begin{pmatrix} K_1\cdot x \\ K_2\cdot x \\ \end{pmatrix} + \begin{pmatrix} b_1 \\ b_ 2 \end{pmatrix}\right) f(x)=ReLU((K1xK2x)+(b1b2)),其中 x ∈ R 3 x \in R^3 xR3

此时 1 ∗ 1 1*1 11卷积操作的公式便与全连接层一致,这就是为什么 1 ∗ 1 1*1 11卷积操作可以等价于一个全连接层。

最后回到Transformer上去,如何用两个 1 ∗ 1 1*1 11卷积代替MLP呢?假设 d m o d e l = 512 d_{model}=512 dmodel=512,序列长度为 n n n,那么可以将每个token看作 [ 1 , 1 , 512 ] [1, 1, 512] [1,1,512],并将其竖起来,使用shape为 [ 1 , 1 , 512 ] [1, 1, 512] [1,1,512]的kernel进行卷积,并使用 2048 2048 2048个这样的kernel,便可得到 [ n , 2048 ] [n, 2048] [n,2048]维度的张量,维度扩大四倍,等价于第一层全连接。

同理再用 512 512 512个shape为 [ 1 , 1 , 2048 ] [1, 1, 2048] [1,1,2048]的kernel便可得到 [ n , 512 ] [n, 512] [n,512]的输出,回到原维度,等价于第二层全连接。


参考

解决疑惑的主要参考

How are 1x1 convolutions the same as a fully connected layer?

辅助参考

卷积神经网络中用1*1 卷积有什么作用或者好处呢?

MLP conv layers

  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
自注意力机制(Self-Attention)是一种能够在序列数据中捕捉长程依赖关系的机制,广泛应用于自然语言处理领域。在全连接层中,我们可以使用自注意力机制来增强模型的表达能力。下面是一个基于自注意力的全连接层模型的实现。 首先,我们需要导入必要的库和模块: ```python import torch import torch.nn as nn import torch.nn.functional as F ``` 然后,我们定义一个自注意力层(SelfAttentionLayer)类,它接受输入张量 x 和输出张量的维度 dim,以及一个可选的 dropout 参数: ```python class SelfAttentionLayer(nn.Module): def __init__(self, dim, dropout=0.1): super(SelfAttentionLayer, self).__init__() self.dim = dim self.dropout = dropout self.q_layer = nn.Linear(dim, dim) self.k_layer = nn.Linear(dim, dim) self.v_layer = nn.Linear(dim, dim) self.dropout_layer = nn.Dropout(dropout) def forward(self, x): q = self.q_layer(x) k = self.k_layer(x) v = self.v_layer(x) scores = torch.matmul(q, k.transpose(-2, -1)) scores = scores / torch.sqrt(torch.tensor(self.dim).float()) weights = F.softmax(scores, dim=-1) weights = self.dropout_layer(weights) outputs = torch.matmul(weights, v) return outputs ``` 在这个自注意力层中,我们分别使用了三个全连接层对输入张量 x 进行线性变换,得到了三个张量 q、k 和 v。然后,我们计算了注意力分数(scores): $$ scores = q \cdot k^T / \sqrt{d} $$ 其中,$d$ 是张量维度。接着,我们使用 softmax 函数将注意力分数转换成注意力权重(weights),并应用 dropout 正则化。最后,我们将注意力权重与 v 相乘,得到了输出张量。 接下来,我们定义一个全连接层(FeedForwardLayer)类,它接受输入张量 x 和输出张量的维度 dim,以及一个可选的 dropout 参数: ```python class FeedForwardLayer(nn.Module): def __init__(self, dim, dropout=0.1): super(FeedForwardLayer, self).__init__() self.dim = dim self.dropout = dropout self.fc1 = nn.Linear(dim, dim*4) self.fc2 = nn.Linear(dim*4, dim) self.dropout_layer = nn.Dropout(dropout) def forward(self, x): h = F.relu(self.fc1(x)) h = self.dropout_layer(h) y = self.fc2(h) return y ``` 在这个全连接层中,我们使用了两个全连接层和一个 ReLU 激活函数。我们还应用了 dropout 正则化来减少过拟合。最后,我们将输出张量 y 返回。 最后,我们定义一个全连接层模型(FullyConnectedLayer),它使用多个自注意力层和全连接层来构建一个深层的全连接神经网络: ```python class FullyConnectedLayer(nn.Module): def __init__(self, dim, num_layers, dropout=0.1): super(FullyConnectedLayer, self).__init__() self.dim = dim self.num_layers = num_layers self.dropout = dropout self.self_attention_layers = nn.ModuleList() self.feed_forward_layers = nn.ModuleList() for i in range(num_layers): self.self_attention_layers.append(SelfAttentionLayer(dim, dropout)) self.feed_forward_layers.append(FeedForwardLayer(dim, dropout)) def forward(self, x): for i in range(self.num_layers): x = x + self.self_attention_layers[i](x) x = x + self.feed_forward_layers[i](x) return x ``` 在这个全连接层模型中,我们使用了一个 nn.ModuleList 对象来保存多个自注意力层和全连接层。在前向传递中,我们依次遍历每一层,并将输入张量 x 作为参数传递给每一层。最后,我们将输出张量返回。 现在,我们可以使用这个自注意力全连接层模型来处理序列数据,并捕捉序列之间的长程依赖关系。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值