Skip to content

使用卷积网络进行序列处理

PyTorch - 使用一维卷积神经网络 (ConvNets) 进行序列处理

Section titled “PyTorch - 使用一维卷积神经网络 (ConvNets) 进行序列处理”

卷积神经网络(CNNs 或 ConvNets)在计算机视觉领域取得了巨大成功,同时对于序列处理任务也非常有效。与用于图像的二维卷积(2D convolutions)不同,一维卷积(1D convolutions)应用于文本或时间序列等序列数据。本章介绍如何在 PyTorch 中构建和使用一维 ConvNets 进行序列建模。

一维 ConvNets 在序列上应用滤波器(filters),捕捉局部模式或 n-gram(在文本中)。主要优点包括参数共享(减少模型大小)和分层特征学习。它们对于文本分类等任务非常高效且性能良好。

我们将需要标准的 PyTorch 模块来构建我们的网络。

import torch
import torch.nn as nn
import torch.nn.functional as F
import 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

使用此模型处理文本涉及几个预处理步骤:

  1. 分词 (Tokenization): 将文本分割成独立的词或子词。
  2. 构建词汇表 (Vocabulary Building): 创建从 token 到唯一整数 ID 的映射。
  3. 数值化 (Numericalization): 将 token 序列转换为整数 ID 序列。
  4. 填充/截断 (Padding/Truncation): 通过填充较短的序列或截断较长的序列,确保批量中所有序列具有相同的长度。nn.Embedding 中的 pad_idx 在这里很重要。
  5. 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 思想)。