Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

16.5 CTC

上一节指出,语音识别具有两个性质,使其非常适合采用编码器—解码器架构:编码器产生输入编码,解码器利用注意力探索该编码。第一,语音中很长的声学输入序列 XX 映射到短得多的字母序列 YY;第二,很难准确知道 XX 的哪一部分映射到 YY 的哪一部分。

本节简要介绍一种编码器—解码器的替代方法:一种名为 CTC 的算法和损失函数。CTC 是 Connectionist Temporal Classification(连接时序分类)的缩写(Graves et al., 2006),以完全不同的方式处理上述问题。CTC 的直觉是为输入的每一帧输出一个字符,使输出与输入等长;然后应用折叠函数,合并连续相同的字母,从而得到更短的序列。

想象对某人说出单词 dinner 的语音进行推理,并假设有一个函数为每个输入频谱帧表示 xix_i 选择最可能的字母。把对应于每个输入帧的字母序列称为对齐,因为它告诉我们每个字母在声学信号中与什么位置对齐。图 16.12 展示了一个对齐示例,以及使用只删除连续重复字母的折叠函数后会发生什么。

但这并不奏效:朴素算法把语音转写成了 diner,而不是 dinner!折叠无法处理双写字母。朴素函数还有另一个问题:它没有说明输入中的静音应与哪个符号对齐。我们不希望把静音转写成随机字母!

CTC 通过在转写字母表中加入一个表示空白的特殊符号来解决这两个问题,我们用 ϵϵ 表示。只要不希望转写字母,就可以在对齐中使用空白符号。空白符号也可以放在字母之间;由于折叠函数只折叠连续的重复字母,因此它不会跨越 ϵϵ 进行折叠。更正式地说,定义映射 B:ayB:a\mapsto y,把对齐 aa 映射到输出 yy:先折叠所有重复字母,再删除所有空白。图 16.13 概略展示了这一折叠函数 BB

图 16.12 折叠输入与字母之间对齐的朴素算法。

图 16.13 CTC 折叠函数 BB,展示空白字符 ϵϵ;对齐 AA 中连续重复的字符被删除,形成输出 YY

CTC 折叠函数是多对一的:许多不同的对齐都映射到同一个输出字符串。例如,图 16.13 中的对齐并不是产生 dinner 字符串的唯一对齐。图 16.14 展示了其他一些可以产生同一输出的对齐。

图 16.14 产生转写 dinner 的另外三个合法对齐。

考虑所有可能产生同一个输出 YY 的对齐集合很有用。我们使用 BB 函数的逆像 B1B^{-1},并将该集合表示为 B1(Y)B^{-1}(Y)

16.5.1 CTC 推理

在了解如何计算 PCTC(YX)P_{CTC}(Y|\mathbf{X}) 之前,先看看 CTC 如何为某个特定对齐 A^={a^1,,a^n}\hat{A}=\{\hat{a}_1,\ldots,\hat{a}_n\} 赋予概率。CTC 做出了很强的条件独立假设:给定输入 x\mathbf{x} 后,CTC 模型在时间 tt 的输出 ata_t 与任意其他时间的输出标签 aia_i 相互独立。因此:

PCTC(AX)=t=1Tp(atX)(16.16)P _ {\mathrm{CTC}} (\mathbf {A} | \mathbf {X}) = \prod_ {t = 1} ^ {T} p (a _ {t} | \mathbf {X})\tag{16.16}

因此,为了寻找最佳对齐 A^={a^1,,a^T}\hat{A}=\{\hat{a}_1,\ldots,\hat{a}_T\},可以在每个时间步 tt 贪心地选择概率最大的字符:

a^t=argmaxcCpt(cX)(16.17)\hat {a} _ {t} = \underset {c \in C} {\operatorname{argmax}} p _ {t} (c | \mathbf {X})\tag{16.17}

然后将得到的序列 AA 传给 CTC 折叠函数 BB,得到输出序列 YY

