目录
CTC解决的问题
CTC Loss要解决的问题就是当label长度小于模型输出长度时,如何做损失函数。
一般做分类时,已有的softmax loss都是模型输出长度和label长度相同且严格对齐,而语音识别或者手写体识别中,无法预知一句话或者一张图应该输出多长的文字,这时做法有两种:seq2seq+attention机制,不限制输出长度,在最后加一个结束符号,让模型自动和gt label对齐;另一种是给定一个模型输出的最大长度,但是这些输出并没有对齐的label怎么办呢,这时就需要CTC loss了。
所以,如果要计算某个输出概率,可以累加其对应的全部输出序列o (也即映射到最终label的“路径”)的概率即可,如下图。
从网络输出到标签
由于我们没有每个时刻输出对应的label,因此CTC使用最大似然进行训练(CTC 假设输出的概率是(相对于输入)条件独立的)
给定输入x,输出序列 的条件概率是:
是序列
中的一个元素 ;
y为模型在所有时刻输出各个字符的概率,shape为C*T(T是时刻,提前已固定。C是字符类别数,所有字符+blank(不是空格,是空) ;
是模型t时刻输出为
的概率;
是一个长度为T的输出序列;
论文中说,把中的元素称之为paths,并用
表示。这个公式就是定义了在输入为x的时候,输出为某个路径
的概率,这个概率的算法,就是把对应的每个时刻的网络输出的对应元素的概率乘起来。论文里说,公式假设了不同时刻网络的输出概率是条件独立的,并且这一点通过给定的网络的输出层与网络不存在反馈连接来保证,原文是:This is ensured by requiring that no feedback connections exist from the output layer to itself or the network。疑惑的是RNN的输出在时间上是存在依赖性的,这里说不同时刻是独立的,总觉得哪里不太对。我的理解是,当RNN的输出确定之后,这时候对于CTC网络来说,每个时刻的输出是独立的,这样似乎可以解释得通。
从网络的输出映射为一个唯一的label的方法,其实就是把输出串的blank删掉,并且相邻的重复字符合并起来就好了。
:这个就是上面说的映射函数,方法就向上面说的那样。
表示可能输出的序列集合,集合元素的长度小于或等于T。
如:B(a−ab−)=B(−aa−−abb)=aab
这个公式定义了在输入为 x 的时候,输出一个给定label的值 的概率,计算方法就是把所有可以用 B 函数映射为ll的路径的概率相加。
构造目标函数
最终的目标就是最大化标签
然而没有一个好的解法的,因为要枚举所有的可能的 的话,时间复杂度就爆炸了。所以论文中给出了两个求近似的解法。
前向传播
两个求近似的解法。
best path decoding求解:
其实就是把每个时刻的最大概率值输出,每个时刻连接在一起作为最终的输出。这种方式计算简单,但不保证能找到最大概率的label。用这个输出序列来求出最后的label:
prefix search decoding求解:
其实我们可以遍历算出所有输出序列,相同标签label的再相加,然后找出最大概率的label,但是很耗时,有指数级别的路径可能性。为了降低时间复杂度,CTC算法处理时采用了动态规划方法。算法的主要思想是,在筛选可能的paths时,只选取前缀与label对应前缀是相同的那些paths。
: 称为前向变量(forward variable)。表示 前缀末端 在 t 时刻到达序列的第 s 个位置的所有可能子路径的概率和。
第一种情况:
(注意对于特殊情况,s=2,t=2其实只有一条路径到达该位置,因为t=1时序列为"a-"这种情况是不存在的,只有为"a"这一种情况,但不影响通用表达式,因为此时的概率为0)
第二种情况:
第三种情况:
最后结果:
于是有论文里的公式:
反向传播
定义βt(s)为 后缀起始于序列末端, t 时刻到达第 s 个符号的所有可能子路径的概率和。
则有
表示t时刻path经过的节点。
表示插入空格的序列,其长度是2*len(label)+1。
对求偏导有:
过程如下:对于
若t时刻过k,则t时刻时不可能经过其他字符的。也就是,在求偏导时,如下图只有红色部分是包含的,其他项可以看做常数项。
和
是和
相关的。
设 = m *
,
= n *
. 则有
*
= m*n*
CTC Loss函数相对于RNN输出层元素的求导过程如下图所示:
CTC代码示例http://blog.prince2015.club/2020/03/30/ctc/http://blog.prince2015.club/2020/03/30/ctc/
预测过程
前面提到,在预测阶段,给定一个输入 x ,计算最大概率对应的输出序列。如果假设时间片之间相互独立,那么只需将每一时间片对应概率最大的字符作为预测值,然后组成序列,最后做去重等处理得到最终结果。不过这样并没有考虑多个序列经过对齐对应同一个输出结果。例如,假如 和
各自的概率都低于
,而二者的概率之和高于后者。前者对齐后的结果均为
,而后者对齐的结果仍为
,显然输出
比
更合理些。所以为了避免这一问题的发生,又引入了另一种算法,称为Beam Search。
里面有个参数 B ,用来指定每次保留的前缀序列的个数。假如设置 B = 3 ,则每次选取概率最大的3个前缀序列,比如 时选取概率最大的3个字符,
时也选取概率最大的3个字符,这样便有9种组合方式。当然对齐之后可能会对应相同的输出,所以要将结果相同的前缀序列进行合并(概率也要相加),然后挑出概率最大的3个作为下一次的输出,以此类推。以序列
为例,具体过程如下图所示。
这里要注意的是,当前缀序列的末尾字符与下一个字符相同时,合并可以产生两种有效输出。
比如上图中 t = 3 时前缀序列为 a , 而合并的字符同样为 ,这样既可以输出
,也可以输出
,二者都是可能的。因为在
时,其中一个结果占位符
在对齐时被移除了,但是在这里后面又遇到了相同的字符,按照前面定义的规则,此时合并的结果应该为
。
的两种计算情况如下图所示。
所以,这两种结果应作为两种序列分别进行概率计算。当然,为了能够计算这两种情况对应的概率,需要分别记录以 结尾的前缀序列的概率,以及不以
结尾的前缀序列的概率。