一、问题从会议现场开始
在政府机关和党政办公场景中,会议记录有一个比较明显的特点:不能等会议结束后再慢慢处理。
例如,党组会议、专题研究会、部门协调会等场景,往往需要在会议进行过程中同步查看发言内容。对于离线会议系统来说,实时转写不仅是一个功能展示问题,也直接影响后续的会议纪要生成和内容检索。
项目适配信创平台时,原有 Paraformer 流式识别方案出现了一个问题:音频能够正常采集,模型也能够返回文字,但文字输出明显滞后。
最开始尝试调整音频缓存和推理线程,效果并不稳定。进一步分析后发现,不能将问题简单归结为 CPU 性能不足,还需要重新检查模型推理方式、分块策略和流式状态管理。
因此,本次技术调整的重点放在 Zipformer 流式识别架构上。
二、先把实时延迟拆开
很多语音项目在做性能优化时,容易只记录模型推理时间。
但从用户实际体验来看,会议现场说出一句话,到屏幕上显示对应文字,中间涉及多个环节。
可以将延迟拆成:

其中:
| 环节 | 具体内容 |
|---|---|
| T1 | 音频采集 |
| T2 | 缓存等待 |
| T3 | 特征提取 |
| T4 | 模型推理 |
| T5 | 解码和前端展示 |
举个例子,假设音频采集和传输没有明显问题,但系统需要积累较长时间的音频才开始执行推理,那么即使模型本身运行速度较快,最终显示文字仍然会慢。
因此,优化之前需要分别记录各个环节的耗时,而不是直接通过增加线程数解决所有问题。
三、Zipformer 流式识别的实现思路
Zipformer 可以结合 Transducer 架构实现流式语音识别。
其核心并不是简单地把完整音频切成多个小文件,而是在持续输入音频的过程中,利用模型的状态信息完成连续解码。
1. 模型结构
典型 Zipformer Transducer 包含三个主要部分:
Encoder:
负责提取音频特征,将输入的声学信息转换为高层表示。
Prediction Network:
根据之前已经输出的文本 Token,预测后续可能出现的内容。
Joiner:
将 Encoder 输出和 Prediction Network 输出进行融合,计算下一个 Token 的概率。
整体处理关系如下:

与需要等待较长上下文的处理方式相比,流式 Transducer 可以利用历史状态持续处理新输入的音频。
但需要注意,实际延迟还受到模型右侧上下文、特征帧移、分块配置和解码策略影响。
2. 为什么需要缓存?
流式模型每次收到新的音频数据,并不意味着都要从头处理之前的所有内容。
合理的状态缓存能够减少重复计算。
例如:
第一批音频:
Audio_1 → Encoder → Cache_1 → Result_1
第二批音频:
Audio_2 + Cache_1 → Encoder → Cache_2 → Result_2
第三批音频:
Audio_3 + Cache_2 → Encoder → Cache_3 → Result_3
这里的 Cache 不只是普通的音频缓存,还可能包含模型内部的历史状态。
具体缓存结构由模型实现决定,不能在不同模型之间直接复用。
如果每次都重新计算完整历史音频,不仅会增加计算量,还会让处理时间随着会议持续时间增长。
四、实现方案:分块采集与持续解码
本次优化建议采用以下结构:

