CTC解决什么问题

CTC,Connectionist Temporal Classification,用来解决输入序列和输出序列难以一一对应的问题。

举例来说,在语音识别中,我们希望音频中的音素和翻译后的字符可以一一对应,这是训练时一个很天然的想法。但是要对齐是一件很困难的事,如下图所示(图源见参考资料[1]),有人说话块,有人说话慢,每个人说话快慢不同,不可能手动地对音素和字符对齐,这样太耗时。

再比如,在OCR中使用RNN时,RNN的每一个输出要对应到字符图像中的每一个位置,要手工做这样的标记工作量太大,而且图像中的字符数量不同,字体样式不同,大小不同,导致输出不一定能和每个字符一一对应。


CTC基本概述

考虑一个LSTM,用w表示LSTM的参数,则LSTM可以表示为一个函数:\(y = N_w(x)\)。

定义输入x的时间步为T,每个时间步上的特征维度记作m,表示m维特征。
\[ x = (x^1, x^2, ..., x^T) \\x^t = (x_1^t, x_2^t, ..., x_m^t)\]

输出时间步也为T,和输入可以一一对应,每个时间步的输出维度作为n,表示n维输出,实际上是n个概率。
\[ y = (y^1, y^2, ..., y^T) \\y^t = (y_1^t, y_2^t, ..., y_n^t)\]

