通过连接主义时间分类(CTC),可将各个组件组装成一个可训练的声学模型。主要目标是设计一个神经网络,它能将音频特征序列转换为字符词汇上的概率分布序列。这个输出正是CTC损失函数计算损失并训练网络所需。一个标准的基于CTC的模型是多个神经网络层的堆叠,每个层都有其特定的职责。此架构旨在处理语音的序列性和可变长度特性。模型架构该模型通常由三个主要部分构成:用于处理序列的循环层、用于将特征投射到词汇空间的线性层,以及用于生成概率分布的Softmax层。digraph G { rankdir=TB; splines=ortho; node [shape=box, style="filled,rounded", fontname="Arial", fontsize=10]; edge [fontname="Arial", fontsize=9]; subgraph cluster_model { label="基于CTC的声学模型"; bgcolor="#e9ecef"; fontname="Arial"; fontsize=12; input [label="输入特征\n(对数梅尔频谱图)\n形状: (批次, T, D)", fillcolor="#a5d8ff"]; rnn [label="双向LSTM/GRU层\n(捕捉时间上下文)", fillcolor="#bac8ff"]; dense [label="时间分布全连接层\n(投射到词汇大小)", fillcolor="#d0bfff"]; softmax [label="Softmax激活\n(生成概率)", fillcolor="#eebefa"]; output [label="输出概率矩阵\n形状: (批次, T, V+1)", fillcolor="#fcc2d7"]; input -> rnn; rnn -> dense; dense -> softmax; softmax -> output; } ctc_loss [label="CTC损失函数", shape=invhouse, fillcolor="#ffc9c9"]; target [label="目标文本\n(Y)", shape=note, fillcolor="#b2f2bb"]; output -> ctc_loss [label="log_probs"]; target -> ctc_loss [label="targets"]; }数据在典型的基于CTC的声学模型中的流向。特征序列经过循环层处理,投射到词汇维度,并转换为概率。然后,CTC损失函数将此输出与目标文本进行比较,以训练模型。我们来逐一分析此架构的每个组件。循环主体:LSTM或GRU声学模型的主要组成部分是一系列循环层,最常见的是长短期记忆(LSTM)或门控循环单元(GRU)网络。选择这些层是因为语音本质上是序列化的;某个时刻声音的含义通常取决于它之前和之后的声音。双向性: 为了让模型能力更强,我们使用双向LSTM。标准的LSTM从左到右处理输入序列。然而,双向LSTM使用两个独立的LSTM:一个从左到右处理序列(正向传递),另一个从右到左处理序列(反向传递)。两者的输出在每个时间步连接起来。这使得模型在时间步$t$的预测能够同时结合过去上下文(来自正向LSTM)和未来上下文(来自反向LSTM),这对语音处理非常有利。这个循环主体的输入是特征序列批次,通常形状为(批次大小, 时间步, 特征数量)。输出是隐藏状态的新序列,每个时间步对应一个。如果使用隐藏层大小为H的双向LSTM,每个序列的输出形状将是(时间步, 2 * H)。投影层:时间分布全连接层在循环层处理完音频特征并捕获时间模式后,我们需要将其输出映射到我们所需的词汇。这通过一个标准的完全连接(或称“全连接”)层完成。此层独立应用于LSTM输出序列中的每个时间步。这通常被称为“时间分布”全连接层。它的作用是获取每个时间步的隐藏状态,并将其投射到一个向量中,该向量的长度等于词汇表大小加一。词汇量: 词汇表包含模型可以输出的所有字符(例如,'a'到'z'、空格、标点符号),以及CTC所需的一个特殊<blank>标记。如果我们的字符集有28个符号,则此层的输出维度将是29。此层在每个时间步的输出是一个由原始、未归一化分数组成的向量,称为logits。对于一个批次,此层的输入可能是(批次大小, 时间步, 2 * H),其输出将是(批次大小, 时间步, 词汇大小 + 1)。激活层:Softmax模型内的最后一步是将全连接层输出的原始logit分数转换为概率。将Softmax激活函数应用于输出张量的最后一个维度(词汇维度)。对于每个时间步,Softmax函数将logit向量归一化为概率分布,所有值介于0和1之间且总和为1。结果张量通常被称为发射矩阵或概率矩阵,其形状为(批次大小, 时间步, 词汇大小 + 1)。(t, c)处的每个条目代表在时间步t观察到字符c的概率。此概率矩阵是训练期间声学模型的最终输出,也是CTC损失函数所需的输入。一个高级实现结构在PyTorch或TensorFlow这样的框架中,您可以将此架构定义为一系列层。以下示例呈现了PyTorch中的简化结构。import torch import torch.nn as nn class CTCAcousticModel(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim, n_layers=2): super(CTCAcousticModel, self).__init__() # 用于处理特征序列的循环层 self.lstm = nn.LSTM( input_size=input_dim, hidden_size=hidden_dim, num_layers=n_layers, bidirectional=True, batch_first=True # 期望输入为 (批次, 序列, 特征) ) # 将LSTM输出映射到词汇大小的全连接层 # 输入维度是 hidden_dim * 2,因为LSTM是双向的 self.fc = nn.Linear(hidden_dim * 2, output_dim) def forward(self, x): # x 形状: (批次, 时间步, 特征数量) # 将输入通过LSTM层 lstm_out, _ = self.lstm(x) # lstm_out 形状: (批次, 时间步, hidden_dim * 2) # 将每个时间步的输出通过全连接层 logits = self.fc(lstm_out) # logits 形状: (批次, 时间步, 输出维度) # 大多数CTC损失实现为了数值稳定性,期望对数概率 return nn.functional.log_softmax(logits, dim=2)这个类封装了整个流程。它接收一个特征序列批次,并返回一个对数概率矩阵批次,这些矩阵已准备好与真实转录本一起传递给CTC损失函数。在下一节中,我们将在实践环节中结合所有这些来训练我们的第一个声学模型。