Skip to content
hnluo edited this page Apr 21, 2023 · 6 revisions

模型介绍

UniASR 模型是一种2遍刷新模型(Two pass)端到端语音识别模型。日益丰富的业务需求,不仅要求识别效果精度高,而且要求能够实时地进行语音识别。一方面,离线语音识别系统具有较高的识别准确率,但其无法实时的返回解码文字结果,并且,在处理长语音时,容易发生解码重复的问题,以及高并发解码超时的问题等;另一方面,流式系统能够低延时的实时进行语音识别,但由于缺少下文信息,流式语音识别系统的准确率不如离线系统,在流式业务场景中,为了更好的折中实时性与准确率,往往采用多个不同时延的模型系统。为了满足差异化业务场景对计算复杂度、实时性和准确率的要求,常用的做法是维护多种语音识别系统,例如,CTC系统、E2E离线系统、SCAMA流式系统等。在不同的业务场景使用不同的模型和系统,不仅会增加模型生产成本和迭代周期,而且会增加引擎以及服务部署的维护成本。因此,我们设计了离线流式一体化语音识别系统——UniASR。UniASR同时具有高精度和低延时的特点,不仅能够实时输出语音识别结果,而且能够在说话句尾用高精度的解码结果修正输出,与此同时,UniASR采用动态延时训练的方式,替代了之前维护多套延时流式系统的做法。通过设计UniASR语音识别系统,我们将之前多套语音识别系统架构统一为一套系统架构,一个模型满足所有业务场景,显著的降低了模型生产和维护成本。 其模型结构如下图所示: image.png UniASR模型结构如上图所示,包含离线语音识别部分和流式语音识别部分。其中,离线与流式部分通过共享一个动态编码器(Encoder)结构来降低计算量。流式语音识别部分是由动态时延 Encoder 与流式解码器(Decoder)构成。动态时延 Encoder 采用时延受限有句记忆单元的自注意力(LC-SAN-M)结构;流式 Decoder 采用动态 SCAMA 结构。离线语音识别部分包含了降采样层(Sride Conv)、Big-Chunk Encoder、文本Encoder与SCAMA Decoder。为了降低刷新输出结果的尾点延时,离线识别部分采用大Chunk 流式结构。其中,Stride Conv结构是为了降低计算量。文本 Encoder 增加了离线识别的语义信息。为了让模型能够具有不同延时下进行语音识别的能力,我们创新性地设计了动态时延训练机制,使得模型能够同时满足不同业务场景对延时和准确率的要求。 根据业务场景特征,我们将语音识别需求大致分为3类: 低延迟实时听写:如电话客服,IOT语音交互等,该场景对于尾点延迟非常敏感,通常需要用户说完以后立马可以得到识别结果。 流式实时听写:如会议实时字幕,语音输入法等,该场景不仅要求能够实时返回语音识别结果,以便实时显示到屏幕上,而且还需要能够在说话句尾用高精度识别结果刷新输出。 离线文件转写:如音频转写,视频字幕生成等,该场景不对实时性有要求,要求在高识别准确率情况下,尽可能快的转录文字。

模型推理

以8k uniasr中文模型为例

代码
  • 若输入格式为文件wav.scp(注:文件名需要以.scp结尾),可添加 output_dir 参数将识别结果写入文件中,api调用方式可参考如下范例:
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks

inference_pipeline = pipeline(
    task=Tasks.auto_speech_recognition,
    model='damo/speech_UniASR_asr_2pass-zh-cn-8k-common-vocab3445-pytorch-online',
    output_dir='./decode_dir'
)

rec_result = inference_pipeline(audio_in='wav.scp')
print(rec_result)

模型训练

以8k uniasr中文模型为例

代码(finetune.py):
import os
import json
import shutil

from modelscope.pipelines import pipeline
from modelscope.metainfo import Trainers
from modelscope.trainers import build_trainer
from modelscope.utils.constant import Tasks

from funasr.datasets.ms_dataset import MsDataset
from funasr.utils.compute_wer import compute_wer


