- 人工智能
- 语音
- 音频
- 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.
本文围绕 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 引擎实现,与仓库中的onnx、paddleinference变体同属一个功能族(见 paddlespeech/server/engine/asr/online 目录)。该模块对外导出三个核心类:
| 类 | 职责 | 生命周期 |
|---|---|---|
ASREngine | 全局引擎,持有服务配置与执行器,负责设备初始化与模型装载 | 进程级(继承单例BaseEngine) |
ASRServerExecutor | 模型资源装载器,继承 CLI 的ASRExecutor,负责下载/加载模型、构建文本特征器 | 引擎级,全局唯一 |
PaddleASRConnectionHanddler | 每个 WebSocket 连接一个的处理器,维护该路音频流的状态(缓存、解码进度、结果) | 连接级,随连接创建与销毁 |
三层分工非常清晰:ASREngine只做一次性的资源初始化,ASRServerExecutor完成模型加载与配置合并,而真正"边收音频边解码"的实时逻辑全部封装在PaddleASRConnectionHanddler中——这是理解整个在线 ASR 服务的关键入口。
从源码结构看,ASREngine.init(config)会依次完成:读取服务配置 → 创建ASRServerExecutor→ 设置设备(支持cpu或gpu: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_model、am_params、cfg_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_conf含graph_optimization_level、intra_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)从全局引擎上继承了配置、模型类型、采样率与文本特征器,并完成三件关键初始化:
- 特征提取器:从模型配置的
preprocess_config构建Transformation,并读取win_length(帧长)与n_shift(帧移,单位均为采样点),换算得到frame_shift_in_ms——这是后续端点检测的时间基准。 - 解码器分发:
init_decoder()根据model_type分流——deepspeech2构建CTCDecoder并绑定 Paddle Inference 预测器;conformer/transformer则构建CTCPrefixBeamSearch搜索器与OnlineCTCEndpoint端点检测器(见 asr_engine.py)。 - 状态复位:
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),处理流程为:
- 用
np.frombuffer(samples, dtype=np.int16)将字节流还原为 16 位 PCM 采样点,累加全局采样计数; - 新音频与上次剩余
remained_wav拼接(保证不丢帧); - 若不足一帧
win_length则直接返回 0,等待更多数据; - 对
remained_wav做 FBank 预处理得到x_chunk,转为(1, T, D)张量并按时间轴拼入cached_feat; - 更新帧计数
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=1、context=7、subsampling=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_hyps、abs_time_step在reset()前一直保留,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_nonsilence | min_trailing_silence | min_utterance_length | 含义 |
|---|---|---|---|---|
| rule1 | False | 5000 ms | 0 | 即使什么都没解码,静音 5 秒也超时 |
| rule2 | True | 1000 ms | 0 | 解码出内容后再静音 1 秒即断句 |
| rule3 | False | 0 | 20000 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)执行二遍解码:
searcher.finalize_search()完成最后一帧搜索,取出 beam 假设;- 对每条假设补
<sos>/<eos>后经model.forward_attention_decoder(hyps_pad, hyps_lens, encoder_out, reverse_weight)得到注意力解码器分数; - 计算
score = decoder_score * (1 - reverse_weight) + r_decoder_score * reverse_weight + ctc_score * ctc_weight,取最高分假设为最终结果; - 依据 viterbi 时间戳计算每个 token 的起止时间(
decode_frame_shift_in_sec将解码帧换算为秒,并叠加连续解码的global_offset_in_sec),生成{"w": token, "bg": 开始秒, "ed": 结束秒}列表。
引擎只接受ctc_prefix_beam_search与attention_rescoring两种解码方式,其他方式会在update_config()中被强制改为attention_rescoring(见 asr_engine.py)。
结果输出与服务集成
get_result()返回当前 partial/ending 的 one-best 文本,get_word_time_stamp()返回词级时间戳,二者配合 WebSocket 层的 asr_api.py 即可实现"边说边返回中间结果、句尾返回最终结果与时间戳"的在线交互。从源码结构看,连接处理器的extract_feat→decode→get_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_method、num_decoding_left_chunks、continuous_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.
相关推荐
PaddleSpeech 在线 ASR ONNX 引擎深度解析:流式语音识别服务核心模块
PaddleSpeech 在线 ASR ONNX 引擎深度解析:流式语音识别服务核心模块 导读 本文以 PaddleSpeech 官方 API 文档中 padd
人工智能语音音频PaddleSpeech 在线 ASR ONNX 引擎深度解析:基于 onnxruntime 的流式语音识别服务端实现
PaddleSpeech 在线 ASR ONNX 引擎深度解析:基于 onnxruntime 的流式语音识别服务端实现 本文围绕 PaddleSpeech 服务
人工智能语音音频NLP媒体生成PaddleSpeech 在线 ASR 引擎 Python 实现:asr_engine 模块 API 与流式解码源码解析
PaddleSpeech 在线 ASR 引擎 Python 实现:asr_engine 模块 API 与流式解码源码解析 导读 本文以 PaddleSpeech
人工智能语音音频
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考