conformer

2 downloads 0 likes on ModelScope updated

Model card

模型简介

2020年,Google在Transformer结构的基础上提出了Conformer(convolution-augmented transformer for speech recognition)

encoder:

img

Decoder: We use a single-LSTM-layer decoder in all our models.

参数量

img

数据集评估

model dev(CER%) test(CER%) RTF
Conformer 4.42 4.87 0.21

模型推断

同sensevoice那一套流程,但是不支持中文,只支持英文。

# 基于funasr的推断简化版,这里推断结果与modelscope一致
# model = AutoModel(model='iic/SenseVoiceSmall')
model = AutoModel(model='iic/speech_conformer_asr-en-16k-vocab4199-pytorch', device="cpu")
# res = model.generate(input='./example/en.mp3')
res = model.generate(input='./example/asr_example.wav')
# res = model.generate(input='./example/asr_example_zh.wav')
print(res)

'./example/en.mp3'推断输出,结果正确。

[{'key': 'en', 'token': ['the', 'tri@@', 'bal', 'chi@@', 'ef@@', 'tain', 'called', 'for', 'the', 'boy', 'and', 'presented', 'him', 'with', 'fifty', 'pieces', 'of', 'gold'], 'text': 'the tribal chieftain called for the boy and presented him with fifty pieces of gold'}]

酋长召唤了那个男孩,并给了他五十枚金币。

模型导出

encoder部分

在funasr->models->transformer->model.py中Transformer类中的inference函数导出模型encoder部分

class Transformer(nn.Module):
    def inference():
        ...
      # Encoder
        encoder_out, encoder_out_lens = self.encode(speech, speech_lengths)
        # lmy: for conformer export onnx
        print("exporting onnx...")
        torch.onnx.export(self.encoder,(speech,speech_lengths),'./conformer.onnx',opset_version=17)
        import numpy as np
        input_speech = speech.numpy().astype(np.float32)
        input_speech_lengths = speech_lengths.numpy().astype(np.float32)
        input_speech.tofile('./speech.ftmp')
        input_speech_lengths.tofile('./speech_lengths.ftmp')

encode输出大小torch.Size([1, 120, 512])

encode输入torch.Size([1, 120, 560])

encode输入lens torch.Size([1])

decoder部分

说明:论文中说解码器是一层LSTM,但代码中的解码过程主要依赖于“束搜索”(beam search)算法。

nbest_hyps = self.beam_search(
            x=encoder_out[0],
            maxlenratio=kwargs.get("maxlenratio", 0.0),
            minlenratio=kwargs.get("minlenratio", 0.0),
        )

代码说明:self.beam_search 方法会根据编码器的输出(encoder_out[0]),以及最大/最小长度比例(maxlenratio、minlenratio),生成一组候选的解码结果(nbest_hyps)。束搜索是一种在序列生成任务中常用的解码策略,可以在每一步保留概率最高的若干条路径,从而获得更优的输出序列。得到的 nbest_hyps 是一个包含多个假设(hypothesis)的列表,每个假设都代表一种可能的解码结果。代码只保留前 self.nbest 个最优假设。对每个 batch 的数据,遍历所有 nbest 假设,取出每个假设的输出序列 hyp.yseq,去掉起始符(sos)、终止符(eos)和 blank(通常为0),只保留有效的 token id。这些 token id 会通过 tokenizer.ids2tokens 转换为实际的 token,再通过 tokenizer.tokens2text 转换为文本。最后还会经过后处理(如去除多余空格、符号等),得到最终的解码文本。

这个init_beam_search算法主要是从transformer decoder 和ctc这两个解码器进行评分并筛选。

模型配置文件中配置了decoder参数

# decoder
decoder: TransformerDecoder
decoder_conf:
    attention_heads: 16
    linear_units: 1536
    num_blocks: 16
    dropout_rate: 0.1
    positional_dropout_rate: 0.1
    self_attention_dropout_rate: 0.0
    src_attention_dropout_rate: 0.0

