PaddleSpeech 在线 ASR 引擎深度解析:asr_engine 模块的流式语音识别服务实现
2026/9/23 19:28:35 网站建设 项目流程
  • 人工智能
  • 语音
  • 音频
  • NLP
  • 媒体生成

【免费下载链接】PaddleSpeech

Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword Spotting. Won NAACL2022 Best Demo Award.

项目地址:https://gitcode.com/paddlepaddle/PaddleSpeech
点击查看免费下载

本文围绕 PaddleSpeech 服务端在线(流式)语音识别的核心 Python 实现——asr_engine.py 模块展开,系统讲解其引擎架构、WebSocket 连接级状态机、流式特征提取、分块(chunk)解码、CTC 前缀束搜索、端点检测与二遍 rescoring 的完整实现原理。读完本文,你将掌握 PaddleSpeech 在线 ASR 服务"边说话边出字"的技术细节,以及如何通过服务端 YAML 配置驱动 deepspeech2 / conformer 在线模型。

模块定位:在线 ASR 服务引擎的三个层次

paddlespeech.server.engine.asr.online.python.asr_engine是 PaddleSpeech Serving(paddlespeech_server)中"在线语音识别(online ASR)"的 Python 引擎实现,与仓库中的onnxpaddleinference变体同属一个功能族(见 paddlespeech/server/engine/asr/online 目录)。该模块对外导出三个核心类:

