通俗易懂解读:hw04.py 文件内容与 Transformer 的应用
这个文件是一个 Python 脚本(hw04.py),用于完成 NTU 2021 Spring 机器学习课程的 HW4 作业任务:扬声器分类(Speaker Classification) 。它主要通过 Transformer 模型(尤其是自注意力机制,Self-Attention)来实现分类,并提供了训练和推理代码。以下我会详细讲解文件的结构,重点教你如何使用 Transformer 和 Self-Attention,并让你明白如何训练模型、调整参数。
1. 文件概述
- 任务:从语音特征(梅尔频谱图,mel-spectrogram)中分类扬声器(600 个类别)。
- 数据集:Voxceleb2 数据集的子集,包含 600 个扬声器的音频特征。
- 目标 :
- 学习使用 Transformer 模型(Simple 级别)。
- 调整 Transformer 参数(Medium 级别)。
- 构建 Conformer(Hard 级别,代码中未实现)。
- 进一步实现 Self-Attention Pooling 和 Additive Margin Softmax(Boss 级别,代码中未实现)。
- 代码结构 :
- 数据准备:解压数据、加载数据集、定义 DataLoader。
- 模型定义:使用 TransformerEncoderLayer 实现分类器。
- 训练:实现训练循环、学习率调度和验证。
- 推理:加载模型,预测测试集扬声器并生成提交文件。
2. Transformer 和 Self-Attention 的原理与应用
先简单讲解 Transformer 和 Self-Attention 的原理,然后结合代码看它们如何被使用。
(1) Transformer 和 Self-Attention 原理
- Transformer :
- 由 Google 在 2017 年论文《Attention is All You Need》提出,是一种基于注意力机制的模型,取代了传统的 RNN。
- 核心组件:自注意力(Self-Attention) 和 前馈神经网络(Feedforward Network)。
- 优点:能并行处理序列(不像 RNN 逐个处理),捕捉长距离依赖。
- Self-Attention :
- 是一种注意力机制,让模型在处理序列中的每个元素时,关注整个序列的其他元素。
- 比如处理"[苹果, 香蕉, 橙子]"时,Self-Attention 会计算:
- "苹果"和其他元素(香蕉、橙子)的相关性。
- "香蕉"和其他元素的相关性,依此类推。
- 计算步骤 :
- 将输入序列(每个元素是一个向量)映射为 Query(Q)、Key(K)、Value(V)三个向量。
- 计算注意力分数:Attention(Q,K,V)=softmax(QKTdk)V \text{Attention}(Q, K, V) = \text{softmax}(\frac{QK^T}{\sqrt{d_k}})V Attention(Q,K,V)=softmax(dkQKT)V。
- 输出一个加权后的向量,表示当前元素对整个序列的关注结果。
(2) 代码中的 Transformer 和 Self-Attention
-
模型定义 (Classifier 类):
python
收起自动换行运行
复制class Classifier(nn.Module): def __init__(self, d_model=80, n_spks=600, dropout=0.1): super().__init__() self.prenet = nn.Linear(40, d_model) self.encoder_layer = nn.TransformerEncoderLayer( d_model=d_model, dim_feedforward=256, nhead=2 ) self.pred_layer = nn.Sequential( nn.Linear(d_model, d_model), nn.ReLU(), nn.Linear(d_model, n_spks), )
- self.prenet:将输入特征(梅尔频谱图,维度为 40)投影到 d_model=80 的维度,为 Transformer 处理做准备。
- self.encoder_layer:使用 PyTorch 的 nn.TransformerEncoderLayer,这是一个标准的 Transformer 编码层,包含:
- Self-Attention:通过 nhead=2 设置多头注意力(Multi-Head Attention),将 d_model 均分为 2 头,每头处理 d_model/nhead=40 维度。
- Feedforward Network:通过 dim_feedforward=256 设置前馈网络的隐藏层维度。
- 默认使用 dropout(0.1)和 ReLU 激活函数。
- 代码中注释掉了一个 nn.TransformerEncoder,原本应该是堆叠多个 TransformerEncoderLayer(比如 num_layers=2),但当前只用了一层。
-
前向传播 (forward 方法):
python
收起自动换行运行
复制def forward(self, mels): out = self.prenet(mels) # (batch size, length, 40) -> (batch size, length, d_model) out = out.permute(1, 0, 2) # (batch size, length, d_model) -> (length, batch size, d_model) out = self.encoder_layer(out) # Transformer 编码 out = out.transpose(0, 1) # (length, batch size, d_model) -> (batch size, length, d_model) stats = out.mean(dim=1) # 平均池化:(batch size, d_model) out = self.pred_layer(stats) # (batch size, n_spks) return out
- Self-Attention 的作用 :
- 输入 mels 是梅尔频谱图,形状为 (batch size, length, 40),表示一个 batch 的音频特征。
- 经过 self.prenet,维度变成 (batch size, length, d_model)。
- out = out.permute(1, 0, 2) 调整维度为 (length, batch size, d_model),因为 TransformerEncoderLayer 期望输入是 (sequence length, batch size, d_model)。
- self.encoder_layer(out) 执行 Self-Attention 和 Feedforward 操作:
- Self-Attention 计算每个时间步(帧)对其他所有帧的关注权重。
- Feedforward 对每个帧独立应用前馈网络。
- 最后通过平均池化(out.mean(dim=1))将序列维度压缩,得到每个样本的特征向量 (batch size, d_model),再通过 self.pred_layer 输出分类结果 (batch size, n_spks)。
- Self-Attention 的作用 :
3. 如何使用 Transformer
通过这个代码,我教你如何在 PyTorch 中使用 Transformer 来完成一个分类任务。
(1) 定义 Transformer 模型
-
使用 nn.TransformerEncoderLayer 构建基本层: python
收起自动换行运行
复制self.encoder_layer = nn.TransformerEncoderLayer( d_model=d_model, # 输入和输出的特征维度 nhead=2, # 多头注意力的头数,d_model 必须能被 nhead 整除 dim_feedforward=256, # 前馈网络的隐藏层维度 dropout=0.1 # dropout 比例 )
-
如果需要堆叠多层,可以用 nn.TransformerEncoder: python
收起自动换行运行
复制self.encoder = nn.TransformerEncoder(self.encoder_layer, num_layers=2)
(代码中注释掉了这部分,当前只用了一层。)
(2) 输入数据准备
- Transformer 需要输入形状为 (sequence length, batch size, d_model):
- 代码中通过 out.permute(1, 0, 2) 调整维度。
- 输入的 mels 是 (batch size, length, 40),先通过 self.prenet 投影到 d_model=80,再调整维度。
(3) 前向传播
- 直接调用 self.encoder_layer(out),PyTorch 会自动处理 Self-Attention 和 Feedforward。
- 输出需要根据任务调整:
- 这里是分类任务,所以用平均池化(out.mean(dim=1))压缩序列维度,然后通过全连接层输出分类结果。
(4) 应用场景
- Transformer 适合处理序列数据(如语音、文本)。
- Self-Attention 让模型能捕捉序列中任意两个位置之间的关系,比如语音中不同帧之间的关联。
4. 如何训练模型
训练一个模型需要准备数据、定义模型、设置优化器和学习率调度器,然后进入训练循环。以下是代码中的训练过程解析。
(1) 数据准备
- 数据集 (myDataset 类):
- 加载梅尔频谱图(torch.load),随机截取 segment_len=128 帧。
- 标签是扬声器 ID(从 mapping.json 中获取)。
- DataLoader (get_dataloader 函数):
- 按 90%(训练)/10%(验证)划分数据集。
- 使用 collate_batch 函数填充批次数据,确保长度一致(填充值为 -20,表示极小的对数值)。
(2) 模型和优化器
- 模型:model = Classifier(n_spks=speaker_num).to(device),初始化 Classifier。
- 损失函数:criterion = nn.CrossEntropyLoss(),用于多分类任务。
- 优化器:optimizer = AdamW(model.parameters(), lr=1e-3),使用 AdamW 优化器,初始学习率 1e-3。
- 学习率调度器 (get_cosine_schedule_with_warmup):
- 包含 Warmup 阶段(前 1000 步,学习率从 0 线性增加到 1e-3)。
- 之后按余弦衰减(Cosine Decay)降低学习率。
(3) 训练循环(main 函数)
-
训练步骤 :
python
收起自动换行运行
复制for step in range(total_steps): batch = next(train_iterator) loss, accuracy = model_fn(batch, model, criterion, device) loss.backward() optimizer.step() scheduler.step() optimizer.zero_grad()
- 每步从 train_loader 获取一个批次。
- 计算损失和准确率(model_fn)。
- 反向传播、优化器更新参数、调度器调整学习率、清空梯度。
-
验证 :
- 每 2000 步(valid_steps)验证一次,计算验证集准确率。
- 保存最佳模型(best_accuracy)。
-
保存模型:每 10,000 步(save_steps)保存最佳模型到 model.ckpt。
(4) 推理(main 函数,推理部分)
- 加载训练好的模型,预测测试集扬声器,生成 output.csv(格式:Id, Category)。
5. 如何调整 Transformer 参数
调整 Transformer 参数是 HW4 的 Medium 级别任务。以下是代码中可以调整的部分,以及调整的意义。
(1) 调整参数的地方
-
在 Classifier 的 init 中: python
收起自动换行运行
复制self.encoder_layer = nn.TransformerEncoderLayer( d_model=d_model, dim_feedforward=256, nhead=2 )
- d_model=80:特征维度,增加会提升模型容量,但计算量更大。
- nhead=2:多头注意力头数,d_model 必须能被整除(当前 80/2=40)。
- dim_feedforward=256:前馈网络隐藏层维度,增加会增强模型表达能力。
-
堆叠多层(当前注释掉了): python
收起自动换行运行
复制self.encoder = nn.TransformerEncoder(self.encoder_layer, num_layers=2)
- num_layers=2:增加层数会让模型更深,能捕捉更复杂的模式,但可能过拟合。
(2) 调整建议
- 增大 d_model:比如从 80 增加到 128,增强模型容量,但需要确保 nhead 能整除(比如 nhead=4)。
- 增加 nhead:比如从 2 增加到 4,允许多头注意力捕捉更多不同类型的依赖关系。
- 调整 dim_feedforward:比如从 256 增加到 512,增强前馈网络的能力。
- 增加层数:取消注释 self.encoder,设置 num_layers=3,让模型更深。
- 调整 dropout:默认 0.1,可以尝试 0.2 或 0.05,防止过拟合。
- 效果:文档中提到(HW04.pdf 第13页),调整参数后公共基准从 0.82523(Simple)提升到 0.90547(Medium)。
(3) 注意事项
- 增加参数会增加计算量,可能需要更大的 batch size 或更长的训练时间。
- 过大的模型可能过拟合,需要调整学习率或增加 dropout。
6. 总结:如何使用 Transformer 训练模型
- 步骤 :
- 准备数据:加载梅尔频谱图,划分训练/验证集,用 DataLoader 批次加载。
- 定义模型:使用 nn.TransformerEncoderLayer,设置 d_model、nhead 等参数。
- 前向传播:通过 Self-Attention 处理序列,池化后输出分类结果。
- 训练:设置优化器、学习率调度器,循环计算损失、更新参数。
- 调整参数:修改 d_model、nhead、num_layers 等,优化性能。
- Self-Attention 的作用:捕捉序列中帧与帧之间的关系,比如语音中不同时间点的关联。
- 训练技巧 :
- 使用 Warmup 学习率调度(代码中已实现)。
- 定期验证,保存最佳模型。
- 调整参数时,观察验证集准确率,避免过拟合。
7. 动手实践
如果你想自己训练一个类似模型,可以用以下步骤:
- 环境准备 :
- 安装 PyTorch:pip install torch
- 下载数据集(代码中的链接已失效,可用其他语音数据集如 LibriSpeech 替代)。
- 修改代码 :
- 调整 d_model=128、nhead=4、num_layers=3,观察效果。
- 实现 Conformer(参考 https://arxiv.org/abs/2005.08100)。
- 训练 :
- 运行 main 函数,设置 total_steps=70000,观察验证准确率。
- 推理 :
- 用训练好的模型预测测试集,生成提交文件
逐行讲解 Conformer 代码实现
由于你之前没有接触过 Conformer,我会从零开始详细讲解之前提供的 ConformerBlock 和 ConformerConvModule 的代码实现,逐行分析其功能、原理和实现细节。Conformer 是 Transformer 的变种,专为语音任务设计,结合了全局建模(Self-Attention)和局部建模(卷积)的优势。以下代码来自之前修改的 hw04.py,我们会重点聚焦 Conformer 的实现部分。
1. Conformer 代码实现
以下是 ConformerConvModule 和 ConformerBlock 的完整代码,我会逐行解释其作用。
python
import torch
import torch.nn as nn
import torch.nn.functional as F
# 卷积模块,用于捕捉局部特征
class ConformerConvModule(nn.Module):
def __init__(self, d_model=80, kernel_size=31, dropout=0.1):
super().__init__()
# Pointwise Convolution 1
self.pointwise_conv1 = nn.Conv1d(d_model, d_model * 2, kernel_size=1, stride=1, padding=0, bias=True)
self.glu = nn.GLU(dim=1) # Gated Linear Unit
# Depthwise Convolution
self.depthwise_conv = nn.Conv1d(
d_model,
d_model,
kernel_size=kernel_size,
stride=1,
padding=(kernel_size - 1) // 2,
groups=d_model, # Depthwise
bias=True
)
self.bn = nn.BatchNorm1d(d_model)
self.swish = nn.Swish()
# Pointwise Convolution 2
self.pointwise_conv2 = nn.Conv1d(d_model, d_model, kernel_size=1, stride=1, padding=0, bias=True)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
# x: (batch, length, d_model) -> (batch, d_model, length) for conv
x = x.transpose(1, 2)
# Pointwise Conv 1 + GLU
x = self.pointwise_conv1(x)
x = self.glu(x)
# Depthwise Conv + BN + Swish
x = self.depthwise_conv(x)
x = self.bn(x)
x = self.swish(x)
# Pointwise Conv 2
x = self.pointwise_conv2(x)
x = self.dropout(x)
# Back to (batch, length, d_model)
x = x.transpose(1, 2)
return x
# Conformer 块,包含 FFN、Self-Attention 和卷积模块
class ConformerBlock(nn.Module):
def __init__(self, d_model=80, nhead=2, dim_feedforward=256, dropout=0.1, kernel_size=31):
super().__init__()
# Feed-Forward Module (half-step)
self.ffn1 = nn.Sequential(
nn.LayerNorm(d_model),
nn.Linear(d_model, dim_feedforward),
nn.Swish(),
nn.Dropout(dropout),
nn.Linear(dim_feedforward, d_model)
)
# Multi-Head Self-Attention
self.self_attention = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
self.norm1 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
# Convolution Module
self.conv_module = ConformerConvModule(d_model, kernel_size, dropout)
self.norm2 = nn.LayerNorm(d_model)
self.dropout2 = nn.Dropout(dropout)
# Feed-Forward Module (half-step)
self.ffn2 = nn.Sequential(
nn.LayerNorm(d_model),
nn.Linear(d_model, dim_feedforward),
nn.Swish(),
nn.Dropout(dropout),
nn.Linear(dim_feedforward, d_model)
)
self.norm3 = nn.LayerNorm(d_model)
self.dropout3 = nn.Dropout(dropout)
def forward(self, x):
# x: (length, batch, d_model)
# FFN 1 (half-step)
x = x + 0.5 * self.dropout1(self.ffn1(x))
# Multi-Head Self-Attention
attn_output, _ = self.self_attention(x, x, x)
x = self.norm1(x + self.dropout1(attn_output))
# Convolution Module
x = self.norm2(x + self.dropout2(self.conv_module(x)))
# FFN 2 (half-step)
x = self.norm3(x + self.dropout3(self.ffn2(x)))
return x
2. 逐行讲解 ConformerConvModule
(1) 初始化方法 init
python
收起自动换行运行
复制
class ConformerConvModule(nn.Module): def __init__(self, d_model=80, kernel_size=31, dropout=0.1):
- class ConformerConvModule(nn.Module):定义一个卷积模块,继承 PyTorch 的 nn.Module 类,所有神经网络模块都需要继承这个类。
- d_model=80:输入和输出的特征维度(类似 Transformer 的隐藏维度)。
- kernel_size=31:卷积核大小,决定了捕捉局部特征的范围(越大,感受野越大)。
- dropout=0.1:Dropout 比例,防止过拟合。
python
收起自动换行运行
复制
super().__init__()
- 调用父类 nn.Module 的初始化方法,确保正确初始化模块。
python
收起自动换行运行
复制
self.pointwise_conv1 = nn.Conv1d(d_model, d_model * 2, kernel_size=1, stride=1, padding=0, bias=True)
- nn.Conv1d:一维卷积,适用于序列数据(如语音的梅尔频谱图)。
- d_model:输入通道数(特征维度)。
- d_model * 2:输出通道数,升维到两倍,用于后续 GLU(Gated Linear Unit)操作。
- kernel_size=1:点卷积(Pointwise Convolution),只对每个时间步独立操作,不涉及邻域。
- stride=1:步幅为 1,不改变序列长度。
- padding=0:无填充,因为 kernel_size=1 不需要填充。
- bias=True:包含偏置参数。
python
收起自动换行运行
复制
self.glu = nn.GLU(dim=1) # Gated Linear Unit
- nn.GLU:Gated Linear Unit,一种门控机制。
- dim=1:在通道维度上操作(因为输入是 (batch, channel, length))。
- GLU 的作用:将 d_model * 2 的通道分成两部分,一部分作为值,另一部分通过 sigmoid 激活作为门控,输出 d_model 个通道。公式为: GLU(x)=x1⋅σ(x2)\text{GLU}(x) = x_1 \cdot \sigma(x_2)GLU(x)=x1⋅σ(x2) 其中 x1 x_1 x1 和 x2 x_2 x2 是通道拆分的两部分。
python
收起自动换行运行
复制
self.depthwise_conv = nn.Conv1d( d_model, d_model, kernel_size=kernel_size, stride=1, padding=(kernel_size - 1) // 2, groups=d_model, # Depthwise bias=True )
- nn.Conv1d:定义深度可分离卷积(Depthwise Convolution)。
- d_model:输入和输出通道数保持一致。
- kernel_size=31:卷积核大小,捕捉局部特征。
- stride=1:步幅为 1。
- padding=(kernel_size - 1) // 2:自动计算填充,确保输出长度不变(例如 kernel_size=31 时,padding=15)。
- groups=d_model:深度卷积,每个输入通道独立卷积,减少参数量。
- bias=True:包含偏置。
python
收起自动换行运行
复制
self.bn = nn.BatchNorm1d(d_model)
- nn.BatchNorm1d:一维批归一化,作用于通道维度。
- 归一化每个通道的特征,加速训练,稳定梯度。
python
收起自动换行运行
复制
self.swish = nn.Swish()
- nn.Swish:激活函数,公式为 Swish(x)=x⋅σ(x) \text{Swish}(x) = x \cdot \sigma(x) Swish(x)=x⋅σ(x),比 ReLU 更平滑。
python
收起自动换行运行
复制
self.pointwise_conv2 = nn.Conv1d(d_model, d_model, kernel_size=1, stride=1, padding=0, bias=True)
- 第二个点卷积,将特征降维回 d_model。
python
收起自动换行运行
复制
self.dropout = nn.Dropout(dropout)
- Dropout 层,随机丢弃部分神经元,防止过拟合。
(2) 前向传播方法 forward
python
收起自动换行运行
复制
def forward(self, x):
- 定义前向传播,输入 x 是 (batch, length, d_model) 的张量。
python
收起自动换行运行
复制
x = x.transpose(1, 2)
- transpose(1, 2):将 (batch, length, d_model) 转换为 (batch, d_model, length),因为 nn.Conv1d 期望输入是 (batch, channel, length)。
python
收起自动换行运行
复制
x = self.pointwise_conv1(x)
- 应用第一个点卷积,将通道数从 d_model 升到 d_model * 2。
python
收起自动换行运行
复制
x = self.glu(x)
- 应用 GLU,将通道数降回 d_model,并通过门控机制选择性保留信息。
python
收起自动换行运行
复制
x = self.depthwise_conv(x)
- 应用深度卷积,捕捉局部特征(kernel_size=31 覆盖 31 个时间步)。
python
收起自动换行运行
复制
x = self.bn(x)
- 应用批归一化,稳定特征分布。
python
收起自动换行运行
复制
x = self.swish(x)
- 应用 Swish 激活,增加非线性。
python
收起自动换行运行
复制
x = self.pointwise_conv2(x)
- 应用第二个点卷积,进一步处理特征,保持维度为 (batch, d_model, length)。
python
收起自动换行运行
复制
x = self.dropout(x)
- 应用 Dropout,防止过拟合。
python
收起自动换行运行
复制
x = x.transpose(1, 2)
- 将维度转回 (batch, length, d_model),与输入一致。
python
收起自动换行运行
复制
return x
- 返回处理后的张量。
3. 逐行讲解 ConformerBlock
(1) 初始化方法 init
python
收起自动换行运行
复制
class ConformerBlock(nn.Module): def __init__(self, d_model=80, nhead=2, dim_feedforward=256, dropout=0.1, kernel_size=31):
- 定义 Conformer 块,参数与 ConformerConvModule 类似。
- nhead=2:多头注意力的头数。
- dim_feedforward=256:前馈网络的隐藏层维度。
python
收起自动换行运行
复制
super().__init__()
python
收起自动换行运行
复制
self.ffn1 = nn.Sequential( nn.LayerNorm(d_model), nn.Linear(d_model, dim_feedforward), nn.Swish(), nn.Dropout(dropout), nn.Linear(dim_feedforward, d_model) )
- 定义第一个前馈模块(FFN1)。
- nn.LayerNorm(d_model):层归一化,归一化每个时间步的特征。
- nn.Linear(d_model, dim_feedforward):将维度从 d_model 扩展到 dim_feedforward。
- nn.Swish():Swish 激活。
- nn.Dropout(dropout):Dropout。
- nn.Linear(dim_feedforward, d_model):降维回 d_model。
python
收起自动换行运行
复制
self.self_attention = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
- nn.MultiheadAttention:多头自注意力。
- d_model:输入维度。
- nhead=2:头数,每头处理 d_model/nhead=40 维度。
- dropout=dropout:注意力中的 Dropout。
python
收起自动换行运行
复制
self.norm1 = nn.LayerNorm(d_model) self.dropout1 = nn.Dropout(dropout)
- norm1 和 dropout1:用于自注意力后的归一化和 Dropout。
python
收起自动换行运行
复制
self.conv_module = ConformerConvModule(d_model, kernel_size, dropout)
- 调用 ConformerConvModule,处理局部特征。
python
收起自动换行运行
复制
self.norm2 = nn.LayerNorm(d_model) self.dropout2 = nn.Dropout(dropout)
- norm2 和 dropout2:卷积模块后的归一化和 Dropout。
python
收起自动换行运行
复制
self.ffn2 = nn.Sequential( nn.LayerNorm(d_model), nn.Linear(d_model, dim_feedforward), nn.Swish(), nn.Dropout(dropout), nn.Linear(dim_feedforward, d_model) )
- 定义第二个前馈模块(FFN2),结构与 FFN1 相同。
python
收起自动换行运行
复制
self.norm3 = nn.LayerNorm(d_model) self.dropout3 = nn.Dropout(dropout)
- norm3 和 dropout3:FFN2 后的归一化和 Dropout。
(2) 前向传播方法 forward
python
收起自动换行运行
复制
def forward(self, x):
- 输入 x 是 (length, batch, d_model),符合 Transformer 的输入格式。
python
收起自动换行运行
复制
x = x + 0.5 * self.dropout1(self.ffn1(x))
- 应用 FFN1,半步前馈(系数 0.5 是 Conformer 的设计)。
- self.ffn1(x):通过 FFN1 处理。
- self.dropout1(...):应用 Dropout。
- x + 0.5 * ...:残差连接,稳定训练。
python
收起自动换行运行
复制
attn_output, _ = self.self_attention(x, x, x)
- 应用多头自注意力。
- self.self_attention(x, x, x):输入 Query、Key、Value 均为 x,计算自注意力。
- attn_output:注意力输出,形状仍为 (length, batch, d_model)。
- _:忽略注意力权重。
python
收起自动换行运行
复制
x = self.norm1(x + self.dropout1(attn_output))
- x + self.dropout1(attn_output):残差连接。
- self.norm1(...):层归一化。
python
收起自动换行运行
复制
x = self.norm2(x + self.dropout2(self.conv_module(x)))
- 应用卷积模块。
- self.conv_module(x):通过 ConformerConvModule 处理。
- self.dropout2(...):Dropout。
- x + ...:残差连接。
- self.norm2(...):层归一化。
python
收起自动换行运行
复制
x = self.norm3(x + self.dropout3(self.ffn2(x)))
- 应用 FFN2,与 FFN1 类似。
python
收起自动换行运行
复制
return x
- 返回处理后的张量,形状不变。
4. 总结与使用
- ConformerConvModule:通过卷积捕捉局部特征,适合语音任务中的短时相关性。
- ConformerBlock:结合 FFN、Self-Attention 和卷积,平衡全局和局部建模。
- 如何使用 :
- 在 Classifier 中替换 TransformerEncoderLayer 为 ConformerBlock。
- 堆叠多层:self.encoder = nn.Sequential(*[ConformerBlock(...) for _ in range(2)])。
- 调整参数(如 d_model、kernel_size)优化性能。