zoukankan      html  css  js  c++  java
  • CTC (Connectionist Temporal Classification) 算法原理

    (原创文章,转载请注明出处哦~)

    简单介绍CTC算法

    CTC是序列标注问题中的一种损失函数

    传统序列标注算法需要每一时刻输入与输出符号完全对齐。而CTC扩展了标签集合,添加空元素

    在使用扩展标签集合对序列进行标注后,所有可以通过映射函数转换为真实序列的 预测序列,都是正确的预测结果。也就是在无需数据对齐处理,即可得到预测序列。

    目标函数就是 最大化 所有正确的预测序列的概率和

    在查找所有正确预测序列时,采用了前向后向算法

    前向过程计算从1-t时刻,预测出正确的前缀的概率;后向过程计算从t - T时刻,预测出正确的后缀的概率。

    那么: 前缀概率 * 后缀概率 / t 时刻预测s的概率 = t 时刻时所有正确的预测序列的概率。

    动态规划降低时间复杂度:只有在前一时刻到达预测出某些特定符号,在当前时刻,才可以做出正确预测。

    那么,到 t 时刻为止,预测出正确的 标签序列的前缀 的概率 = (到t - 1为止预测正确的所有子序列概率和) * 预测出当前标签的概率。 

    定义与背景

    CTC全称:Connectionist temporal classification, 主要用于处理序列标注问题中的输入与输出标签的对齐问题。

    --------------------------------

    什么是数据的对齐问题? (参考链接:https://www.cnblogs.com/qcloud1001/p/9041218.html)

    传统的语音识别的声学模型训练,对于每一帧的数据,需要知道对应的label才能进行有效的训练,在训练数据之前需要做语音对齐的预处理。

    上图是“你好”这句话的声音的波形示意图, 每个红色的框代表一帧数据,传统的方法需要知道每一帧的数据是对应哪个发音音素。比如第1,2,3,4帧对应n的发音,第5,6,7帧对应i的音素,第8,9帧对应h的音素,第10,11帧对应a的音素,第12帧对应o的音素。(这里暂且将每个字母作为一个发音音素)

    ------------------------------------

    传统模型的不足:

    1. 训练数据之前需要做语音对齐的预处理,工作比较耗时,并且在缺失对齐标签时,无法做出准确预测;

    2. 输出的预测是局部分类,只利用了当前帧的信息,并未利用序列的全局信息(比如相邻两个标签的连续性等,则需要通过其他外加处理。)

    ------------------------------------------

    CTC与传统模型的对比:

    1. 与传统的声学模型训练相比,采用CTC作为损失函数的声学模型训练,是一种完全端到端的声学模型训练,不需要预先对数据做对齐,只需要一个输入序列和一个输出序列即可以训练。这样就不需要对数据对齐和一一标注,输入输出之间的alignment不再那么重要。

    2. CTC直接输出序列预测的概率,不需要外部的后处理。

    --------------------------------------------

    CTC的算法原理

    <1> 符号定义与目标函数

    1. $A$: 序列标注任务中的标签所在字母表集合为 $A$

    2. $A'$: 扩展的字母表集合。CTC的softmax 输出层中,比 $A$ 多包含一个标签。我们记为$'blank'$. 即 $A' = A igcup {blank}$. 那么在输出预测时,前$|A|$个单元输出的是对应字母表$A$中各元素的预测概率,最后一个单元输出的是预测为$'blank'$的概率。

    3. $y_k^t$: 网络在 $t$ 时刻输出元素 $k$ 的概率,即在给定长度为 $T$ 的输入序列 $x$ 后,在 $t$ 时刻,预测为 $A'$ 中的元素 $k$ 的概率。

    4. $A^{'T}$: 在 $A'$ 集合上的所有长度为 $T$ 的序列集合。

    5. 假设在每一个时刻的输出与其他时刻的输出是条件独立的(或者说,条件独立于给定的 $x$ ),那么可以得到在给定输入 $x$ 后,得到 $A^{'T}$ 集合中任何一条路径 $pi$ 的概率分布: $ pi in A^{'T}$ 的分布:

    $$p(pi | x) = prod_{t = 1} ^ T y_{pi_t}^t  ag1$$ 。

    我们记在$A^{'T}$集合中的序列  $pi$ 为 $paths$.

    6. $l$: 我们记在 $A$ 集合中产生的标签序列为 $l$。

    7. 由于在$A^{'T}$ 集合中可能有多条 $paths$,最终所映射的都是同一个序列, 我们需要定义一个多对一的函数,来实现从 $paths$集合到预测序列的映射。$F: A^{'T}  ightarrow A^{le T}$

    其中,我们设定映射后的序列长度不大于映射前的序列长度。

    这是要做什么呢?举个例子:

    $F(a-ab-) = F(-aa--abb) = aab$ 

    函数映射关系是,将'-'与'-'之间的重复的元素,只保留一个,并且去掉'-'分隔。这样,无论我们的神经网络预测出的序列为‘a-ab-’ 或是 ‘-aa--abb’,它所对应的最终的预测结果都是 'aab',而我们的目标函数也是将 'aab' 与真实标签序列作比较。

    这样就不难看出,CTC算法并不要求预测标签与输入的一一对齐关系,而是关注于整个序列的最终预测结果,也就是经过这个函数映射后的结果。

    那么我们预测出真实标签序列的概率可以表示为:

    $$ p(l|x) = sum_{pi in F^{-1}(l)} p(pi|x) ag2$$ 

    即所有的可以映射为真实标签序列的 预测序列的概率和。

    <2> blank标签的角色

    1. 可以出现重复字符。

    设想一下,如果没有'-',对于单词中有重复字符的,比如'apple',其函数映射的结果为'aple',这是不能满足实际情况需要的

    2. 如果没有'-',那么神经网络需要一直不停的预测出来一个label,直到下一个不同的label出现。而真实情况中,经常出现一段间隔内(比如语音的停顿处),并没有标签。所以有blank可以满足这样的情况需要。

    <3> 前向后向算法的前向过程

    由公式(2),我们的目标函数是对所有可以映射为真实标签序列的paths的预测出的概率求和。那么首先,我们要先知道都有哪些paths可以映射为真实标签序列。

    对于长度为 $T$ 的输入序列和长度为 $U$ 的标签序列,有2^[(T - U^2 + U(T-3))] * 3^ [(U - 1)(T- U) - 2]种不同路径

    有指数级别的路径可能性,不能满足实际需要。为了降低时间复杂度,CTC算法处理时采用了动态规划方法。算法的主要思想是,在筛选可能的paths时,只选取前缀与$l$对应前缀是相同的那些paths. 这样说很难理解,举个例子。

    例子的来源:https://docs.google.com/presentation/d/12gYcPft9_4cxk2AD6Z6ZlJNa3wvZCW1ms31nhq51vMk/pub?start=false&loop=false&delayms=3000&slide=id.g24e9f0de4f_0_1095

     1. 首先,我们构造一个table,希望通过这个table,直观的看出所有可以映射到真实标签序列'apple'的可能路径。

    table的横坐标为输入的时间序列,纵坐标为将真实标签序列两两字母以'-'分隔,并且在首尾各加一个'-'。用$U'$标记标签序列经过'-'扩展后的序列。

    那么从首'-'或'a'开始,到尾'e'或'-'结束;箭头只能向右,或向下,所有依次经过a, p, p, l, e的那些路径,即为我们要找的,可以映射为真实标签序列的路径。

    比如下图1,黑线部分表示的预测的序列为 '- - a p - p l e'  (t1时刻预测'-', t2时刻预测为'-', t3时刻预测为'a' ...)。 注意,红色箭头是错误的,因为我们不可能先预测出第3个标签,再预测出第2个标签,标签需要按顺序依次预测出。所以,箭头只能向右或向下!$F( '- - a p - p l e') = 'apple'$

     图1.路径举例

    那么初始t1时刻,我们只能处于'-'或‘a’的位置,

    图2. 初始时刻状态

    而最终,我们需要依次经过apple所有字母。

    图3.搜索的最终结果状态。

    那么,求总路径的问题,也就是找从初始位置,到结束位置的所有的可能路径的问题。

    动态规划体现在哪里呢?敲黑板,下面内容是重点~

    (1) 对于一条可能路径,其字路径的概率可以表示为,对应时刻神经网络预测标签的概率乘积。比如,如下图所示 $p('- a p - ') = y_-^1 * y_a ^ 2 * y_p^3 * y_-^4$

    图4. subpath的概率计算

    (2) $alpha_t(s)$: 称为前向变量(forward variable)。表示 前缀末端 在 $t$ 时刻到达序列的第 $s$ 个位置的所有可能子路径的概率和。前缀的意义是,在前$(1-t)$时间内,经过映射后,可以得到真实标签序列的前s / 2个符号。我们把符合这个要求的所有可能路径前缀的概率加和,即为 $alpha_t(s)$。

    那么以此类推,在T时刻,可以到达终止节点'-',或真实标签最后一个符号的,概率和,即为所有可以映射得到真实标签序列的预测序列的概率和,也就是我们需要最大化的目标。

    如下图5所示:在 $t3$ 时刻,共有四条路径前缀终止于扩展的标签序列的的第4个节点p.那么$alpha_3(4) = p('-ap') + p('aap') + p('a-p') + p('app')$ 这四条子路径的前缀经过映射后,都可以得到真实标签序列的前缀:'ap'.

    图5.前缀相同的子路径的概率和

    (3) 之后,我们需要做的就是,对于每一个cell,都计算其对应的 $alpha_t(s)$。我们可以递归地进行计算。

    即计算可以到达(t, s) 这个cell 的所有子路径概率和 与 在t时刻预测出符号s的概率做乘积,即为在t时刻,到达符号s的所有子路径的概率。

    在计算中,有三种情况:

    Case1. 第 $s$ 个符号为blank时。

    比如 $s = 3, t = 3$,序列的第三个符号为 '-'。见图中红色的cell. $alpha_3(3)$ 只取决于 $alpha_2(3)$ (蓝色cell)和 $alpha_2(2)$ (绿色cell).  那么易得,$alpha_3(3) = (alpha_2(3) + alpha_2(2)) * y_-^3$.

    一般的,有$$alpha_t(s) = (alpha_{t-1}(s) + alpha_{t-1}(s-1)) * y_{seq(s)}^t ag3$$

    图6. Case1.前向算法中s=‘-’时的$alpha_s^t$ 计算

    Case2. 第s个符号与第(s - 2)个符号相同时,即$seq(s) == seq(s - 2) $。

    此时(t,s)cell,只取决于 (t-1, s) (蓝色cell) 和 (t - 1, s - 1)(绿色cell)。一般的:

    $$alpha_t(s) = (alpha_{t-1}(s) + alpha_{t-1}(s-1)) * y_{seq(s)}^t ag4$$

    图7. Case2.前向算法中seq(s) == seq(s - 2)时的$alpha_s^t$ 计算

    Case3. 其他情况。有:

    $$alpha_t(s) = (alpha_{t-1}(s) + alpha_{t-1}(s-1) +  alpha_{t-1}(s-2) ) * y_{seq(s)}^t ag5$$

     图8. Case3.前向算法中其他情况下 $alpha_s^t$ 的计算

    (4) 通过以上步骤,我们最终可以得到$alpha_T(U - 1)$ 和 $alpha_T(U)$。那么预测出真实标签序列的概率为 $$p(l|x) =alpha_T(U' - 1) + alpha_T(U') ag6 $$. 前向过程完成!

    图9. 前向过程完成得到预测出真实序列的概率

    <4> 前向后向算法的后向过程

    后向算法与前向类似,只是方向不同,后向是找 从序列末端到首端的各个子路径的概率。

    定义$eta_t(s)$为 后缀起始于序列末端, $t$ 时刻到达第 $s$ 个符号的所有可能子路径的概率和。

    比如,$t = 6, s = 8, eta_6(8)$表示所有以下图中红色节点开始的,最终可以到达序列末端的子路径的概率。$eta_6^(8) = p('lle') + p('l-e') + p('lee') + p('le')$

    图10. 反向过程举例

    那么,将前向过程中所有箭头反向,使用同样的计算方式,即可计算出反向变量。

    图10. 反向过程

    <5> 前向后向算法

     <3>部分$alpha_s(t)$前向变量记录1-t时间内预测出正确前缀的概率(或者可以说,子路径的概率和);<4>部分$eta_s(t)$后向变量记录t - T时间内预测出正确后缀的概率;

    那么

    $$alpha_t(s) * eta_t(s) / y_t^s ag7$$

    即为 在t时刻,所有正确预测的,并且经过第s符号的,路径的概率和。(除以 y_t^s 因为在 $alpha$ 和 $eta$ 中乘了两次)。

    举个例子:

    $alpha_{t3}(2) = y_-^{t1} * y_-^{t2} * y_a^{t3} +  y_-^{t1} * y_-^{t2} * y_-^{t3}$

    $eta_{t3}(2) = y_a^{t3} * y_p^{t4} * y_-^{t5} * y_p^{t6} * y_l^{t7} * y_e^{t8}$

    $alpha_{t3}(2) * eta_{t3}(2) = ( y_-^{t1} * y_-^{t2} * y_a^{t3} ) * (  y_a^{t3} * y_p^{t4} * y_-^{t5} * y_p^{t6} * y_l^{t7} * y_e^.{t8}) +  (y_-^{t1} * y_-^{t2} * y_-^{t3}) * (y_a^{t3} * y_p^{t4} * y_-^{t5} * y_p^{t6} * y_l^{t7} * y_e^{t8}) = (p('--ap-ple') + p('-aap-ple) + p('aaap-ple')) * y_a^3$  

    那么 $alpha_{t3}(2) * eta_{t3}(2) /  y_a^3$ = 在t3时刻,经过符号a的所有正确预测序列的概率和。

    图11. 前向后向算法,在t时刻经过第s符号的所有正确预测的路径的概率和

    那么将t3时刻,正确预测序列可能会预测出,a, -, p, -,用上面方法,将经过这四个符号的路径概率加和,即可得到在t3时刻,可以做出正确预测的概率。

    图11. 前向后向算法,在t时刻可以做出正确预测的概率

    最后,应用于1 - T的所有时刻,可以得到在任意时刻内预测出正确标签序列的概率。

    $$p('apple') = sum_{s = 1}^{|seq|} frac{alpha_s(t) * eta_s(t)}{y_{seq(s)} ^ t} ag8$$

    <6> 反向传播

    我们的目标是最大化$p('apple')$,也就是$ min {-ln(p('apple'))}$,这是我们的目标函数

    在反向传播时,我们需要对神经网络的每一个预测输出求偏导。

     $$frac{partial{(-ln(p('apple')))}}{partial{y_k^t}} = -frac{1}{p('apple')} * frac{partial{p('apple')}}{partial{y_k^t}} ag9$$ 

    我们重点看$$frac{partial{p('apple')}}{partial{y_k^t}} ag{10}$$的求解。

    $p('apple')= frac{alpha_{s1}(t) * eta_{s1}(t)}{y_{s1}^t} + ... + frac{alpha_{k}(t) * eta_{s1}(t)}{y_{k}^t} +... + frac{alpha_{sT}(t) * eta_{sT}(t)}{y_{sT}^t}$

    若t时刻过k,则t时刻时不可能经过其他字符的。也就是,在求偏导时,只有红色部分是包含$y_k^t$的,其他项可以看做常数项。

    最终,

    $$frac{partial{p('apple')}}{partial{y_k^t}} = -frac{1}{{y_k^t}^2} * sum_{s:seq(s) = k}alpha_t(s) * eta_t(s) ag{11}$$

    举个例子:

    这样就完成了!

    ------------------------------------------

    思考:

    $alpha_S(T)$即可可表示所有正确预测序列的概率和,也就是可以表示目标函数,为什么要引入$eta$呢?

    原因:为了反向传播时候求偏导方便呀!否则$alpha_S(T)$是关于$y_k^t$的一个复杂的函数,很难直接求导的,引入$eta$后,我们可以关注于t时刻内的偏导计算,会简便许多。

    ------------------------------------------

    <7> 要点总结

    1. 动态规划

    2. 矩阵 $alpha$ (前向变量)用于计算loss.

    3. 矩阵$eta$ (后向变量)用来方便计算gradients.

    参考链接:

    1. Supervised Sequence Labelling with Recurrent Neural Networks:https://www.cs.toronto.edu/~graves/preprint.pdf

    2. 语音识别中的CTC算法的基本原理解释:https://www.cnblogs.com/qcloud1001/p/9041218.html

    3. https://docs.google.com/presentation/d/12gYcPft9_4cxk2AD6Z6ZlJNa3wvZCW1ms31nhq51vMk/pub?start=false&loop=false&delayms=3000&slide=id.g24e9f0de4f_0_1095 

    =======================================

         感谢您的支持![支付宝]             

    您愿意请我吃一根雪糕吗? O(∩_∩)O

  • 相关阅读:
    HTML/CSS的学习过程一览
    C++ MFC实现基于RFID读写器的上位机软件
    Java实现UDP之Echo客户端和服务端
    Java实现TCP之Echo客户端和服务端
    Java实现Socket之WhoisClient
    Java实现Socket之TimeClient
    openWRT自学---针对backfire版本的主要目录和文件的作用的分析整理
    openWRT自学计划安排
    openWRT自学---对官方的开发指导文档的解读和理解 记录2:如何控制内核模块的编译
    openWRT自学---对官方的开发指导文档的解读和理解 记录1:编译一个package
  • 原文地址:https://www.cnblogs.com/shiyublog/p/10493348.html
Copyright © 2011-2022 走看看