职责生命周期
ASREngine全局引擎,持有服务配置与执行器,负责设备初始化与模型装载进程级(继承单例BaseEngine
ASRServerExecutor模型资源装载器,继承 CLI 的ASRExecutor,负责下载/加载模型、构建文本特征器引擎级,全局唯一
PaddleASRConnectionHanddler每个 WebSocket 连接一个的处理器,维护该路音频流的状态(缓存、解码进度、结果)连接级,随连接创建与销毁

三层分工非常清晰:ASREngine只做一次性的资源初始化,ASRServerExecutor完成模型加载与配置合并,而真正"边收音频边解码"的实时逻辑全部封装在PaddleASRConnectionHanddler中——这是理解整个在线 ASR 服务的关键入口。

从源码结构看,ASREngine.init(config)会依次完成:读取服务配置 → 创建ASRServerExecutor→ 设置设备(支持cpugpu:id,异常时会提示检查 YAML 中device字段)→ 调用init_model()装载模型,成功后打印Initialize ASR server engine successfully on device: ...ASREngine.new_handler()则为每个连接返回一个新的PaddleASRConnectionHanddler(self)(见 asr_engine.py),这正对应 WebSocket 服务"一路连接、一路解码"的模型。

服务端配置:如何启用并调优在线 ASR 引擎

在线 ASR 引擎通过服务端 YAML 配置驱动。以 Conformer 在线模型为例,ws_conformer_application.yaml 给出了完整配置:

# 服务设置 host: 0.0.0.0 port: 8090 protocol: 'websocket' # websocket 仅支持 online 引擎类型 engine_list: ['asr_online'] # 任务格式:<speech task>_<engine type> # ASR 引擎配置 asr_online: model_type: 'conformer_online_multicn' am_model: # am 静态模型 pdmodel 文件路径 [可选] am_params: # am 静态模型 pdiparams 文件路径 [可选] lang: 'zh' sample_rate: 16000 cfg_path: # 模型配置文件路径 [可选] decode_method: # 解码方式,留空则由引擎自动设置 num_decoding_left_chunks: -1 # 允许参考的历史 chunk 数,-1 表示不限制 force_yes: True device: cpu # cpu 或 gpu:id continuous_decoding: True # 端点检测命中后是否连续解码 am_predictor_conf: device: # 推理设备,可单独指定 switch_ir_optim: True glog_info: False # True -> 打印 glog summary: True # False -> 不打印 predictor 配置

其中am_modelam_paramscfg_path留空时,引擎会根据model_type + '-' + lang + '-' + sample_rate_str(如conformer_online_multicn-zh-16k)自动从资源库下载对应模型。sample_rate支持 16000(16k)与 8000(8k),并在特征提取时断言与预处理配置中的fs一致(见 asr_engine.py)。

对于 DeepSpeech2 在线模型,可参考 ws_ds2_application.yaml,其中asr_online-onnx使用 ONNX Runtime 推理(am_predictor_confgraph_optimization_levelintra_op_num_threads等 ONNX Session 参数),asr_online-inference使用 Paddle Inference。需要注意:DeepSpeech2 在线模型不支持端点检测,引擎中直接断言continuous_decoding is False, "ds2 model not support endpoint"(见 asr_engine.py)。

连接处理器:一路音频流的完整状态机

PaddleASRConnectionHanddler的构造函数(见 asr_engine.py)从全局引擎上继承了配置、模型类型、采样率与文本特征器,并完成三件关键初始化:

  1. 特征提取器:从模型配置的preprocess_config构建Transformation,并读取win_length(帧长)与n_shift(帧移,单位均为采样点),换算得到frame_shift_in_ms——这是后续端点检测的时间基准。
  2. 解码器分发init_decoder()根据model_type分流——deepspeech2构建CTCDecoder并绑定 Paddle Inference 预测器;conformer/transformer则构建CTCPrefixBeamSearch搜索器与OnlineCTCEndpoint端点检测器(见 asr_engine.py)。
  3. 状态复位reset()统一清零采样计数、帧计数、端点标志,并按模型类型重置解码器缓存。

连接级状态被拆分为三组:

  • model_reset():重置音频残留remained_wav、特征缓存cached_feat,以及 Conformer 在线解码所需的att_cache(自注意力缓存)、cnn_cache(卷积缓存)、encoder_out与全局解码偏移offset(以解码帧为单位);
  • output_reset():重置部分/最终结果result_transcripts、词级时间戳word_time_stamp、束搜索假设hyps
  • 会话级计数:num_samples(累计采样点)、num_frames(当前话语帧数)、global_frame_offset(连续解码时的全局帧偏移)。

正是这组状态,支撑了"音频可以断断续续到达、结果可以逐块产出"的在线体验。

流式特征提取:从 PCM 字节流到 FBank 缓存

extract_feat(samples)接收 WebSocket 传入的 PCM 字节串(见 asr_engine.py),处理流程为:

  1. np.frombuffer(samples, dtype=np.int16)将字节流还原为 16 位 PCM 采样点,累加全局采样计数;
  2. 新音频与上次剩余remained_wav拼接(保证不丢帧);
  3. 若不足一帧win_length则直接返回 0,等待更多数据;
  4. remained_wav做 FBank 预处理得到x_chunk,转为(1, T, D)张量并按时间轴拼入cached_feat
  5. 更新帧计数num_frames,并将已消费的音频截断:remained_wav = remained_wav[n_shift * num_frames:],即按帧移滑动窗口,只保留不足以再成帧的尾巴。

这里的特征缓存cached_feat是流式解码的"蓄水池"——每次解码消费其中的若干帧后,只保留end - cached_feature_num的尾部帧用于下一块的上下文拼接。

分块流式解码:deepspeech2 与 conformer 两条路径

decode(is_finished)是连接处理器的解码入口(见 asr_engine.py),按模型类型走两条完全不同的实现。

DeepSpeech2:逐 chunk 前向 + 循环状态

DeepSpeech2 的解码参数由在线结构固定:decoding_chunk_size=1context=7subsampling=4,由此推导:

  • cached_feature_num = context - subsampling = 3(下一块需要保留的上下文帧数);
  • decoding_window = (chunk_size - 1) * subsampling + context = 7(解码窗口);
  • stride = subsampling * chunk_size = 4(滑动步长)。

decode_one_chunk通过 Paddle Inference 预测器(am_predictor)依次设置输入(音频、音频长度、RNN 隐状态h_box、单元状态c_box)与输出句柄,run()后取出 logits 交给CTCDecoder.next()滚动解码,并将输出状态写回chunk_state_h_box/c_box作为下一 chunk 的循环初始状态(见 asr_engine.py)。由于需要拼接上下文,当缓存帧数不足解码窗口且未结束时,解码会等待更多音频。

Conformer/Transformer:chunk 自注意力 + 增量编码

Conformer 在线模型利用流式编码器的forward_chunk实现真正的增量解码(见 asr_engine.py):

decoding_chunk_size = cfg.decoding_chunk_size # 例如 16,解码帧单位 num_decoding_left_chunks = cfg.num_decoding_left_chunks # 例如 -1 subsampling = self.model.encoder.embed.subsampling_rate # 例如 4 context = self.model.encoder.embed.right_context + 1 # 例如 7 required_cache_size = decoding_chunk_size * num_decoding_left_chunks

每个 chunk 的音频片段通过model.encoder.forward_chunk(chunk_xs, self.offset, required_cache_size, att_cache=..., cnn_cache=...)前向,offset记录全局解码帧位置;各 chunk 输出y拼接后累积到encoder_out。随后计算 CTC log 概率(model.ctc.log_softmax(ys)),送入CTCPrefixBeamSearch搜索器得到当前最优假设hyps,最后更新特征缓存。

is_finished=True时(末尾帧),只需保证缓存大于context帧即可处理最后的上下文,确保不遗漏句尾内容。

CTC 前缀束搜索:在线增量解码的核心

CTCPrefixBeamSearch(见 ctc_search.py)实现了 CTC prefix beam search 的增量版本,用于 Conformer 在线解码:

  • 两级剪枝:每帧先用logp.topk(first_beam_size)做第一级 token 剪枝,再按log_add([pb, pnb])(blank 与非 blank 路径对数概率之和)排序截取second_beam_size条假设,控制计算量;
  • 状态定义:每条假设维护 7 个字段——blank 结束分数、非 blank 结束分数、viterbi blank 结束分数、viterbi 非 blank 分数、当前 token 概率、blank 结束时间戳列表、非 blank 结束时间戳列表。时间戳信息正是后续词级时间戳输出的数据来源;
  • 跨 chunk 延续cur_hypsabs_time_stepreset()前一直保留,search()每处理一个 chunk 的 CTC 概率后更新假设,从而把多块音频的搜索状态无缝衔接,实现"增量出字"。

get_one_best_hyps()返回当前最优假设,update_result()通过text_feature.defeaturize(hyp)将 token id 序列转换为可读文本,写入result_transcripts

端点检测与连续解码:一句话说完自动断句

OnlineCTCEndpoint(见 ctc_endpoint.py)实现基于 CTC blank 概率的在线端点检测,参照 "End-to-End ASR Integrated with CTC-based Voice Activity Detection" 论文思路,定义了三条终止规则(任一命中即触发端点):

规则must_contain_nonsilencemin_trailing_silencemin_utterance_length含义
rule1False5000 ms0即使什么都没解码,静音 5 秒也超时
rule2True1000 ms0解码出内容后再静音 1 秒即断句
rule3False020000 ms无论状态如何,话语达到 20 秒强制截断

检测逻辑:对每个 CTC 帧计算blank_prob = exp(logprob[blank]),超过blank_threshold=0.8视为静音帧并累计trailing_silence_frames,否则清零;再按frame_shift_in_ms换算为毫秒后与三条规则比对(见 ctc_endpoint.py)。

continuous_decoding=True且端点被检测到时,endpoint_state置位,reset_continuous_decoding()会记录global_frame_offset、重置模型缓存与搜索器/端点器,开始下一句话的解码——注意它不会清空历史文本,从而在同一连接内连续输出多句话的结果。

二遍 rescoring:注意力重打分与词级时间戳

对于decoding_method == "attention_rescoring"的 Conformer/Transformer 模型,rescoring()(见 asr_engine.py)执行二遍解码:

  1. searcher.finalize_search()完成最后一帧搜索,取出 beam 假设;
  2. 对每条假设补<sos>/<eos>后经model.forward_attention_decoder(hyps_pad, hyps_lens, encoder_out, reverse_weight)得到注意力解码器分数;
  3. 计算score = decoder_score * (1 - reverse_weight) + r_decoder_score * reverse_weight + ctc_score * ctc_weight,取最高分假设为最终结果;
  4. 依据 viterbi 时间戳计算每个 token 的起止时间(decode_frame_shift_in_sec将解码帧换算为秒,并叠加连续解码的global_offset_in_sec),生成{"w": token, "bg": 开始秒, "ed": 结束秒}列表。

引擎只接受ctc_prefix_beam_searchattention_rescoring两种解码方式,其他方式会在update_config()中被强制改为attention_rescoring(见 asr_engine.py)。

结果输出与服务集成

get_result()返回当前 partial/ending 的 one-best 文本,get_word_time_stamp()返回词级时间戳,二者配合 WebSocket 层的 asr_api.py 即可实现"边说边返回中间结果、句尾返回最终结果与时间戳"的在线交互。从源码结构看,连接处理器的extract_featdecodeget_result/rescoring调用序列,正是服务端在线 ASR 对一次 WebSocket 语音流的完整处理管线。

引擎类本身基于单例BaseEngine(见 base_engine.py),preprocess/run/postprocess对在线场景均抛出NotImplementedError——在线引擎不走传统的"整段输入-整段输出"流水线,而是以连接为粒度由new_handler()生成独立处理器,这也从架构上印证了在线 ASR 与离线 ASR 的本质区别。

小结

PaddleSpeech 在线 ASR 引擎(asr_engine.py)通过"全局引擎 + 连接处理器"的两级设计,把流式特征提取、增量 CTC 解码、端点检测与二遍 rescoring 收敛到一条清晰的调用链上:extract_feat攒帧成块,decode分模型类型做 chunk 解码(DeepSpeech2 走带循环状态的 Inference 预测器,Conformer/Transformer 走forward_chunk增量编码),OnlineCTCEndpoint依据三条静音规则断句,最后rescoring输出带时间戳的最终文本。配合 ws_conformer_application.yaml 中的decode_methodnum_decoding_left_chunkscontinuous_decoding等参数,即可针对不同场景调优在线识别的延迟、精度与断句策略。

  • 人工智能
  • 语音
  • 音频
  • NLP
  • 媒体生成

【免费下载链接】PaddleSpeech

Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword Spotting. Won NAACL2022 Best Demo Award.

项目地址:https://gitcode.com/paddlepaddle/PaddleSpeech
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询