假设要对26个英文字符进行识别,考虑到有些位置没有字符,定义一个-作为空白符加入到字符集合\({L}'= \{a,b,c,...,x,y,z\} \cup \{-\}= L \cup \{-\} = \{a,b,c,...,x,y,z,-\}\),那么对于LSTM而言每个时间步的输出维度n就是27,表示27个字符在这个时间步上输出的概率。

如果根据这些概率进行选取,每个时间步选取一个元素,就可以得到输出序列,其输出空间可以记为\({L}'^T\)。

定义一个B变换,对LSTM的输出序列(比如下例中的4个\(\pi\))进行变换,变换成真实输出(比如下例中的state),把连续的相同字符删减为1个并删去空白符。举例说明,当T=12时:
\[\begin{split} B(\pi^1) &= B(--stta-t---e) = state \\ B(\pi^2) &= B(sst-aaa-tee-) = state \\ B(\pi^3) &= B(--sttaa-tee-) = state \\ B(\pi^4) &= B(sst-aa-t---e) = state \end{split}\]

其中\(\pi\)表示LSTM的一种输出序列。当我们优化LSTM时,只需要最大化以下概率,即给定输入x的情况下,输出为l的概率,\(l\)表示真实输出。对下式取负号,就可以使用梯度下降对其求最小。
\[p(l \ | \ x) = \sum_{B(\pi)=l} p(\pi | x)\]

假设时间步之间的输出独立,那么对于任意一个输出序列\(\pi\)的概率计算式子如下,
\[p(\pi | x) = \prod_{t=1}^{T} y_{\pi_t}^t\]

其中下标\(\pi_t\)表示的是,输出序列在t时间步选取的元素对应的索引,比如该序列在第一个时间步选取的元素是a,那么得到的值就是1。选取的是z,那么得到的值就是26。选取的是空白符,那么得到的值就是27。为了方便观测,也用对应的字符表示,其实是一个意思,如下式所示。
\[ \pi = (--stta-t---e) \\ p(\pi | x) = {y^1}_{-} \cdot {y^2}_{-} \cdot y^3_{s} \cdot y^4_{t} \cdot y^5_{t} \cdot y^6_{a} \cdot {y^7}_{-} \cdot {y^8}_{t} \cdot {y^9}_{-} \cdot {y^{10}}_{-} \cdot {y^{11}}_{-} \cdot y^{12}_{e} \]

但是对于某一个真实输出,比如上述的state,有多个LSTM的输出序列可以通过B转换得到。这些序列都是我们要的结果,我们要使给定x,这些输出序列的概率加起来最大。如果逐条遍历来求得,时间复杂度是指数级的,因为有T个位置,每个位置有n种选择(字符集合的大小),那么就有\(n^T\)种可能。因此CTC借用了HMM中的“前向-后向算法”(forward-backward algorithm)来计算。


CTC中的前向后向算法

由于真实输出\(l\)是一个序列,序列可以通过一个路径图中的一条路径来表示,我们也称输出序列\(l\)为路径\(l\)。定义路径\({l}'\)为“在路径\(l\)每两个元素之间以及头尾插入空白符”,如:
\[l = state \\ {l}' = -s-t-a-t-e-\]

对某个时间步的某个字符求导(这里用k表示字符集合中的某个字符或字符索引),恰好是与概率\(y_k^t\)相关的路径。
\[ \frac{\partial \, p(l \ |x)}{\partial \, y_k^t} = \frac{\partial \, \sum_{B(\pi)=l, \pi_t=k} p(\pi | x)}{\partial \, y_k^t} \]

以前面的\(\pi^1, \pi^2, \pi^3, \pi^4\)为例子,画出两条路径(还有两条没画出来),如下图所示(图源见参考资料[1])。

4条路径都在t=6时经过了字符a,观察4条路径,可以得到如下式子。
\[ \begin{split} \pi^1 &= b = b_{1:5} + a_6 + b_{7:12} \\ \pi^2 &= r = r_{1:5} + a_6 + r_{7:12} \\ \pi^3 &= b_{1:5} + a_6 + r_{7:12} \\ \pi^4 &= r_{1:5} + a_6 + b_{7:12} \end{split}\]

\[\begin{split} p( \pi^1, \pi^2, \pi^3, \pi^4 | x) &= {y^1}_{-} \cdot {y^2}_{-} \cdot {y^3}_{s} \cdot {y^4}_{t} \cdot {y^5}_{t} \cdot {y^6}_{a} \cdot {y^7}_{-} \cdot {y^8}_{t} \cdot {y^9}_{-} \cdot {y^{10}}_{-} \cdot {y^{11}}_{-} \cdot {y^{12}}_{e} \\ &+ {y^1}_{s} \cdot {y^2}_{s} \cdot {y^3}_{t} \cdot {y^4}_{-} \cdot {y^5}_{a} \cdot {y^6}_{a} \cdot {y^7}_{a} \cdot {y^8}_{-} \cdot {y^9}_{t} \cdot {y^{10}}_{e} \cdot {y^{11}}_{e} \cdot {y^{12}}_{-} \\ &+ {y^1}_{-} \cdot {y^2}_{-} \cdot {y^3}_{s} \cdot {y^4}_{t} \cdot {y^5}_{t} \cdot {y^6}_{a} \cdot {y^7}_{a} \cdot {y^8}_{-} \cdot {y^9}_{t} \cdot {y^{10}}_{e} \cdot {y^{11}}_{e} \cdot {y^{12}}_{-} \\ &+ {y^1}_{s} \cdot {y^2}_{s} \cdot {y^3}_{t} \cdot {y^4}_{-} \cdot {y^5}_{a} \cdot {y^6}_{a} \cdot {y^7}_{-} \cdot {y^8}_{t} \cdot {y^9}_{-} \cdot {y^{10}}_{-} \cdot {y^{11}}_{-} \cdot {y^{12}}_{e}\end{split}\]

令:
\[\begin{split} forward &= p(b_{1:5} + r_{1:5} | x) = {y^1}_{-} \cdot {y^2}_{-} \cdot {y^3}_{s} \cdot {y^4}_{t} \cdot {y^5}_{t} + {y^1}_{s} \cdot {y^2}_{s} \cdot {y^3}_{t} \cdot {y^4}_{-} \cdot {y^5}_{a} \\ backward &= p(b_{7:12} + r_{7:12} | x) = {y^7}_{-} \cdot {y^8}_{t} \cdot {y^9}_{-} \cdot {y^{10}}_{-} \cdot {y^{11}}_{-} \cdot {y^{12}}_{e} + {y^7}_{a} \cdot {y^8}_{-} \cdot {y^9}_{t} \cdot {y^{10}}_{e} \cdot {y^{11}}_{e} \cdot {y^{12}}_{-} \end{split}\]
那么可以做如下表示:
\[p( \pi^1, \pi^2, \pi^3, \pi^4 | x) = forward \cdot y_a^t \cdot backward\]
上述的forward和backward只包含了4条路径,如果推广一下forward和backward的含义,考虑所有路径,可做如下表示:
\[\sum_{B(\pi)=l, \pi_6=a} p(\pi | x) = forward \cdot y_a^t \cdot backward\]

定义forward为\(\alpha_t({l}'_k)\),表示t时刻经过\({l}'_k\)字符的路径概率中1-t的概率之和,式子定义如下。
\[\alpha_t({l}'_k) = \sum_{B(\pi)=l, \ \pi_t = {l}'_k} \ \prod_{{t}'=1}^{t} {y_{\pi_{{t}'}}^{{t}'}}\]

t=1时,符号只能是空白符或\(l_1\),可以得到以下初始条件:
\[\alpha_1(-) = {y^1}_{-} \\ \alpha_1(l_1) = y^1_{l_1} \\ \alpha_1(l_t) = 0, \forall t > 1 \]

观察上图((图源见参考资料[1])可以发现,如果t=6时字符是a,那么t=5时只能是字符a,t,空白符三选一,否则经过B变换后无法得到state。
可以得到以下递推关系:
\[\alpha_6(a) = (\alpha_5(a) + \alpha_5(t) + \alpha_5(-)) \cdot y_a^6\]

更一般地,可以得到如下递推关系:
\[\alpha_t({l}'_k) = (\alpha_{t-1}({l}'_k) + \alpha_{t-1}({l}'_{k-1}) + \alpha_{t-1}(-)) \cdot y_{{l}'_k}^t\]

定义backward为为\(\beta_t(s)\),表示t时刻经过\({l}'_k\)字符的路径概率中t-T的概率之和,式子定义如下。
\[\beta_t({l}'_k) = \sum_{B(\pi)=l, \ \pi_t = {l}'_k} \ \prod_{{t}'=t}^{T} {y_{\pi_{{t}'}}^{{t}'}}\]

t=T时,符号只能是空白符或\(l_{|{l}'|-1}\),可以得到以下初始条件:
\[\beta_T(-) = {y^T}_- \\ \beta_T({l}'_{|{l}'|-1}) = y^T_{l_{|{l}'|-1}} \\ \beta_T(l_{|{l}'|-i}) = 0, \forall i > 1 \]

同理,可以得到如下递推关系:
\[\beta_t({l}'_k) = (\beta_{t+1}({l}'_k) + \beta_{t+1}({l}'_{k+1}) + \beta_{t+1}(-)) \cdot y_{{l}'_k}^t\]

根据forward和backward的式子定义,它们相乘可以得到:
\[\alpha_t({l}'_k) \beta_t({l}'_k) = \sum_{B(\pi)=l, \ \pi_t = {l}'_k} \ y_{{l}'_k}^t \prod_{t=1}^{T} {y_{\pi_t}^t}\]

又因为\(p(l \ | \ x)\) 对\({l}'_k\)求导时,只跟那些\(\pi_t = {l}'_k\)的路径有关,那么求导时(注意是求导时)可以简写如下式子:
\[p(l \ | \ x) = \sum_{B(\pi)=l, \ \pi_t = {l}'_k} p(\pi | x) = \sum_{B(\pi)=l, \ \pi_t = {l}'_k} \ \prod_{t=1}^{T} y_{\pi_t}^t\]

结合上面两式,得到:
\[p(l \ | \ x) = \sum_{B(\pi)=l, \ \pi_t = {l}'_k} \ \frac{\alpha_t({l}'_k) \beta_t({l}'_k) }{y_{{l}'_k}^t}\]

最后可以得到求导式(这里用k来表示字符,和\({l}'_k\)的含义相同):
\[\frac{\partial \, p(l \ |x)}{\partial \, y_k^t} = \frac{\partial \ \sum_{B(\pi)=l, \ \pi_t = k} \frac{\alpha_t(k) \beta_t(k) }{y_k^t}}{\partial \, y_k^t}\]

求导式里的forward和backward可以用前面的dp递推式计算出来,时间复杂度是\(nT\),相比于前面的指数复杂度\(n^T\)大大减小了计算量。

这样对LSTM的输出y求导之后,再根据y对LSTM里面的权重参数w进行链式求导,就可以使用梯度下降的方法来更新参数了。


CTC的预测

一种方法是Best Path search。计算概率最大的一条输出序列(假设时间步独立,那么直接在每个时间步取概率最大的字符输出即可),但是这样没有考虑多个输出序列对应一个真实输出这件事,举个例子,[s,s,-]和[s,s,s]的概率比[s,t,a]低,但是它们的概率之和会高于[s,t,a]。

第二种方法是Beam Search。假设指定B=3,预测过程如下图所示(图源见参考资料[2])。在第一个时间步选取概率最大的三个字符,然后在第二个时间步也选取概率最大的三个字符,两两组合(概率相乘)可以组合成9个序列,这些序列在B转换之后会得到一些相同输出,把具有相同输出的序列进行合并,比如有3个序列都可以转换成a,把它们合并(概率加在一起),计算出概率最大的三个序列,然后继续和下一个时间步的字符进行同样的合并。

有一点需要注意的是合并相同字符时,比如我们看上图T=3的时候,第一个前缀序列a,在跟相同字符a合并的时候,除了产生a之外,还会产生一个aa的有效输出。这是因为这个前缀序列a在T=2的时候曾经是把空白符合并掉了,实际上这个前缀序列a后面是跟着一个空白符的,所以它在跟相同字符a合并的时候中间是有一个隐藏的空白符,合并之后得到的应该是两个a。

因此在合并相同字符时,如果要合并成aa,需要统计在这之前以空白符结尾的那些序列的概率,如果要合并成a,计算的是不以空白符结尾的那些序列的概率。出于这个事实,我们需要跟踪前两处输出,以便于后续的合并计算,见下图所示(图源见参考资料[2])。


CTC的几个性质

第一个是条件独立性。CTC做了一个假设就是不同时间步的输出之间是独立的。这个假设对于很多序列问题来说并不成立,输出序列之间往往存在联系。

第二个是单调对齐。CTC只允许单调对齐,在语音识别中可能是有效的,但是在机器翻译中,比如目标语句中的一些比较后的词,可能与源语句中前面的一些词对应,这个CTC是没法做到的。

第三个是多对一映射。CTC的输入和输出是多对一的关系。这意味着输出长度不能超过输入长度,这在手写字体识别或者语音中不是什么问题,因为通常输入都会大于输出,但是对于输出长度大于输入长度的问题CTC就无法处理了。


参考资料

[1] 知乎上的一篇文章:一文读懂CRNN+CTC文字识别

[2] Distill上一篇关于CTC的介绍(作者Hannun Awni):Sequence Modeling With CTC

转载于:https://www.cnblogs.com/liaohuiqiang/p/9953978.html

CTC(Connectionist Temporal Classification)介绍相关推荐

  1. 白话CTC(connectionist temporal classification)算法讲解

    CTC是计算一种损失值,主要的优点是可以对没有对齐的数据进行自动对齐, 主要用在没有事先对齐的序列化数据训练上.比如语音识别.ocr识别等等. CTC算法有个经典的英文的论文,但是相信大多数人和我一样 ...

  2. CTC介绍(connectionist temporal classification论文翻译)

    1.摘要 我基本是基于论文<Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Rec ...

  3. CMU 11-785 L16 Connectionist Temporal Classification

    Sequence to sequence Sequence goes in, sequence comes out No notion of "time synchrony" be ...

  4. 一文读懂CRNN+CTC(Connectionist Temporal Classification)文字识别

    先总结一把CTC,下面文档太长: CTC是一种Loss计算方法,用CTC代替Softmax Loss,TF和pytorch都有对CTC的实现,从而解决OCR或者语音识别中序列对齐的问题.CTC特点: ...

  5. Connectionist Temporal Classification(CTC)、音识别模型小型综述和一个简易的语音识别模型的tensorflow实现

    CTC是一种端到端的语音识别技术,他避免了需要字或者音素级别的标注,只需要句子级别的标注就可以进行训练,感觉非常巧妙,也很符合神经网络浪潮人们的习惯.特别是LSTM+CTC相较于之前的DNN+HMM, ...

  6. CTC的直观理解(Connectionist Temporal Classification连接时序分类),单行文本时序分类识别的端到端方法

    CTC(Connectionist Temporal Classification), ctc擅长单行验证码识别: 两组谷歌验证码示例 ctc可以提高单行文本识别鲁棒性(不同长度不同位置 ).本文用几 ...

  7. 联结主义时间分类(Connectionist temporal classification)的论文笔记

    前言: { 最近在github上更新了一些代码,但没在这里更新文章,这次就在这写一篇论文的阅读笔记. 论文是<Connectionist temporal classification: Lab ...

  8. Connectionist Temporal Classification(CTC)

    论文地址:https://www.cs.toronto.edu/~graves/icml_2006.pdf 概述 这是论文中第三节的翻译,加入了一些自己的理解.还有没看懂的地方,希望可以有人一起讨论 ...

  9. 分布式任务调度框架(Temporal)介绍

    分布式任务调度框架基本能力: 任务管理能力(增删改查.执行.定时执行.延时执行.健康监控) 集群管理能力(扩展简单.效率高) 编程能力(运行代码) Web界面管理 目前市面上有很多可用于处理分布式任务 ...

最新文章

  1. C# 类型实例化的语法糖--unity下诡异结果
  2. python游戏脚本实例-Python使用pygame模块编写俄罗斯方块游戏的代码实例
  3. LeetCode Contains Duplicate
  4. Nginx加入linux系统服务
  5. 存储过程双层循环_mysql嵌套存储过程实现循环嵌套
  6. python3 logging模块_Python3之logging模块浅析
  7. 运筹学广泛的使用计算机,运筹学笔记
  8. Ajax — 第五天
  9. thinkphp 5数据库操作
  10. 2016级算法第二次上机-F.ModricWang's Number Theory II
  11. java filterinputstream_java.io.FilterInputStream.close()
  12. 在eclipse中安装jadclipse的反编译插件
  13. macos masm5 Warning level (0-2) expected after W option
  14. 【Go实战基础】程序里面数据是如何显示到浏览器当中的
  15. linux常用命令-part3
  16. 机器学习|切比雪夫、辛钦和贝努里大数定律|15mins入门|概统学习笔记(十四)
  17. 饿了么的树形控件的使用
  18. 简单的集装箱号码识别
  19. 集成模型Bagging和Boosting的区别
  20. 英语英文理论驾考宝典 驾照考试题库软件

热门文章

  1. html视频位置居中,绝对定位的元素水平居中
  2. Python Supervisord 进程管理工具
  3. 办公室适宜摆放的植物有哪些
  4. Android 安兔兔测试屏幕尺寸与实际不符的问题修改
  5. 股市暴跌之下,中国科技公司有多惨?!
  6. SpringMVC详解
  7. 【Python爬虫实战】爬取某小说网排行榜上的图书封面图片
  8. vue3的tsx写法(v-show、v-if、v-for、v-bind、v-on),父子组件传值
  9. Android关闭软键盘的方法
  10. Nonlinear Complementary Filters on the Special Orthogonal Group 学习笔记