实时ASR的几个大坑
1、实时ASR就没有准确的,只要是流式的,就准确不了,基本上都是一边说一边改,你要是急着往LLM里面塞,LLM必然混乱。
2、目前实时最好的办法依然是VAD+ASR
3、Onnx比PT快的不是一星半点,但是modelscope上那个玩意iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx在我的机器上就是跑不了,输出是乱码。我怀疑和numpy的版本有关系,哪个numpy导出的,推理也必须用这个numpy版本。
4、不要相信网络API的stream模式,哪怕是Qwen的付费API,效果都不咋地。这不难理解,chunk的上下文不足,处理不好就容易崩。
5、不要害怕参数,ASR这个级别全文预算速度非常快,但是不要用.pt的版本,一定要转onnx来运行,效果会好非常多。用pt的情况下,CPU是2s音频7s推理,慢的和乌龟一样。用onnx,1s转录大概只需要0.25秒。
6、做实时你必须考虑用户的感受,你这边还在BBBBB的说话,那边你就送ASR是没有意义的,LLM又没有实时推理功能。这点理论上是可以做的,因为LLM的确是因果推理。理论上可以一边输入文字,一边处理。但我还不知道谁能这么玩的。
给一个onnx的asr推理案例,这玩意funasr自己的github上没有,在funasr-onnx的pypi里面有。
首先得把funasr从torch转为onnx
# modelscope download --model iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch --local_dir ./paraformer
# 命令行先执行这个指令,把paraformer的pt文件拉到本地。
from funasr import AutoModel
ASR_MODEL_PATH = "./paraformer"
model_asr = AutoModel(model=ASR_MODEL_PATH, disable_update=True)
export_dir = model_asr.export(quantize=False)
print(f"ONNX model exported to: {export_dir}")
#onnx会保存在原来的文件夹下,这里也打印了,自己看一下就行
然后开始推理
from funasr_onnx import Paraformer
import wave
import sys
import soundfile as sf
import logging
import numpy as np
import time
import re
def check_wav_info(wav_path):
"""检查 WAV 文件信息"""
flag=False
try:
with wave.open(wav_path, 'rb') as wf:
n_channels = wf.getnchannels()
sample_width = wf.getsampwidth()
framerate = wf.getframerate()
n_frames = wf.getnframes()
duration = n_frames / framerate
print(f"WAV 文件:{wav_path}")
print(f" 声道数:{n_channels}")
print(f" 采样位深:{sample_width} 字节 ({sample_width * 8}-bit)")
print(f" 采样率:{framerate} Hz")
print(f" 总帧数:{n_frames}")
print(f" 总时长:{duration:.2f} 秒")
print(f" 文件大小:{n_channels * sample_width * n_frames} 字节 (音频数据)")
# 检查是否符合 ASR 模型要求
print("\n格式检查:")
if n_channels != 1:
print(f" ❌ 错误:需要单声道,当前为 {n_channels} 声道")
else:
print(f" ✓ 声道数正确(单声道)")
if framerate != 16000:
print(f" ❌ 错误:需要 16000Hz 采样率,当前为 {framerate}Hz")
else:
print(f" ✓ 采样率正确(16000Hz)")
if sample_width != 2:
print(f" ❌ 错误:需要 16-bit (2 字节),当前为 {sample_width * 8}-bit")
else:
print(f" ✓ 位深正确(16-bit)")
return True
except FileNotFoundError:
print(f"错误:文件不存在:{wav_path}")
return flag
except Exception as e:
print(f"错误:{e}")
return flag
def clean_asr_text(text):
# 1. 把中文和中文之间的空格去掉
text = re.sub(r'(?<=[\u4e00-\u9fa5])\s+(?=[\u4e00-\u9fa5])', '', text)
# 2. 把中文和英文、英文和中文之间的空格也去掉
text = re.sub(r'(?<=[\u4e00-\u9fa5])\s+(?=[a-zA-Z])', '', text)
text = re.sub(r'(?<=[a-zA-Z])\s+(?=[\u4e00-\u9fa5])', '', text)
return text
if __name__ == "__main__":
asr_model_dir = "./paraformer
#此处放你的asr模型地址,可以用modelscope去下载。
# 这里要提前转换为onnx格式,代码在上面
asr_model = Paraformer(asr_model_dir, batch_size=1, quantize=False)
wav_path = "./test.wav"
flag=check_wav_info(wav_path)
if flag:
start_time = time.perf_counter()
result = asr_model(wav_path)
end_time = time.perf_counter()
print(f"识别耗时:{end_time - start_time:.2f} 秒")
raw_text = result[0]['preds']
cleaned_text = clean_asr_text(raw_text)
print(cleaned_text)
WAV 文件:D:\Python_Project\recorded_sentences\sentence_0002_121128.wav
声道数:1
采样位深:2 字节 (16-bit)
采样率:16000 Hz
总帧数:31680
总时长:1.98 秒
文件大小:63360 字节 (音频数据)格式检查:
✓ 声道数正确(单声道)
✓ 采样率正确(16000Hz)
✓ 位深正确(16-bit)
识别耗时:0.16 秒
你在干什么你跟我说说话
更多推荐
所有评论(0)