CTC Loss原理及实现

最近看ctc看了很多遍,因为一直看不懂,所以就死磕到底,最后发现越看越有感觉,就记录一下。

1、CTC 主要解决的问题

CTC Loss要解决的问题就是当label长度小于模型输出长度时,如何做损失函数。

一般做分类时,已有的softmax loss都是模型输出长度和label长度相同且严格对齐,而语音识别或者手写体识别中,无法预知一句话或者一张图应该输出多长的文字,这时做法有两种:seq2seq+attention机制,不限制输出长度,在最后加一个结束符号,让模型自动和gt label对齐;另一种是给定一个模型输出的最大长度,但是这些输出并没有对齐的label怎么办呢,这时就需要CTC loss了。

2、对比传统方法

在传统的语音识别的模型中,我们对语音模型进行训练之前,往往都要将文本与语音进行严格的对齐操作。这样就有两点不太好:
 1. 严格对齐要花费人力、时间。
 2. 严格对齐之后,模型预测出的label只是局部分类的结果,而无法给出整个序列的输出结果,往往要对预测出的label做一些后处理才可以得到我们最终想要的结果。
  虽然现在已经有了一些比较成熟的开源对齐工具供大家使用,但是随着deep learning越来越火,有人就会想,能不能让我们的网络自己去学习对齐方式呢?因此CTC(Connectionist temporal classification)就应运而生啦。
  想一想,为什么CTC就不需要去对齐语音和文本呢?因为CTC它允许我们的神经网络在任意一个时间段预测label,只有一个要求:就是输出的序列顺序只要是正确的就ok啦~这样我们就不在需要让文本和语音严格对齐了,而且CTC输出的是整个序列标签,因此也不需要我们再去做一些后处理操作。
  对一段音频使用CTC和使用文本对齐的例子如下图所示:

如上图,传统的Framewise训练需要进行语音和音素发音的对齐,比如“s”对应的一整段语音的标注都是s;而CTC引入了blank(该帧没有预测值),“s”对应的一整段语音中只有一个spike(尖峰)被认为是s,其他的认为是blank。对于一段语音,CTC最后的输出是spike的序列,不关心每一个音素对应的时间长度。

与传统神经网络主要区别
训练流程和传统的神经网络类似,构建loss function,然后根据BP算法进行训练,不同之处在于传统的神经网络的训练准则是针对每帧数据,即每帧数据的训练误差最小,而CTC的训练准则是基于序列(比如语音识别的一整句话)的,比如最大化p(z|x)p(z|x) ,序列化的概率求解比较复杂,因为一个输出序列可以对应很多的路径,所有引入前后向算法来简化计算。

输出

语音识别中的DNN训练,每一帧都有相应的状态标记,比如有5帧输入x1,x2,x3,x4,x5,对应的标注分别是状态a1,a1,a1,a2,a2。
CTC的不同之处在于输出状态引入了一个blank,输出和label满足如下的等价关系:

F(a−ab−)=F(−aa−−abb)=aab

多个输出序列可以映射到一个输出。

前期准备

前后向算法

考虑到计算 p ( l ∣ x ) p(l|x) p(lx)需要计算很多条路径的概率,随着输入长度呈指数化增加,可以引入类似于HMM的前后向算法来计算该概率值。
为了引入blank节点,在label首尾以及中间插入blank节点,如果label序列原来的长度为U,那么现在变为U’=2U+1。

为看清全貌,假设T=7,则上图全貌如下:

前向(将前向与后向对比)

前向变量 α t ( s ) \alpha_t(s) αt(s) ,表示 t t t时刻在节点 s s s的前向概率值,其中 s ∈ [ 1 , 2 ∣ l ∣ + 1 ] s\in[1, 2|l|+1] s[1,2l+1]
初始化值如下:
α 1 ( 1 ) = y b 1 \alpha_1(1)=y_b^1 α1(1)=yb1
α 1 ( 2 ) = y l 1 1 \alpha_1(2)=y_{l_1}^1 α1(2)=yl11
α 1 ( s ) = 0 , ∀ s > 2 \alpha_1(s)=0,\quad \forall s > 2 α1(s)=0,s>2
递推关系:
α t ( s ) = { α ˉ t ( s ) y l s ′ t i f l s ′ = b o r l s − 2 ′ = l s ′ ( α ˉ t ( s ) + α t − 1 ( s − 2 ) ) y l s ′ t o t h e r w i s e \alpha_t(s)=\left\{ \begin{matrix} \bar{\alpha}_t(s)y_{l_s^{'}}^t \quad if l_s^{'}=b \quad or \quad l^{'}_{s-2}=l^{'}_s \\ (\bar\alpha_t(s)+\alpha_{t-1}(s-2))y_{l_s^{'}}^t \quad otherwise \end{matrix} \right. αt(s)={αˉt(s)ylstifls=borls2=ls(αˉt(s)+αt1(s2))ylstotherwise
其中:
α ˉ t ( s ) = d e f α t − 1 ( s ) + α t − 1 ( s − 1 ) \bar\alpha_t(s)\xlongequal{def}\alpha_{t-1}(s)+\alpha_{t-1}(s-1) αˉt(s)def αt1(s)+αt1(s1)


后向
初始化值:
β T ( ∣ l ′ ∣ ) = y b T \beta_T(|l^{'}|)=y_b^T βT(l)=ybT
β T ( ∣ l ′ ∣ − 1 ) = y l ∣ l ∣ T \beta_T(|l^{'}|-1)=y_{l_{|l|}}^T βT(l1)=yllT


ctc代码学习参考ctc

图1. 序列建模【src】

虽然并没为限定 Nw 具体形式,下面为假设其了某种神经网络(e.g. RNN)。
下面代码示例 toy Nw:

import numpy as np

np.random.seed(1111)

T, V = 12, 5
m, n = 6, V

x = np.random.random([T, m])  # T x m
w = np.random.random([m, n])  # weights, m x n

def softmax(logits):
    max_value = np.max(logits, axis=1, keepdims=True)
    exp = np.exp(logits - max_value)
    exp_sum = np.sum(exp, axis=1, keepdims=True)
    dist = exp / exp_sum
    return dist

def toy_nw(x):
    y = np.matmul(x, w)  # T x n 
    y = softmax(y)
    return y

y = toy_nw(x)
print(y)
print(y.sum(1, keepdims=True))

参考:CTC Loss和Focal CTC Loss

参考:CTC loss 理解

参考:CTC学习笔记(二) 训练和公式推导

参考:《Supervised Sequence Labelling with Recurrent Neural Networks》 chapter7
参考:【Learning Notes】CTC 原理及实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落花逐流水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值