这里将音频采集和模型推理解耦,主要是为了避免模型推理耗时反过来阻塞音频采集。
1. 音频分块处理
建议使用固定长度的 PCM 音频块作为采集单位。
例如:
import numpy as np
import queue
audio_queue = queue.Queue(maxsize=100)
SAMPLE_RATE = 16000
CHUNK_MS = 100
CHUNK_SIZE = SAMPLE_RATE * CHUNK_MS // 1000
def audio_callback(pcm_data):
"""
pcm_data: 采集线程获取的PCM数据
"""
samples = np.frombuffer(
pcm_data,
dtype=np.int16
).astype(np.float32) / 32768.0
if not audio_queue.full():
audio_queue.put(samples)
else:
# 实际项目中应记录丢帧并触发告警
print("audio queue is full")
这里使用 100ms 作为采集块示例,并不是说模型必须每 100ms 完成一次解码。
采集块大小、模型内部帧移和解码触发条件应分别配置。
另外,实际生产环境中不建议简单丢弃满队列后的音频数据。应结合缓冲区水位、处理能力和会议录音完整性制定背压策略。
2. 持续输入模型
以 sherpa-onnx 的在线识别接口为例,可以将音频持续送入同一个 Stream:
import sherpa_onnx
recognizer = sherpa_onnx.OnlineRecognizer.from_transducer(
encoder="encoder.onnx",
decoder="decoder.onnx",
joiner="joiner.onnx",
tokens="tokens.txt",
num_threads=2,
sample_rate=16000,
feature_dim=80,
decoding_method="greedy_search",
provider="cpu"
)
stream = recognizer.create_stream()
def process_audio_chunk(chunk):
recognizer.accept_waveform(
stream,
16000,
chunk
)
while recognizer.is_ready(stream):
recognizer.decode_stream(stream)
return recognizer.get_result(stream)
上述为接口逻辑示意,具体方法签名应根据安装版本调整。
生产环境还需要补充音频结束通知、结果读取、异常恢复和流对象生命周期管理。
尤其要注意,不能每收到一个音频块就重新创建 Stream,否则模型历史状态无法连续保留。
3. 临时结果和最终结果分离
在会议现场,用户看到的识别文本可能会不断变化。
例如:
第一次:
今天我们召开一个专题会议
第二次:
今天我们召开一个专题会议,主要研究
第三次:
今天我们召开一个专题会议,主要研究下一阶段的工作安排。
如果每次都把返回结果直接追加到数据库,就容易出现文字重复。
因此建议维护两类文本:
class MeetingText:
def __init__(self):
self.partial_text = ""
self.final_text = []
def update_partial(self, text):
self.partial_text = text
def append_final(self, text):
if text:
self.final_text.append(text)
self.partial_text = ""
实际系统还需要根据解码器返回的句子边界、稳定性标记等信息更新状态,不能仅依靠字符串拼接判断句子是否完成。
五、针对信创平台的性能优化
模型完成接入后,还需要结合实际硬件进行性能验证。
1. 控制线程数量
线程并不是越多越好。
在信创 CPU 平台上,如果推理线程数量过多,可能出现线程竞争、上下文切换增加和内存带宽争用。
建议分别测试:
-
单线程;
-
双线程;
-
四线程;
-
多路并发情况下的线程分配。
同时记录 CPU 使用率和 P95 推理延迟。
2. 检查推理后端
如果使用 ONNX 模型,需要确认目标平台的 ONNX Runtime 版本及算子支持情况。
重点检查:
-
模型是否能够完整加载;
-
是否存在不支持的算子;
-
是否出现 CPU 与其他执行后端之间的意外切换;
-
是否发生重复内存拷贝;
-
模型加载后内存占用是否稳定。
如果目标平台具备专用推理加速能力,还需要使用相应厂商提供的运行库和适配方案进行验证。
3. 优化音频缓存
缓存长度直接影响用户体验。
缓存过长会增加等待时间,缓存过短可能造成处理开销增加。
因此建议通过测试确定合适的采集块、解码块及队列容量,而不是简单地将所有缓存设置成最小值。
4. 建立实时率监控
可以使用以下方法记录处理性能:
import time
def benchmark(process_func, audio_chunk):
start = time.perf_counter()
process_func(audio_chunk)
cost = time.perf_counter() - start
print("processing cost:", round(cost, 4), "seconds")
正式测试时,应分别统计:
-
首字延迟;
-
分块处理耗时;
-
端到端延迟;
-
P50、P95和P99延迟;
-
实时率RTF;
-
长时间运行内存变化。
不能只测一次,也不能只使用提前准备好的短录音。
六、政府机关离线会议场景的工程要求
模型切换完成后,系统还需要满足政务内网的运行要求。
第一,模型文件和推理依赖应提前准备,部署时不依赖外部网络。
第二,会议录音、转写文本和会议纪要应按照业务权限进行管理。
第三,系统应支持异常恢复,不能因为单次模型解码失败导致整场会议录音丢失。
第四,语音识别结果应保留必要的时间信息,方便后续核对原始录音。
第五,后续接入声纹识别、会议纪要及知识库时,应尽量通过统一的文本数据接口衔接,避免各模块重复处理原始音频。
对于类似熙瑾会悟的离线会议产品,这种分层设计也有助于后续适配不同国产化操作系统和硬件平台。
七、总结
这次流式识别优化,核心并不是简单地把 Paraformer 换成 Zipformer,而是重新考虑模型架构、音频缓存、在线解码和运行环境之间的关系。
Zipformer 流式 Transducer 为持续音频输入提供了另一种技术实现路线,但最终能否降低信创平台上的实时延迟,仍需要通过目标设备上的实际测试确认。
对于政府机关和党政办公场景,真正可用的智能语音系统,不仅要识别准确,还需要做到持续运行、及时反馈、数据可控和结果可追溯。
只有将模型优化与工程部署结合起来,才能让离线语音识别真正融入日常办公流程。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/HySpark/article/details/170144022