def modelscope_finetune(params):
    if not os.path.exists(params["model_dir"]):
        os.makedirs(params["model_dir"], exist_ok=True)
    # dataset split ["train", "validation"]
    ds_dict = MsDataset.load(params["dataset_path"])
    kwargs = dict(
        model=params["modelscope_model_name"],
        data_dir=ds_dict,
        dataset_type=params["dataset_type"],
        work_dir=params["model_dir"],
        batch_bins=params["batch_bins"],
        max_epoch=params["max_epoch"],
        lr=params["lr"])
    trainer = build_trainer(Trainers.speech_asr_trainer, default_args=kwargs)
    trainer.train()
    pretrained_model_path = os.path.join(os.environ["HOME"], ".cache/modelscope/hub", params["modelscope_model_name"])
    required_files = ["am.mvn", "decoding.yaml", "configuration.json"]
    for file_name in required_files:
        shutil.copy(os.path.join(pretrained_model_path, file_name),
                    os.path.join(params["model_dir"], file_name))
    

def modelscope_infer(params):
    # prepare for decoding
    with open(os.path.join(params["model_dir"], "configuration.json")) as f:
        config_dict = json.load(f)
        config_dict["model"]["am_model_name"] = params["decoding_model_name"]
    with open(os.path.join(params["model_dir"], "configuration.json"), "w") as f:
        json.dump(config_dict, f, indent=4, separators=(',', ': '))
    decoding_path = os.path.join(params["model_dir"], "decode_results")
    if os.path.exists(decoding_path):
        shutil.rmtree(decoding_path)
    os.mkdir(decoding_path)

    # decoding
    inference_pipeline = pipeline(
        task=Tasks.auto_speech_recognition,
        model=params["model_dir"],
        output_dir=decoding_path
    )
    audio_in = os.path.join(params["test_data_dir"], "wav.scp")
    inference_pipeline(audio_in=audio_in)

    # computer CER if GT text is set
    text_in = os.path.join(params["test_data_dir"], "text")
    if os.path.exists(text_in):
        text_proc_file = os.path.join(decoding_path, "1best_recog/token")
        compute_wer(text_in, text_proc_file, os.path.join(decoding_path, "text.cer"))
        os.system("tail -n 3 {}".format(os.path.join(decoding_path, "text.cer")))

if __name__ == '__main__':
    finetune_params = {}
    finetune_params["modelscope_model_name"] = "damo/speech_UniASR_asr_2pass-zh-cn-8k-common-vocab3445-pytorch-online"
    finetune_params["dataset_path"] = "./example_data/"
    finetune_params["model_dir"] = "./checkpoint"
    finetune_params["dataset_type"] = "small"
    finetune_params["batch_bins"] = 2000
    finetune_params["max_epoch"] = 20
    finetune_params["lr"] = 0.00005

    modelscope_finetune(finetune_params)

    infer_params = {}
    infer_params["model_dir"] = "./checkpoint"
    infer_params["decoding_model_name"] = "20epoch.pb"
    infer_params["test_data_dir"] = "./example_data/test/"
    modelscope_infer(infer_params)
流程:

modelscope模型资源下载->本地数据加载->模型训练->模型测试并计算CER

运行命令
CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node 2 finetune.py > log.txt 2>&1

如果想在一台机器上跑多个多卡任务,请加上master_port参数,防止DDP通信端口被占用。具体执行命令为:

CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node 2 --master_port 47770 finetune.py > log.txt 2>&1
训练代码主要参数说明
  • modelscope_model_name:需要finetune的modelscope模型名字
  • dataset_path:训练数据目录、需自行提供,目录格式如下:
tree ./example_data/
./example_data/
├── validation
│   ├── text
│   └── wav.scp
├── test
│   ├── text
│   └── wav.scp
└── train
    ├── text
    └── wav.scp

3 directories, 6 files

text文件中存放音频标注,wav.scp文件中存放wav音频绝对路径,样例如下:

cat text
BAC009S0002W0122 而 对 楼 市 成 交 抑 制 作 用 最 大 的 限 购
BAC009S0002W0123 也 成 为 地 方 政 府 的 眼 中 钉
IC0004W0044 放 一 首 歌 the sound of silence

cat wav.scp
BAC009S0002W0122 /mnt/data/wav/train/S0002/BAC009S0002W0122.wav
BAC009S0002W0123 /mnt/data/wav/train/S0002/BAC009S0002W0123.wav
IC0004W0044  /mnt/data/wav/train/S0002/IC0004W0044.wav
  • model_dir:训练模型、配置文件、解码结果保存目录
  • dataset_type:训练dataloader类型。训练数据小于1000小时推荐small,大于1000小时推荐large。small、large dataloader主要的区别在于训练读取数据是否做全局shuffle
  • batch_bins:训练的batch_size值,如果dataset_type="small",batch_bins表示为fbank特征帧数,如果dataset_type="large",batch_bins表示为样本的总时长(单位ms)。
  • max_epoch:训练的epoch数
  • lr:训练学习率