CTC Algorithm Explained Part 1:Training the Network(CTC 算法详解之训练篇)
转载本文请注明出处:https://xiaodu.io/ctc-explained 作者:yudonglee
现实应用中许多问题可以抽象为序列学习(sequence learning)问题,比如词性标注(POS Tagging)、语音识别(Speech Recognition)、手写字识别(Handwriting Recognition)、机器翻译(Machine Translation)等应用,其核心问题都是训练模型把一个领域的(输入)序列转成另一个领域的(输出)序列。近年来基于 RNN 的序列到序列模型(sequence-to-sequence models)在序列学习任务中取得了显著的效果提升,本文介绍一种 RNN(Recurrent Neural Networks)的端到端训练方法 ——CTC(Connectionist Temporal Classification)算法,它可以让 RNN 直接对序列数据进行学习,而无需事先标注好训练数据中输入序列和输出序列的映射关系,打破了 RNN 应用于语音识别、手写字识别等领域的数据依赖约束,使得 RNN 模型在序列学习任务中取得更好的应用效果。
本文总共分为五部分来全面阐述 CTC 算法(本篇为 Part 1):
Part 1:Training the Network(训练算法篇),介绍 CTC 理论原理,包括问题定义、公式推导、算法过程等。Part 1 链接。
Part 2:Decoding the Network(解码算法篇),介绍 CTC Decoding 的几种常用算法。Part 2 链接。
Part 3:CTC Demo by Speech Recognition(CTC 语音识别实战篇),基于 TensorFlow 实现的语音识别代码,包含详细的代码实战讲解。Part 3 链接。
Part 4:CTC Demo by Handwriting Recognition(CTC 手写字识别实战篇),基于 TensorFlow 实现的手写字识别代码,包含详细的代码实战讲解。Part 4 链接。
Part 5:Conclusion(总结展望篇),总结 CTC 算法的理论局限性和适用场景,以及近年来相关的最新研究动态。Part 5 链接。
接下来,我们先从 “问题” 的背景说起。
1. 背景介绍
在序列学习任务中,RNN 模型对训练样本一般有这样的依赖条件:输入序列和输出序列之间的映射关系已经事先标注好了。比如,在词性标注任务中,训练样本中每个词(或短语)对应的词性会事先标注好,如下图(DT、NN 等都是词性的标注,具体含义请参考链接)。由于输入序列和输出序列是一一对应的,所以 RNN 模型的训练和预测都是端到端的,即可以根据输出序列和标注样本间的差异来直接定义 RNN 模型的 Loss 函数,传统的 RNN 训练和预测方式可直接适用。
然而,在语音识别、手写字识别等任务中,由于音频数据和图像数据都是从现实世界中将模拟信号转为数字信号采集得到,这些数据天然就很难进行 “分割”,这使得我们很难获取到包含输入序列和输出序列映射关系的大规模训练样本(人工标注成本巨高,且启发式挖掘方法存在很大局限性)。因此,在这种条件下,RNN 无法直接进行端到端的训练和预测。
如下图,输入是 “apple” 对应的一段说话音频和手写字图片,从连续的音频信号和图像信号中逐一分割并标注出对应的输出序列非常费时费力,在大规模训练下这种数据要求是完全不切实际的。而如果输入序列和输出序列之间映射关系没有提前标注好,那传统的 RNN 训练方式就不能直接适用了,无法直接对音频数据和图像数据进行训练。
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818172751652-2026780867.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818172758616-1727885331.png)
因此,在语音识别、图像识别等领域中,由于数据天然无法切割,且难以标注出输入和输出的序列映射关系,导致传统的 RNN 训练方法不能直接适用。那么,如何让 RNN 模型实现端到端的训练成为了关键问题。
Connectionist Temporal Classification(CTC)[1] 是 Alex Graves 等人在 ICML 2006 上提出的一种端到端的 RNN 训练方法,它可以让 RNN 直接对序列数据进行学习,而无需事先标注好训练数据中输入序列和输入序列的映射关系,使得 RNN 模型在语音识别等序列学习任务中取得更好的效果,在语音识别和图像识别等领域 CTC 算法都有很比较广泛的应用。总的来说,CTC 的核心思路主要分为以下几部分:
- 它扩展了 RNN 的输出层,在输出序列和最终标签之间增加了多对一的空间映射,并在此基础上定义了 CTC Loss 函数
- 它借鉴了 HMM(Hidden Markov Model)的 Forward-Backward 算法思路,利用动态规划算法有效地计算 CTC Loss 函数及其导数,从而解决了 RNN 端到端训练的问题
- 最后,结合 CTC Decoding 算法 RNN 可以有效地对序列数据进行端到端的预测
接下来,通过一个语音识别的实际例子来引出 CTC 的解决思路
2. 一个实际的例子–声学模型
语音识别的核心问题是把一段音频信号序列转化文字序列,传统的语音识别系统主要分为以下几部分,如下图。
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818172810469-227719745.png)
其中,X 表示音频信号,O 是它的特征表示,一般基于 LPC、MFCC 等方法提取特征,也可以基于 DNN 的方式 “学到” 声学特征的表示。为了简化问题,我们暂且把 O 理解为是由实数数组组成的序列,它是音频信号的特征表示。Q 是 O 对应的发音字符序列,即建模单元,一般可以是音素、音节、字、词等。W 是音频信号 X 对应的文字序列,即我们最终的识别结果。
如图所示,核心问题是通过解码器找到令 P (W|X) 最大化的的 W,通过贝叶斯公式可将其分解为 P (O|Q)、P (Q|W)、P (W),分别对应声学模型、发音模型、语言模型。
其中,声学模型就是对 P (O|Q) 进行建模,通过训练可以 “学到” 音频信号和文字发音间的联系。为了简化问题,我们假定声学模型的建模单元 Q 选择的是音节,O 选择的是 MFCC 特征(由 39 维数组组成的序列)。
如下图,输入序列是一段 “我爱你中国” 的音频,输出序列是音节序列 “wo3 ai4 ni3 zhong1 guo2”,如果训练样本中已经 “分割” 好音频,并标注好它和音节的对应关系,则 RNN 模型如下:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818172830552-1676153845.png)
然而,如前面所述,对音频 “分割” 并标注映射关系的数据依赖是不切实际的,实际情况是对音频按照时间窗口滑动来提取特征,比如按照每 10 毫秒音频提取特征得到一个 N 维数组。如下图所示:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818172856883-2138260509.png)
由于人说话发音是连续的,且中间也会有 “停顿”,所以输出序列中存在重复的元素,比如 “wo3 wo3”,也存在表示间隔符号 “_”。需从输出序列中去除掉重复的元素以及间隔符,才可得到最终的音节序列,比如,“wo3 wo3 ai4 _ ni3 _ zhong1 guo2 _” 归一处理后得到 “wo3 ai4 ni3 zhong1 guo2”。因此,输出序列和最终的 label 之间存在多对一的映射关系,如下图:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818172905406-857723143.png)
RNN 模型本质是对𝒑(𝒛│𝒙) 建模,其中 x 表示输入序列,o 表示输出序列,z 表示最终的 label,o 和 l 存在多对一的映射关系,即:𝒑(𝒛│𝒙)=sum of all P (o|x),其中 o 是所有映射到 z 的输出序列。因此,只需要穷举出所有的 o,累加一起即可得到𝒑(𝒛│𝒙),从而使得 RNN 模型对最终的 label 进行建模。
经过以上的映射转换,解决了端到端训练的问题,RNN 模型实际上是对映射到最终 label 的输出序列的空间建模。然而,对每一个 z 都 “穷举所有的 o”,这个计算的复杂度太大,会使得训练速度变得非常慢,因此怎么更高效地进行端到端训练成为待解决的关键问题。
通过以上的实际例子,我们对问题的解决思路有了更加直观的了解,接下来就开始正式介绍 CTC 的理论原理。
3. 问题定义
以 RNN 声学模型为例子,建模的目标是通过训练得到一个 RNN 模型,使其满足:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173004612-2058797868.png)
本质上是最大似然预估, S 是训练数据集,X 和 Z 分别是输入空间(由音频信号向量序列组成的集合)和目标空间(由声学模型建模单元序列组成的集合),L 是由输出的字符集(声学建模单元的集合),且 x 的序列长度小于或等于 z 的序列长度。
接下来,在介绍如何计算 Loss 函数之前,我们需要对 RNN 输出层做一个简单的扩展。
4. RNN 输出层扩展
如下图,为了便于读者理解,简化了 RNN 的结构,只有单向的一层 LSTM,把声学建模单元选择为字母 {a-z},并对建模单元字符集做了扩展,且定义了从输出层到最终 label 序列的多对一映射函数,使得 RNN 输出层能映射到最终的 label 序列。
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173015883-1128359578.png)
所以,如果要计算𝒑(𝒛│𝒙),可以累加其对应的全部输出序列 (也即映射到最终 label 的 “路径”) 的概率即可,如下图。
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173040794-677770129.png)
5. CTC Loss 函数定义
如下图,基于 RNN 条件独立假设,即可得到 CTC Loss 函数的定义:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173053806-107795531.png)
假定选择单层 LSTM 为 RNN 结构,则最终的模型结构如下图:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173113847-614380754.png)
6. CTC Loss 函数计算
由于直接暴力计算 𝒑(𝒛│𝒙) 的复杂度非常高,作者借鉴 HMM 的 Forward-Backward 算法思路,利用动态规划算法求解。
如下图,为了更形象表示问题的搜索空间,用 X 轴表示时间序列, Y 轴表示输出序列,并把输出序列做标准化处理,输出序列中间和头尾都加上 blank,用 l 表示最终标签,l’表示扩展后的形式,则由 2|l| + 1 = 2|l’|,比如:l=apple => l’=_a_p_p_l_e_
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173143299-1064931774.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173151329-1683546341.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173204733-220260279.png)
图中并不是所有的路径都是合法路径,所有的合法路径需要遵循一些约束,如下图:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173228709-1401089468.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173239230-127593738.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173247765-1857729690.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173255242-1005000223.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173303700-1160132649.png)
所以,依据以上约束规则,遍历所有映射为 “apple” 的合法路径,最终时序 T=8,标签 labeling=“apple” 的全部路径如下图:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173347817-1370348111.png)
接下来,如何计算这些路径的概率总和?暴力遍历?分而治之?作者借鉴 HMM 的 Forward-Backward 算法思路,利用动态规划算法求解,可以将路径集合分为前向和后向两部分,如下图所示:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173400947-734875384.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173410257-1160170841.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173422934-2114281832.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173435777-310902922.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173448191-673640731.png)
通过动态规划求解出前向概率之后,可以用前向概率来计算 CTC Loss 函数,如下图:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173500546-817060863.png)
类似地方式,我们可以定义反向概率,并用反向概率来计算 CTC Loss 函数,如下图:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173510839-1145540270.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173521585-711511529.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173537931-1519531550.png)
去掉箭头方向,把前向概率和后向概率结合起来也可以计算 CTC Loss 函数,这对于后面 CTC Loss 函数求导计算是十分重要的一步,如下图所示:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173547110-183585992.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173604778-940011699.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200818173627612-1029331902.png)
总结一下,根据前向概率计算 CTC Loss 函数,得到以下结论:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200820171520367-508412364.png)
根据后向概率计算 CTC Loss 函数,得到以下结论:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200820171534566-1958578198.png)
根据任意时刻的前向概率和后向概率计算 CTC Loss 函数,得到以下结论:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200820171544811-1637326340.png)
至此,我们已经得到 CTC Loss 的有效计算方法,接下来对其进行求导
7. CTC Loss 函数求导
我们先回顾下 RNN 的网络结构,如下图飘红部分是 CTC Loss 函数求导的核心部分:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200820171553783-203705576.png)
CTC Loss 函数相对于 RNN 输出层元素的求导过程如下图所示:
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200820171607320-1925543773.png)
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200820171620052-313812272.png)
8. 总结
总结一下,本篇通过 RNN 声学模型的例子引出了问题背景,并通过实际例子一步一步的介绍如何定义和计算 CTC Loss 函数,最终通过反向传播算法完成对 RNN 模型的端到端训练过程。
![](https://img2020.cnblogs.com/blog/1099781/202008/1099781-20200820171629603-558245671.png)
至此,CTC 算法的模型训练过程与原理介绍完了,下一篇将介绍 CTC 算法的模型预测部分,Part2 链接。
References
- Graves et al., Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with RNNs. In ICML, 2006. (Graves 提出 CTC 算法的原始论文)
- Graves et al., A Novel Connectionist System for Unconstrained Handwriting Recognition. In IEEE Transactions on PAML, 2009.(CTC 算法在手写字识别中的应用)
- Graves et al., Towards End-to-End Recognition with RNNs. In JMLR, 2014.(CTC 算法在端到端声学模型中的应用)
- Alex Graves, Supervised Sequence Labelling with Recurrent Neural Networks. In Studies in Computational Intelligence, Springer, 2012.( Graves 的博士论文,关于 sequence learning 的研究,主要是 CTC)
12080 total views , 24 views today
44 Replies to “CTC Algorithm Explained Part 1:Training the Network(CTC 算法详解之训练篇)”