模型编译

encoder部分

Where 算子不支持

不支持定位funasr->models->transformer->attention.py(同sensevoicesmall)

class MultiHeadedAttention(nn.Module):
    def Forward_attention(self,walue,scores,mask):
        ...
        if mask is not None:

            attn = torch.softmax(scores,dim=-1)

            # # lmy note: for onnx export
            # mask = mask.unsqueeze(1).eq(0)  # (batch, 1, *, time2)

            # min_value = -float(
            #     "inf"
            # )  # min_value = float(np.finfo(torch.tensor(0, dtype=qk.dtype).numpy().dtype).min)
            # scores = scores.masked_fill(mask, min_value)
            # attn = torch.softmax(scores, dim=-1).masked_fill(
            #     mask, 0.0
            # )  # (batch, head, time1, time2)
            # # lmy note end
        else:
            attn = torch.softmax(scores, dim=-1)  # (batch, head, time1, time2)

修改后成功导出模型encode部分,输入为[1,120,560]耗时为114.767ms

img

decoder部分

BeamSearch

BeamSearch 是 FunASR 里用于做解码的类,它会用你给定的参数(如 beam_size、权重、打分器、sos/eos、词表等)来做语音识别的束搜索推理。

beam_size:束宽,决定每步保留多少条候选路径。

weights:各个打分器(如 CTC、decoder、LM、ngram、length_bonus)的权重。

scorers:包含 CTC、decoder、长度奖励等打分器对象。

sos/eos:起始和终止符号的 id。

vocab_size、token_list:词表大小和内容。

pre_beam_score_key:控制 CTC/Attention 混合解码的策略。

为什么不能直接导出 ONNX 或 trace?

BeamSearch 不是标准的 nn.Module,内部有大量 Python 控制流、list、dict、对象操作,甚至可能有 for/while/if 等原生逻辑。这些都不能被 torch.jit.trace 或 torch.onnx.export 支持。

目前 FunASR 的 BeamSearch 主要是 Python 逻辑,不适合直接导出。

这代码里的decoder是给ctc和transformer_decoder两部分初始化分数分别为0.5,我为了方便,不动态选择decoder,直接指定ctc。参照sensevoicesmall,ctc是可以导出且编译的。

# lmy modified decoder beam search
        ctc_logp = self.ctc.softmax(encoder_out)
        x = ctc_logp[0, : encoder_out_lens[0].item(), :]
        yseq = x.argmax(dim=-1)
        yseq = torch.unique_consecutive(yseq, dim=-1)
        # 去掉 blank 和重复
        prev = None
        output_ids = []
        for idx in yseq:
            if idx != self.blank_id and idx != prev:
                output_ids.append(idx)
            prev = idx
        # 转成文本
        token = tokenizer.ids2tokens(output_ids)
        text = tokenizer.tokens2text(token)
        text_postprocessed, _ = postprocess_utils.sentence_postprocess(token)
        result_i = {"key": key[0], "token": token, "text": text_postprocessed}
        results = [result_i]
        return results, meta_data

两个demo结果如下

'./example/en.mp3'推断输出,结果正确。

[{'key': 'en', 'token': ['the', 'tri@@', 'bal', 'chi@@', 'ef@@', 'tain', 'called', 'for', 'the', 'boy', 'and', 'presented', 'him', 'with', 'fifty', 'pieces', 'of', 'gold'], 'text': 'the tribal chieftain called for the boy and presented him with fifty pieces of gold'}]

酋长召唤了那个男孩,并给了他五十枚金币。

您可以通过如下git clone命令,或者ModelScope SDK来下载模型

SDK下载

#安装ModelScope
pip install modelscope
#SDK模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('limengying/conformer')

Git下载

#Git模型下载
git clone https://www.modelscope.cn/limengying/conformer.git