趋近智
本次实践练习将利用 LSTM 和联结主义时间分类 (CTC) 损失,构建并训练声学模型。它将指导您完成构建一个简单但功能完备的端到端语音识别系统,并使用 PyTorch 实现。我们将使用一个小型、易于管理的数据集,以便专注于实现细节,而不受漫长训练时间的困扰。
我们的目标是创建一个模型,该模型将梅尔频谱图作为输入,并输出一个字符概率序列,然后 CTC 损失函数使用这些概率序列来计算梯度并训练网络。
首先,请确保您已安装所需的库。我们将主要使用 torch 和 torchaudio 进行建模和数据处理,使用 librosa 进行特征提取。
pip install torch torchaudio librosa
对于本次练习,我们假设您有一个预处理过的数据集,包含音频文件和一个对应的元数据文件(例如 .csv 或 .json),该文件将每个音频文件映射到其转录文本。接下来,我们定义一个 PyTorch Dataset 来处理数据的加载、处理和分词。
首要一步是定义我们的字母表。模型的输出层必须为每个可能的字符设置一个节点,以及一个用于 CTC 所需的特殊 blank 标记的额外节点。
# 一个简单的英文字符集
# 空白标记通常在索引 0
char_map_str = """
' 0
<SPACE> 1
a 2
b 3
c 4
d 5
e 6
f 7
g 8
h 9
i 10
j 11
k 12
l 13
m 14
n 15
o 16
p 17
q 18
r 19
s 20
t 21
u 22
v 23
w 24
x 25
y 26
z 27
"""
# 在实际项目中,您会根据训练数据的转录文本生成此内容。
接下来,我们为 DataLoader 创建一个自定义的 collate_fn。由于每个音频片段及其转录文本的长度不同,我们无法简单地将它们堆叠成一个批次。此函数将把批次中的每个序列填充到最长序列的长度,并且它还会记录原始的、未填充的长度。CTC 损失函数需要这些原始长度才能正常工作。
# 在您的数据加载脚本中
import torch
import torchaudio
def collate_fn(batch):
# 一个批次包含一个元组列表:(频谱图, 标签, 输入长度, 标签长度)
spectrograms = [item[0] for item in batch]
labels = [item[1] for item in batch]
input_lengths = [item[2] for item in batch]
label_lengths = [item[3] for item in batch]
# 填充频谱图和标签
padded_spectrograms = torch.nn.utils.rnn.pad_sequence(spectrograms, batch_first=True)
padded_labels = torch.nn.utils.rnn.pad_sequence(labels, batch_first=True)
return padded_spectrograms, padded_labels, torch.tensor(input_lengths), torch.tensor(label_lengths)
这个 collate_fn 是在 PyTorch 中处理变长序列数据时的标准模式,对于批处理我们的语音数据很必要。
我们的声学模型将采用一种直接的架构。它将包含几个双向 LSTM 层,之后是一个全连接线性层。LSTM 负责学习语音特征中的时间模式,而最终的线性层将 LSTM 的输出投影到每个时间步的字符词汇表上的概率分布。
让我们在 PyTorch 中定义这个模型。
import torch.nn as nn
class LSTMAcousticModel(nn.Module):
def __init__(self, n_features, n_hidden, n_class, n_layers, dropout):
super(LSTMAcousticModel, self).__init__()
# LSTM 层
self.lstm = nn.LSTM(
input_size=n_features,
hidden_size=n_hidden,
num_layers=n_layers,
batch_first=True,
bidirectional=True,
dropout=dropout
)
# 分类层
# 输出为 2 * n_hidden,因为 LSTM 是双向的
self.classifier = nn.Linear(n_hidden * 2, n_class)
def forward(self, x):
# x 是输入频谱图:(批次, 时间, 特征)
lstm_out, _ = self.lstm(x)
# 将 LSTM 输出通过分类器
# 输出为 (批次, 时间, 类别数)
output = self.classifier(lstm_out)
# CTC 损失需要 log_softmax
# CTC 损失期望时间维度在前
return nn.functional.log_softmax(output, dim=2).permute(1, 0, 2)
下图呈现了数据流经我们模型的路径。输入频谱图由双向 LSTM 处理,生成的隐藏状态被传递到线性层,该线性层生成 CTC 所需的字符概率。
简单基于 LSTM 的声学模型内的数据流。代码中最后的
permute操作会重新排列张量维度,以符合 PyTorch 的CTCLoss所期望的格式。
模型和数据加载器准备就绪后,我们可以编写主要的训练函数。此函数将安排将数据馈送给模型、计算损失以及更新模型权重的过程。
此过程的核心是 torch.nn.CTCLoss。它需要四个参数:
log_probs: 我们模型输出的对数概率,形状为 (时间, 批次, 类别)。targets: 批次中所有字符标签连接在一起的 1D 张量。input_lengths: 包含批次中每个频谱图原始长度的张量。target_lengths: 包含批次中每个转录文本原始长度的张量。这是一个用于单个训练周期的简化函数。
def train_epoch(model, device, train_loader, criterion, optimizer):
model.train()
running_loss = 0.0
for i, batch in enumerate(train_loader):
# 将数据移动到选定的设备(例如 GPU)
spectrograms, labels, input_lengths, label_lengths = batch
spectrograms, labels = spectrograms.to(device), labels.to(device)
optimizer.zero_grad()
# 前向传播
# 输出形状为 (时间, 批次, 类别数)
output = model(spectrograms)
# 计算 CTC 损失
loss = criterion(output, labels, input_lengths, label_lengths)
# 反向传播和优化
loss.backward()
optimizer.step()
running_loss += loss.item()
avg_loss = running_loss / len(train_loader)
print(f"Training Loss: {avg_loss:.4f}")
return avg_loss
要运行训练,您需要实例化模型、损失函数和优化器,然后在循环中调用 train_epoch 函数。
# 超参数
n_features = 128 # 梅尔频谱图中的特征数量
n_hidden = 256
n_class = 28 # 词汇表中的字符数量 + 1(用于空白标记)
n_layers = 2
dropout = 0.2
learning_rate = 1e-4
epochs = 10
# 设置
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = LSTMAcousticModel(n_features, n_hidden, n_class, n_layers, dropout).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
criterion = nn.CTCLoss(blank=0).to(device) # blank=0 假定空白标记在索引 0
# 仅用于演示的虚拟训练加载器和验证加载器
# 实际中,它们会是 torch.utils.data.DataLoader 实例
# train_loader = ...
# valid_loader = ...
# 训练循环
for epoch in range(epochs):
print(f"Epoch {epoch+1}/{epochs}")
train_loss = train_epoch(model, device, train_loader, criterion, optimizer)
# 通常您也会在此处运行验证循环
# valid_loss = evaluate(...)
随着训练的进行,您应该看到 CTC 损失下降,这表明模型正在学习将音频特征映射到正确的字符序列。绘制每个周期的训练和验证损失是一种标准方法,用于监控此过程并检查过拟合。良好的训练过程会显示两个损失值稳步下降。
LSTM-CTC 模型的训练曲线示例。训练损失和验证损失之间的差距表明模型可能开始过拟合,这是一个常见问题,可以通过增加数据、正则化或数据增强来解决。
训练结束后,您可以使用简单的贪心解码器来转录新的音频文件。这包括将频谱图通过模型,在每个时间步获取输出概率的 argmax 以得到最可能的字符,然后折叠重复字符并删除空白标记以生成最终文本。
本次动手实践提供了一个完整(尽管简单)的声学模型训练流程。您已成功构建了一个能从音频特征学习转录语音的系统。尽管这个模型是一个很好的开始,但其性能可以通过更先进的架构和解码技术显著提高,我们将在后续章节中介绍这些内容。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造