这个寻找最佳对齐 AA 的简单推理算法可以如下实现:由于每个时间点都要做决策,可以把 CTC 视为序列建模任务;在时间 tt,对每个输入词元 xt\mathbf{x}_t 输出一个对应的字母 y^t\hat{\mathbf{y}}_t,从而不需要完整的解码器。图 16.15 展示了这种架构:使用一个编码器,在每个时间步产生隐藏状态 hth_t,再在每个时间步对字符词表应用 softmax 完成解码。

图 16.15 CTC 推理:使用仅含编码器的模型,在每个输出步骤对隐藏状态 hth_t 上的字符词表应用简单 softmax 进行解码。

然而,式(16.17)和图 16.14 中的推理算法存在潜在缺陷。问题在于,我们选择的是最可能的对齐 AA,但最可能的对齐未必对应最可能的最终折叠输出字符串 YY。这是因为有许多对齐可以导向同一个输出字符串,所以最可能的输出字符串可能不对应概率最高的单个对齐。例如,假设输入 X=[x1x2x3]\mathbf{X}=[x_1x_2x_3] 的最可能对齐 AA 是 [a b ϵϵ],但接下来两个最可能的对齐是 [b ϵϵ b] 和 [ϵ b b]。把这两个对齐的概率相加后,输出 Y=[bb]Y=[b b] 可能比 Y=[ab]Y=[a b] 更可能。

因此,最可能的输出序列 YY 不是具有单个最佳 CTC 对齐的序列,而是所有可能对齐的概率之和最高的序列:

PCTC(YX)=AB1(Y)P(AX)=AB1(Y)t=1Tp(atht)Y^=argmaxYPCTC(YX)(16.18)\begin{array}{r l} P _ {C T C} (Y | \mathbf {X}) & = \sum_ {A \in B ^ {- 1} (Y)} P (A | \mathbf {X}) \\ & = \sum_ {A \in B ^ {- 1} (Y)} \prod_ {t = 1} ^ {T} p (a _ {t} | h _ {t}) \\ \hat{Y} & = \underset {Y} {\operatorname{argmax}} P _ {C T C} (Y | \mathbf {X}) \end{array}\tag{16.18}

但对所有对齐求和的代价非常高(对齐数量很多),因此我们使用 Viterbi 束搜索的一个版本近似这一求和:巧妙地在束中保留映射到同一输出字符串的高概率对齐,并将它们相加,作为式(16.18)的近似。Hannun(2017)对这一 CTC 束搜索扩展有清晰说明。

由于前面提到的强条件独立假设(给定输入后,时间 tt 的输出与时间 t1t-1 的输出独立),CTC 不会隐式地从数据中学习语言模型,这一点不同于基于注意力的编码器—解码器架构。因此,使用 CTC 时必须插入一个语言模型(以及某种长度因子 L(Y)L(Y)),并使用在开发集上训练的插值权重:

scoreCTC(YX)=logPCTC(YX)+λ1logPLM(Y)+λ2L(Y)(16.19)\operatorname{score} _ {\mathrm{CTC}} (Y | \mathbf {X}) = \log P _ {\mathrm{CTC}} (Y | \mathbf {X}) + \lambda_ {1} \log P _ {\mathrm{LM}} (Y) + \lambda_ {2} L (Y)\tag{16.19}

16.5.2 CTC 训练

为了训练基于 CTC 的 ASR 系统,我们使用带特殊 CTC 损失函数的负对数似然损失。因此,整个数据集 DD 的损失是每个输入 XX 对应正确输出 YY 的负对数似然之和:

LCTC=(X,Y)DlogPCTC(YX)(16.20)L _ {\mathrm{CTC}} = \sum_ {(X, Y) \in D} - \log P _ {\mathrm{CTC}} (Y | \mathbf {X})\tag{16.20}

