Transformer-02 MASK、FFN、残差连接+层归一化及Embedding

Transformer的MASK、FFN、残差连接和Embedding解析
本文介绍了Transformer中MASK的作用,包括Padding mask和Sequence mask,阐述了FFN在网络中的功能,解释了残差连接和层归一化的区别,并详细讨论了词Embedding和位置Embedding在Transformer中的应用。

     关于除了attention其他的transformer部分,结合看的transformer论文及自己的其他查询资料总结如下:

一、 MASK

     mask操作在sequence类操作很常见,因为定长输入的序列很多时候存在填充情况,不利用mask参数告诉模型无意义填充值,会导致无效学习,甚至由于梯度传播的梯度消失问题,还会影响模型效果。而对于transform模型,除了类似sequence模型存在的需要padding mask的情况,还在decode部分,需要对于decode进行未来数据的遮蔽,进行sequence mask,具体如下,以下参考博客:Transformer 模型详解_XP-Code的博客-CSDN博客_get_attn_key_pad_mask

1 Padding mask

    什么是padding mask呢?由于我们的每个批次输入序列长度是不一样的,也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充0。因为这些填充的位置,其实是没什么意义的,所以我们的attention机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。具体的做法是,把这些位置的值加上一个非常大的负数(可以是负无穷),这样的话,经过softmax,这些位置的概率就会接近0!而我们的padding mask实际上是一个张量,每个值都是一个Boolen,值为False的地方就是我们要进行处理的地方。

2 Sequence mask

sequence mask是为了使得decoder不能看见未来的信息。也就是对于一个序列,在time_step为t的时刻,我们的解码输出应该只能依赖于t时刻之前的输出,而不能依赖t之后的输出。因此我们需要想一个办法,把t之后的信息给隐藏起来。那么具体怎么做呢?也很简单:产生一个上三角矩阵,上三角的值全为0,下三角的值全为1,对角线也是0。把这个矩阵作用在每一个序列上,就可以达到我们的目的啦。

值得注意的是,本来mask只需要二维的矩阵即可,但是考虑到我们的输入序列都是批量的,所以我们要把原本二维的矩阵扩张成3维的张量(加入了一个batch_size维度)。上面的代码可以看出,我们已经进行了处理。

总的来说,attention中的mask 参数有几种情况:

对于decoder的第一个多头注意力子层,里面使用到的scaled dot-product attention,同时需要padding mask和sequence mask作为attn_mask,具体实现就是两个mask相加作为attn_mask。

其他情况,attn_mask一律等于padding mask。

二、 FFN

     FFN的主要作用也就是简单的神经网络,可以起到空间变化的作用,具体来说,对于多头注意力得到特征对再进行FFN的非线性处理,可以挖掘特征的非线性关系,增强特征的表现能力。

三、 残差连接+层归一化(Layer Normalization)

      这里需要先区别层归一化layer normalization 及 批量归一化 Batch Normalization,BN 方法。

Batch Normalization:是经常会应用于图像处理中的一种方法,即每层通过对于批量样本的净输入的标准化来减少样本带来的输入的过大偏移。

layer normalization:是和批量归一化非常类似的方法. 和批量归一化不同的是层归一化是对一个中间层的所有神经元进行归一化。LN主要是会用于NLP处理中,之所以利用LN而不是BN进行处理,原因在于:

       NLP中 batch normalization与 layer normalization - 知乎 如果我们将一批文本组成一个batch,那么BN的操作方向是,对每句话的第一个词进行操作。但语言文本的复杂性是很高的,任何一个词都有可能放在初始位置,且词序可能并不影响我们对句子的理解。而BN是针对每个位置进行缩放,这不符合NLP的规律。而LN则是针对一句话进行缩放的,且LN一般用在第三维度,如[batchsize, seq_len, dims]中的dims,一般为词向量embedding的维度,或者是RNN的输出维度等等,这一维度各个特征的量纲应该相同。因此也不会遇到上面因为特征的量纲不同而导致的缩放问题。

      在transformer中,比如对于encoder的multi-head attention进行layer normalization,可以理解为对于embedding通过multi-head attention得到的各组特征进行标准化。具体操作如下:

