当声学模型处理完音频信号后,它会生成一个概率矩阵。对于每个时间步,它会为词汇表中每个可能的字符(含CTC的特殊blank标记)分配一个概率。现在的任务是在这些概率中找到最有可能的词语序列。由于路径数量随序列长度呈指数增长,尝试每条可能的路径进行穷举搜索在计算上是不可行的。这就是解码算法发挥作用的地方。它们是智能搜索策略,旨在不评估所有可能性的情况下,找到高质量的转录结果。我们将讨论两种主要方法:一种是简单快速的贪心搜索,另一种是更高效但计算量大的集束搜索。贪心搜索:最简单的选择方式贪心搜索,也称为最佳路径解码,是最直接的解码策略。在声学模型输出的每个时间步,它只选择概率最高的单个字符。它之所以“贪心”,是因为它在每一步都做出局部最优的选择,期望能得到全局最优的结果。让我们用一个例子来说明。假设模型处理了一个短音频片段,并为前几个时间步生成了以下概率矩阵。为简洁起见,我们只显示前几个字符的概率。时间 -> 1 2 3 4 5 ------------------------------------------------- 'c' 0.1 0.8 0.1 0.1 0.1 'a' 0.2 0.1 0.7 0.1 0.2 't' 0.6 0.1 0.1 0.2 0.8 '_' (空白) 0.1 0.0 0.1 0.6 0.1一个贪心解码器将执行以下步骤:时间 1: 字符 't' 的最高概率为 0.6。路径:t。时间 2: 'c' 的最高概率为 0.8。路径:tc。时间 3: 'a' 的最高概率为 0.7。路径:tca。时间 4: blank 标记的最高概率为 0.6。路径:tca_。时间 5: 't' 的最高概率为 0.8。路径:tca_t。原始输出路径是 tca_t。为了得到最终转录结果,我们应用两条CTC后处理规则:合并连续相同字符:此处无需更改。移除所有blank标记:tca_t 变为 tcat。这看起来合理,但贪心方法有一个明显的不足:它目光短浅。在一个时间步看起来最好的选择,之后可能会导致死胡同。例如,模型在“recognize”开头可能对字符“w”比“r”稍微更有信心。贪心解码器会固定选择“w”,并可能被迫生成“weck a nice beach”,因为从最初的错误中恢复是不可能的。它没有回溯机制,也无法考虑可能性稍低但最终更有前景的路径。集束搜索:保持多种可能性集束搜索提供了一种更好的解决方案,通过同时考虑多个潜在路径,即可能性。它不是在每一步都确定选择单个最佳字符,而是维护一个包含 k 个最有可能的路径的列表。这个列表被称为“集束”(beam),而 k 则是“集束宽度”。该过程如下:初始化(时间 1): 从第一个时间步的概率分布中选择前 k 个字符。这 k 个字符构成了我们最初的假设集束。扩展(时间 t): 对于当前集束中的每个 k 个假设,通过附加词汇表中每个可能的字符来扩展它。如果集束宽度为 k 且词汇量大小为 N,这将创建 k * N 个新的候选假设。评分和修剪(时间 t): 计算 k * N 个新候选的每个概率。然后,按分数对所有候选进行排序,并只保留前 k 个假设。这组新的 k 个假设成为下一个时间步的集束。迭代: 重复扩展和修剪步骤,直到处理完所有时间步。最终选择: 在最后一个时间步之后,选择总体得分最高的假设作为最终转录结果。digraph BeamSearch { rankdir=TB; splines=ortho; node [shape=record, style="rounded,filled", fontname="helvetica", margin="0.1,0.05"]; edge [fontname="helvetica", fontsize=10]; subgraph cluster_0 { label = "时间 0"; style=dashed; start [label="<开始>", shape=circle, style=filled, fillcolor="#ced4da"]; } subgraph cluster_1 { label = "时间 1"; style=dashed; h1 [label="{h|-0.9}", fillcolor="#a5d8ff"]; e1 [label="{e|-1.2}", fillcolor="#a5d8ff"]; c1 [label="{c|-2.3}", fillcolor="#e9ecef"]; } subgraph cluster_2 { label = "时间 2"; style=dashed; he2 [label="{he|-1.5}", fillcolor="#a5d8ff"]; h_2 [label="{h_|-1.8}", fillcolor="#a5d8ff"]; eh2 [label="{eh|-2.5}", fillcolor="#e9ecef"]; ee2 [label="{e|-2.9}", fillcolor="#e9ecef"]; } subgraph cluster_3 { label = "时间 3"; style=dashed; hel3 [label="{hel|-2.0}", fillcolor="#a5d8ff"]; he_3 [label="{he_|-2.2}", fillcolor="#a5d8ff"]; h_l3 [label="{h_l|-2.6}", fillcolor="#e9ecef"]; h_e3 [label="{h_e|-3.1}", fillcolor="#e9ecef"]; } start -> h1; start -> e1; start -> c1 [style=dashed, color="#868e96"]; h1 -> he2; h1 -> h_2; e1 -> eh2 [style=dashed, color="#868e96"]; e1 -> ee2 [style=dashed, color="#868e96"]; he2 -> hel3; he2 -> he_3; h_2 -> h_l3 [style=dashed, color="#868e96"]; h_2 -> h_e3 [style=dashed, color="#868e96"]; {rank=same; start;} {rank=same; h1; e1; c1;} {rank=same; he2; h_2; eh2; ee2;} {rank=same; hel3; he_3; h_l3; h_e3;} }集束宽度为2的集束搜索解码的可视化。在每个时间步,假设都会被扩展,但只有前两个(蓝色)会被保留到下一步。可能性较低的路径(灰色)被修剪掉。数字代表对数概率分数。整合语言模型当我们整合语言模型时,集束搜索的真正优势就体现出来了。我们可以不再仅根据声学模型的输出来评估假设,而是使用本章开头介绍的组合得分公式:$$ \text{得分}(\text{假设}) = \text{声学得分} + \alpha \cdot \text{语言模型得分} $$在集束搜索过程中,特别是在评分和修剪步骤中,我们调整每个候选假设的得分。声学得分 是声学模型的累积概率。语言模型得分 是语言模型对当前假设所形成的词语序列分配的概率。权重 $\alpha$ 是一个超参数,可以调整它来控制语言模型对最终结果的影响程度。较高的 $\alpha$ 会使解码器偏向于语法正确或常见的短语,而较低的 $\alpha$ 则使其更信任声学模型。例如,如果解码器正在考虑两个假设:“recognize speech”和“wreck a nice beach”,它们的声学得分可能非常接近。然而,一个训练有素的语言模型会为“recognize speech”分配更高的概率,从而提升其总得分,确保它留在集束中。更大的集束宽度 k 允许解码器考虑更多可能性,从而增加了找到正确转录结果的机会。然而,这会增加计算和内存的开销,因为每一步要评估的假设数量随 k 线性增长。实际应用中,选择5到10之间的集束宽度通常能在准确性和性能之间取得良好平衡。通过保持多个假设活跃并融入语言学知识,集束搜索明显优于贪心搜索,并且是当今大多数高性能语音识别系统中使用的标准解码算法。