要计算单个输入对 (X,Y)(X,Y) 的 CTC 损失,需要知道给定输入 XX 时输出 YY 的概率。正如式(16.18)所示,要计算给定输出 YY 的概率,就要对所有会折叠为 YY 的可能对齐求和。换句话说:

PCTC(YX)=AB1(Y)t=1Tp(atht)(16.21)P _ {\mathrm{CTC}} (Y | \mathbf {X}) = \sum_ {A \in B ^ {- 1} (Y)} \prod_ {t = 1} ^ {T} p \left(a _ {t} \mid h _ {t}\right)\tag{16.21}

直接对所有可能的对齐求和不可行,因为对齐太多。但可以使用动态规划合并对齐,高效地计算这一总和;该方法是前向—后向算法的一个版本,HMM(附录 A)和 CRF 的训练也使用前向—后向算法。训练和推理的原始动态规划算法见 Graves et al.(2006);Hannun(2017)对二者都有详细说明。

16.5.3 结合 CTC 与编码器—解码器

还可以结合前面介绍的两种架构/损失函数,即编码器—解码器架构中的交叉熵损失和 CTC 损失。图 16.16 展示了示意图。训练时,可以用在开发集上调节的 λ\lambda 对两个损失加权:

L=λlogPencdec(YX)(1λ)logPctc(YX)(16.22)L = - \lambda \log P _ {e n c d e c} (Y | \mathbf {X}) - (1 - \lambda) \log P _ {c t c} (Y | \mathbf {X})\tag{16.22}

推理时,也可以把二者与语言模型(或长度惩罚)结合,同样使用学习得到的权重:

Y^=argmaxY[λlogP encdec (YX)+(1λ)logPCTC(YX)+γlogPLM(Y)](16.23)\hat{Y} = \underset {Y} {\operatorname{argmax}} \left[ \lambda \log P _ {\text { encdec }} (Y | \mathbf {X}) + (1 - \lambda) \log P _ {\mathrm{CTC}} (Y | \mathbf {X}) + \gamma \log P _ {\mathrm{LM}} (Y) \right]\tag{16.23}

图 16.16 结合 CTC 与编码器—解码器损失函数。

16.5.4 流式模型:用 RNN-T 改进 CTC

由于 CTC 的强独立性假设(假设时间 tt 的输出与时间 t1t-1 的输出独立),基于 CTC 的识别器无法达到基于注意力的编码器—解码器识别器那么高的准确率。不过,CTC 识别器的优点是可以用于流式识别。流式识别意味着在线识别词语,而不是等到句末才识别。对于命令和口述等许多应用,用户仍在说话时就开始识别至关重要。使用注意力的算法必须先计算整个输入的隐藏状态序列,为注意力分布提供上下文,然后解码器才能开始解码;相比之下,CTC 算法可以立即从左到右输入字母。

如果希望进行流式识别,就需要改进 CTC,去除条件独立假设,使模型能够了解输出历史。图 16.17 所示的 RNN-Transducer(RNN-T)就是这样一种模型(Graves, 2012;Graves et al., 2013)。RNN-T 有两个主要组件:CTC 声学模型,以及一个独立的语言模型组件,称为 predictor,它以输出词元历史为条件。在每个时间步 tt,给定输入 x1xtx_1\ldots x_t,CTC 编码器输出隐藏状态 htench_t^{enc}。语言模型 predictor 接收前一个输出词元(不计空白),输出隐藏状态 hupredh_u^{pred}。二者通过另一个网络,该网络的输出再经过 softmax,以预测下一个字符。

PRNNT(YX)=AB1(Y)P(AX)=AB1(Y)t=1Tp(atht,y<ut)\begin{array}{l} P _ {R N N - T} (Y | \mathbf {X}) = \sum_ {A \in B ^ {- 1} (Y)} P (A | \mathbf {X}) \\ = \sum_ {A \in B ^ {- 1} (Y)} \prod_ {t = 1} ^ {T} p (a _ {t} | h _ {t}, y _ {< u _ {t}}) \end{array}