四、 Embedding

1  词Embedding

     值得注意的应该只有encoder及decode部分的embedding是权重共享的,另外输出的结果也利用相同的embedding进行解码,得到预测的文本序列生成结果。

2  位置Embedding -- Positional Encoding

     关于attention is all your need 中的  巧妙的位置编码这里就不说了,note下keras中的transformer位置参数并没有采用以上方法,而是直接建立了一个sequence_length*output_dim的新的embedding。

<think>我们正在设计一个多任务模型架构图,该模型包含1D CNN、Transformer Encoder、全连接层,以及分类和回归输出头。我们将分步构建这个模型。 步骤1:输入数据 假设输入数据是一维时序信号,形状为`(batch_size, sequence_length, input_features)`。 步骤2:1D CNN层 使用1D CNN进行局部特征提取。可以包含多个卷积层和池化层。输出形状为`(batch_size, new_sequence_length, num_filters)`。 步骤3:Transformer Encoder 将CNN的输出作为Transformer Encoder的输入。Transformer Encoder由多个相同的层组成,每层包含多头自注意力机制和前馈神经网络。 注意:Transformer Encoder要求输入是三维张量`(batch_size, sequence_length, features)`,这正是CNN的输出形状。 输出形状仍为`(batch_size, new_sequence_length, d_model)`,其中`d_model`是Transformer的隐藏层维度。 步骤4:全局平均池化(可选) 为了减少序列长度的影响并提取全局特征,可以在Transformer之后添加全局平均池化层(Global Average Pooling),将每个特征维度上的序列平均,输出形状变为`(batch_size, d_model)`。 或者,我们也可以使用Transformer Encoder输出的第一个位置([CLS] token)的输出作为全局表示,但这里我们并没有添加额外的[CLS] token,因此使用全局平均池化更直接。 步骤5:全连接层 将池化后的特征通过一个或多个全连接层,得到高层次的表示。输出形状为`(batch_size, hidden_units)`。 步骤6:多任务输出头 分类头:一个全连接层(通常带有softmax激活函数)用于分类任务,输出形状为`(batch_size, num_classes)`。 回归头:一个全连接层(通常不带激活函数或使用线性激活)用于回归任务,输出形状为`(batch_size, regression_targets)`。 现在,我们可以绘制一个结构图(使用文字描述): 输入 (1D序列) | 1D CNN (卷积和池化) | Transformer Encoder (多个编码器层) | 全局平均池化 (在序列维度上) | 全连接层 (一个或多个) | 分支为两个头: | | |--- 分类头 (全连接+softmax) 输出分类结果 |--- 回归头 (全连接) 输出回归值 注意:在代码实现中,我们可以使用PyTorch或TensorFlow/Keras来构建这个模型。 下面是一个简单的PyTorch代码示例: ```python import torch import torch.nn as nn from torch.nn import TransformerEncoder, TransformerEncoderLayer class MultiTaskModel(nn.Module): def __init__(self, input_features, num_filters, d_model, nhead, num_layers, hidden_units, num_classes, regression_targets): super().__init__() # 1D CNN self.cnn = nn.Sequential( nn.Conv1d(input_features, num_filters, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool1d(kernel_size=2, stride=2), # 可以添加更多层 ) # 假设经过CNN后序列长度变为原来的1/2(因为stride=2的池化) # Transformer Encoder self.transformer_encoder_layer = TransformerEncoderLayer(d_model=d_model, nhead=nhead) self.transformer_encoder = TransformerEncoder(self.transformer_encoder_layer, num_layers=num_layers) # 全局平均池化 self.global_avg_pool = nn.AdaptiveAvgPool1d(1) # 全连接层 self.fc = nn.Sequential( nn.Linear(d_model, hidden_units), nn.ReLU(), ) # 分类头 self.classifier = nn.Linear(hidden_units, num_classes) # 回归头 self.regressor = nn.Linear(hidden_units, regression_targets) def forward(self, x): # 输入x: (batch_size, sequence_length, input_features) # 1D CNN要求输入为(batch_size, input_features, sequence_length) x = x.permute(0, 2, 1) x = self.cnn(x) # 输出: (batch_size, num_filters, new_seq_len) # 为了输入Transformer,需要将维度调整为(batch_size, new_seq_len, num_filters) x = x.permute(0, 2, 1) # 现在形状为(batch_size, new_seq_len, num_filters) # 注意:Transformer期望输入形状为(seq_len, batch_size, d_model) x = x.permute(1, 0, 2) # 调整为(seq_len, batch_size, num_filters) # 通过Transformer Encoder x = self.transformer_encoder(x) # 输出: (seq_len, batch_size, d_model) # 调整回(batch_size, seq_len, d_model) x = x.permute(1, 0, 2) # (batch_size, seq_len, d_model) # 全局平均池化:在序列维度上取平均 # 先调整维度:将d_model作为通道维度,序列长度作为长度维度 x = x.permute(0, 2, 1) # (batch_size, d_model, seq_len) x = self.global_avg_pool(x) # 输出: (batch_size, d_model, 1) x = x.squeeze(-1) # (batch_size, d_model) # 全连接层 x = self.fc(x) # (batch_size, hidden_units) # 多任务输出 class_output = self.classifier(x) # (batch_size, num_classes) reg_output = self.regressor(x) # (batch_size, regression_targets) return class_output, reg_output ``` 注意:上述代码中,我们假设CNN的输出通道数(num_filters)等于Transformer的d_model。如果不是,则需要一个线性变换来调整维度。 另外,Transformer的输入要求是(seq_len, batch_size, d_model),所以我们进行了维度置换。 结构图(文字描述): ``` Input (batch_size, seq_len, input_features) | v 1D CNN [Conv1d(input_features->num_filters, kernel=3, padding=1) -> ReLU -> MaxPool1d(kernel=2, stride=2)] | Output: (batch_size, num_filters, new_seq_len) [new_seq_len = (seq_len+2*padding - (kernel_size-1)-1)//stride + 1? 实际上,我们使用padding=1,所以卷积后长度不变,然后池化步长为2,所以长度减半] | v Permute: (batch_size, new_seq_len, num_filters) | v Permute: (new_seq_len, batch_size, num_filters) -> 输入Transformer Encoder | v Transformer Encoder (num_layers层) -> 输出: (new_seq_len, batch_size, d_model) [注意:这里我们假设num_filters=d_model] | v Permute: (batch_size, new_seq_len, d_model) | v Permute: (batch_size, d_model, new_seq_len) -> 全局平均池化 (AdaptiveAvgPool1d(1)) -> (batch_size, d_model, 1) -> squeeze(-1) -> (batch_size, d_model) | v 全连接层 [Linear(d_model, hidden_units) -> ReLU] -> (batch_size, hidden_units) | |---------------------------- | | v v 分类头 [Linear(hidden_units, num_classes)] 回归头 [Linear(hidden_units, regression_targets)] | | v v 分类输出 (batch_size, num_classes) 回归输出 (batch_size, regression_targets) ``` 注意:在实际使用中,可能需要根据具体任务调整超参数(如卷积层数、Transformer层数等)和结构(如添加残差连接、LayerNorm等)。 生成相关问题: 1. 如何调整模型以适应不同长度的输入序列? 2. 在多任务学习中,如何处理分类和回归任务的损失平衡? 3. 为什么在CNN之后使用Transformer?有什么优势? 4. 这个模型在哪些实际应用中可以发挥作用?</think>### 多任务模型架构图(1D CNN + Transformer Encoder + FC + 多输出头) 以下是您要求的模型架构示意图(文字描述版): ``` 输入层 (1D序列数据) │ (shape: [batch_size, seq_len, channels]) ▼ 1D CNN 特征提取层 │ ├─ Conv1D + 激活函数(ReLU) │ ├─ 批归一化(BatchNorm) │ └─ 池化层(MaxPool/AvgPool) ▼ (输出: [batch_size, new_seq_len, filters]) 位置编码层 │ (添加可学习的位置嵌入) ▼ Transformer Encoder 层 │ ├─ 多头自注意力机制(Multi-Head Attention) │ ├─ 残差连接 + 层归一化 │ ├─ 前馈网络(FFN) │ └─ 重复N个编码器层 ▼ (输出: [batch_size, new_seq_len, d_model]) 全局特征聚合层 │ ├─ 全局平均池化(Global Average Pooling) │ └─ 或 [CLS] 令牌聚合 ▼ (输出: [batch_size, d_model]) 全连接层(FC) │ ├─ 多层感知机(MLP) │ └─ Dropout正则化 ▼ (输出: [batch_size, hidden_units]) ┌───────────────┴───────────────┐ ▼ ▼ 分类头 回归头 ├─ Dense(num_classes) ├─ Dense(regression_targets) └─ Softmax激活 └─ 线性激活 ``` ### 关键组件说明: 1. **1D CNN层** - 使用卷积核在时序维度滑动提取局部特征 - 公式:$y_t = \sum_{k=0}^{K-1} w_k \cdot x_{t+k} + b$ - 输出维度变化:$[B,L,C] \rightarrow [B,L',F]$ 2. **Transformer Encoder** - 多头注意力机制: $$ \text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V $$ - 每个编码器层包含: - 自注意力子层 - 前馈网络子层($\text{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2$) - 残差连接层归一化[^3] 3. **多任务输出头** - **分类头**:全连接+ Softmax,输出概率分布 - **回归头**:全连接层(无激活函数),输出连续值 - 共享特征提取主干,独立输出层 ### PyTorch实现框架 ```python import torch import torch.nn as nn from torch.nn import TransformerEncoder, TransformerEncoderLayer class MultiTaskModel(nn.Module): def __init__(self, input_dim, seq_len, num_classes, regression_targets): super().__init__() # 1D CNN self.cnn = nn.Sequential( nn.Conv1d(input_dim, 64, kernel_size=5, padding=2), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(64, 128, kernel_size=3, padding=1), nn.ReLU() ) # Transformer self.pos_encoder = nn.Embedding(seq_len//2, 128) # 序列长度减半 encoder_layers = TransformerEncoderLayer(d_model=128, nhead=8) self.transformer = TransformerEncoder(encoder_layers, num_layers=3) # 全连接层 self.fc = nn.Sequential( nn.Linear(128, 256), nn.ReLU(), nn.Dropout(0.5) ) # 多任务头 self.classifier = nn.Linear(256, num_classes) self.regressor = nn.Linear(256, regression_targets) def forward(self, x): # CNN处理 (B, C, L) -> (B, F, L') x = self.cnn(x.permute(0,2,1)) # 位置编码 positions = torch.arange(x.shape[2]).to(x.device) x = x + self.pos_encoder(positions)[None,:,:].permute(0,2,1) # Transformer (L', B, F) x = self.transformer(x.permute(2,0,1)) # 全局平均池化 x = x.mean(dim=0) # (B, F) # 全连接层 features = self.fc(x) # 多任务输出 return self.classifier(features), self.regressor(features) ``` ### 设计要点 1. **特征融合策略** CNN提取局部特征 → Transformer捕获长距离依赖 → 全局池化聚合时序信息 2. **多任务平衡** - 建议使用加权损失:$\mathcal{L} = \alpha \mathcal{L}_{cls} + \beta \mathcal{L}_{reg}$ - 动态调整权重(如不确定性加权法[^1]) 3. **位置编码** 使用可学习嵌入替代正弦编码,适应变长输入 4. **梯度流优化** - 共享层使用较大学习率 - 任务特定层使用较小学习率 - 梯度裁剪防止爆炸 ### 应用场景 此架构适用于: - 生理信号分析(EEG/ECG分类+异常值回归)[^2] - 金融时序预测(涨跌分类+价格回归) - 工业传感器监测(故障分类+剩余寿命预测)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值