文章来源于新一代Kaldi,作者NGK编辑部
从大家在 github 和微信群的反馈中我们发现有好些人对新一代 Kaldi 中的流式模型还不甚了解,本文试图作一个引导性的介绍,不包含如何安装软件,也不会有 step by step 的运行流程,里面会有一些示例性的运行脚本,具体细节请参看 github 仓库中的 README 和官方文档。
Introduction
Icefall项目中,我们使用了两种类型的Encoder来实现流式的Xformer-transducer模型,分别是Conformer[1]和Emformer[2].Conformer模型本身不支持流式,但我们可以使用 Mask 让模型只看到有限的上下文,实现 Chunk by Chunk 的计算。Emformer模型是 Facebook 团队提出的,本质上还是一个使用 Mask 实现 Chunk by Chunk 计算的 transformer 类模型,不过增加了一些机制以提升流式识别的准确率。Conformer
Icefall中实现流式Conformer模型的方法基本和Wenet[3]中的一致,使用 Unified non-streaming and streaming 的方法训练,即一部分的数据使用 Full Attention,一部分的数据使用 Dynamic Chunk Attention。不过,为了使模型更适宜 batch 推理,我们总是使用有限的left_context进行训练。
Emformer
Emformer主要在 Transformer 基础上做了两点比较大的改动,一个是引入了Memory Bank的机制让模型能看到更多的历史信息,有点像 LSTM 中的 Cell State。另一个是在训练中引入了Right Context,为实现 Right Context 它使用了一种 Hard Copy 的方式在特征层面为每个 Chunk 配上对应的 Right Context 输入。整体的模型结构如下图所示,Chunk 中增加了 Memory Bank 和 Right Context
egs/librispeech/ASR/conv_emformer_transducer_stateless2。我们致力于整合 Conformer 和 Emformer 模型的优点,目前来看可能会造成性能差异的两者的不同点是:1)Conformer 模型中包含Relative Position Encoding模块而 Emformer 中没有 【攻城狮内心OS:实在是加不上呀】。2)Emformer 在训练中可以看到Right Context, 而 Conformer 不行。
Training
小编注:数据准备相关的工作对流式和非流式模型没有区别,本文不会涉及,我们假设读者已经成功运行了数据集下面的
prepare.sh脚本。
Icefall中 LibriSpeech 数据集的pruned_transducer_statelessX模型大多已经支持了流式模型的训练,跟训练非流式模型相比,只需要增加几个训练参数就可以做到。dynamic-chunk-training=True, 训练流式或非流式模型的开关。short-chunk-size=20, Unified 方法训练中有一部分使用 Full Attention 训练,一部分使用 Chunk Attention 训练,这个参数是 Chunk Attention 训练中最大的 chunk size。为控制时延,我们一般不会用太大的 chunk size 解码,所以这个值取 20 左右就行。num-left-chunks=4, 在用 Chunk Attention 训练时当前 Chunk 能看到的左上下文的 Chunk 数 (注意:chunk size 是动态变化的),设置为 -1 表示看所有历史信息,但为了更好支持 batch 推理,一般设置一个固定的较小值。causal-convolution=True, 使用流式模型时,每一层的卷积必须是 causal 类型的。
--short-chunk-size是 Subsampling 之后的帧数。
--chunk-length--left-context-length--right-context-length都是 subsampling 之前的帧数.Decoding
decode.py里面。对于 Conformer 模型,只需将--simulate-streaming设置为True,并配上相应的 chunk size 就行。
--decode-chunk-size和--left-context都是 subsampling 之后的帧数。streaming_decode.py里面, 解码的命令跟 simulate streaming 类似,只是少了--simulate-streaming参数,并且--max-duration换成了--num-decode-streams,即几条音频同时解码。
--decode-chunk-size和--left-context都是 subsampling 之后的帧数。streaming_decode.py里还有一个--right-context参数,我们尝试在解码时引入更多的上下文信息,实现方法非常简单,即让相邻的两个 chunk 有些重叠。但这个策略并不是对所有的模型和解码参数有效,可能的原因是 right context 的引入导致了训练解码不一致问题。decode.py和streaming_decode.py文件里,只是参数名称略有不同。Deploy
export.py文件里,对于 Conformer 模型,导出命令长这样:
--jit要设置为True。另外,如果你要导出 流式的 Conformer 模型,必须设置--streaming-model为True, 并且--causal-convolution也为True。--jit为True。不过没有了 streaming 和非 streaming 的区别。

Adapt to streaming
pruned_transducer_stateless{,2, 3, 4, 5}和 WenetSpeech 中的pruned_transducer_stateless5支持流式训练。欢迎大家向其他数据集增加流式模型的支持,并跑出 SOTA 的结果。在非流式 Conformer Transducer 模型中支持流式只需要做几件事:pruned_stateless_emformer_rnnt2,conv_emformer_transducer_stateless,conv_emformer_transducer_stateless2。如果你想将 Emformer 模型用到其他数据集,我们推荐你使用conv_emformer_transducer_stateless2。引用链接
[1] Conformer: https://arxiv.org/pdf/2005.08100.pdf
[2] Emformer: https://arxiv.org/pdf/2010.10759.pdf
[3] Wenet: https://arxiv.org/pdf/2012.05481.pdf
[4] icefall: https://github.com/k2-fsa/icefall/blob/master/egs/librispeech/ASR/RESULTS.md#librispeech-bpe-training-results-pruned-stateless-streaming-conformer-rnn-t
[5] Conv-Emformer: https://github.com/k2-fsa/icefall/blob/master/egs/librispeech/ASR/RESULTS.md#librispeech-bpe-training-results-pruned-stateless-conv-emformer-rnn-t
[6] Conv-Emformer2: https://github.com/k2-fsa/icefall/blob/master/egs/librispeech/ASR/RESULTS.md#librispeech-bpe-training-results-pruned-stateless-conv-emformer-rnn-t-2
[7] README.md: https://github.com/k2-fsa/icefall/blob/master/egs/librispeech/ASR/RESULTS.md
[8] PR: https://github.com/k2-fsa/icefall/pull/454
