使用卷积网络进行序列处理
PyTorch - 使用一维卷积神经网络 (ConvNets) 进行序列处理
Section titled “PyTorch - 使用一维卷积神经网络 (ConvNets) 进行序列处理”卷积神经网络(CNNs 或 ConvNets)在计算机视觉领域取得了巨大成功,同时对于序列处理任务也非常有效。与用于图像的二维卷积(2D convolutions)不同,一维卷积(1D convolutions)应用于文本或时间序列等序列数据。本章介绍如何在 PyTorch 中构建和使用一维 ConvNets 进行序列建模。
一维 ConvNets 在序列上应用滤波器(filters),捕捉局部模式或 n-gram(在文本中)。主要优点包括参数共享(减少模型大小)和分层特征学习。它们对于文本分类等任务非常高效且性能良好。
步骤 1:核心 PyTorch 导入
Section titled “步骤 1:核心 PyTorch 导入”我们将需要标准的 PyTorch 模块来构建我们的网络。
import torchimport torch.nn as nnimport torch.nn.functional as Fimport torch.optim as optim步骤 2:定义一个用于序列的基础一维 ConvNet
Section titled “步骤 2:定义一个用于序列的基础一维 ConvNet”让我们定义一个适合文本分类等任务的简单一维 ConvNet。典型的架构包括:
- 一个嵌入层(embedding layer),将输入 token ID 转换为密集向量。
- 一个或多个一维卷积层(1D convolutional layers),用于提取特征。
- 卷积后应用激活函数(如 ReLU)。
- 一个池化层(pooling layer)(如 Max Pooling),用于降低维度并捕捉最重要的特征。
- 一个全连接层(fully connected layer),用于生成最终输出(例如,类别概率)。
class BasicSequenceCNN(nn.Module): def __init__(self, vocab_size, embedding_dim, n_filters, filter_size, output_dim, pad_idx): super().__init__()
# 1. Embedding Layer # Maps token indices to dense vectors. padding_idx ensures padded tokens are zero vectors and don't contribute to gradients. self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=pad_idx)
# 2. 1D Convolutional Layer # in_channels = embedding_dim (features per token) # out_channels = n_filters (number of filters/feature maps to produce) # kernel_size = filter_size (like n-gram size, e.g., 3 for trigrams) self.conv = nn.Conv1d(in_channels=embedding_dim, out_channels=n_filters, kernel_size=filter_size)
# 3. Fully Connected Layer # Takes pooled features and maps to output_dim (e.g., number of classes) self.fc = nn.Linear(n_filters, output_dim)
def forward(self, text_indices): # text_indices shape: (batch_size, seq_len)
# 通过嵌入层 # embedded shape: (batch_size, seq_len, embedding_dim) embedded = self.embedding(text_indices)
# Conv1d 期望的输入 shape: (batch_size, in_channels/embedding_dim, seq_len) # 因此,我们交换维度 1 和 2。 embedded = embedded.permute(0, 2, 1) # permuted embedded shape: (batch_size, embedding_dim, seq_len)
# 通过卷积层 # conved shape: (batch_size, n_filters, seq_len - filter_size + 1) conved = self.conv(embedded)
# 应用 ReLU 激活 conved = F.relu(conved)
# 在时间维度(序列长度维度)上执行全局最大池化 (Global Max Pooling) # 这会从每个特征图中取最大值。 # conved.shape[2] 是卷积后序列的长度。 # pooled shape 在 squeeze 之前: (batch_size, n_filters, 1) pooled = F.max_pool1d(conved, kernel_size=conved.shape[2]) # pooled shape 在 squeeze 之后: (batch_size, n_filters) pooled = pooled.squeeze(2)
# 通过全连接层 # output shape: (batch_size, output_dim) output = self.fc(pooled) return output步骤 3:数据准备(概念性)
Section titled “步骤 3:数据准备(概念性)”使用此模型处理文本涉及几个预处理步骤:
- 分词 (Tokenization): 将文本分割成独立的词或子词。
- 构建词汇表 (Vocabulary Building): 创建从 token 到唯一整数 ID 的映射。
- 数值化 (Numericalization): 将 token 序列转换为整数 ID 序列。
- 填充/截断 (Padding/Truncation): 通过填充较短的序列或截断较长的序列,确保批量中所有序列具有相同的长度。
nn.Embedding中的pad_idx在这里很重要。 - DataLoader: 使用 PyTorch 的
Dataset和DataLoader来高效地加载和批量处理数据。
例如,‘hello world’ 在分词和数值化后可能变成 [10, 15]。如果要求的序列长度是 5,填充 token 的 ID 是 0,则它会变成 [10, 15, 0, 0, 0]。
步骤 4:模型训练(概念性概述)
Section titled “步骤 4:模型训练(概念性概述)”一个典型的训练循环包括:
# --- 超参数和实例化 (示例) ---# VOCAB_SIZE = 5000 # 示例:你的词汇表大小# EMBEDDING_DIM = 100 # 词嵌入维度# N_FILTERS = 64 # 卷积层中的滤波器数量# FILTER_SIZE = 3 # 卷积层的 Kernel size(例如,用于三元词 n-gram)# OUTPUT_DIM = 2 # 示例:用于二分类(例如,情感积极/消极)# PAD_IDX = 1 # 词汇表中填充 token 的索引# LEARNING_RATE = 0.001
# device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# model = BasicSequenceCNN(VOCAB_SIZE, EMBEDDING_DIM, N_FILTERS, FILTER_SIZE, OUTPUT_DIM, PAD_IDX).to(device)
# --- 损失函数和优化器 ---# criterion = nn.CrossEntropyLoss() # 如果 OUTPUT_DIM > 1 且使用原始分数# # 如果 OUTPUT_DIM = 1 用于二分类,使用 nn.BCEWithLogitsLoss()# optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
# --- 示例用虚拟输入 ---# BATCH_SIZE = 32# SEQ_LEN = 50# dummy_input_indices = torch.randint(0, VOCAB_SIZE, (BATCH_SIZE, SEQ_LEN), dtype=torch.long).to(device)# # 在实际数据中如果序列长度可变,请确保正确使用 PAD_IDX# # dummy_labels = torch.randint(0, OUTPUT_DIM, (BATCH_SIZE,), dtype=torch.long).to(device)
# --- 训练周期 (概念性) ---# model.train() # 设置模型为训练模式# optimizer.zero_grad()# predictions = model(dummy_input_indices)# # loss = criterion(predictions, dummy_labels)# # loss.backward()# # optimizer.step()# # print(f'Loss: {loss.item()}')
# --- 评估 (概念性) ---# model.eval() # 设置模型为评估模式# with torch.no_grad():# pass # 在验证集上执行评估print("BasicSequenceCNN 已定义。请实现数据加载和训练循环以使用。")一维 ConvNets 在序列处理中的应用
Section titled “一维 ConvNets 在序列处理中的应用”一维 ConvNets 对于各种基于序列的任务都具有通用性:
- 文本分类 (Text Classification): 情感分析、主题分类、垃圾邮件检测。
- 命名实体识别 (NER): 在文本中识别实体,如人名、地点、组织(通常与其他层结合使用)。
- 时间序列分析 (Time Series Analysis): 传感器数据或金融数据中的预测、异常检测。
- DNA/蛋白质序列分析 (DNA/Protein Sequence Analysis): 在生物序列中查找模式。
虽然存在更复杂的架构,如 LSTMs、GRUs 和 Transformers,但一维 ConvNets 为许多序列处理问题提供了一个强大且计算高效的基线。你可以探索各种变体,例如使用具有不同滤波器大小的多个卷积层,并连接它们的输出来进行更丰富的特征提取(类似于计算机视觉中的 Inception 思想)。