背景与痛点:Linux 语音处理“老毛病”
做语音项目久了,会发现 Linux 虽然稳定,但一碰音频就“矫情”:
- 驱动版本碎片化,ALSA/Pulse/PipeWire 轮番打架,录音设备说没就没
- 依赖链深,PyAudio、PortAudio、ffmpeg 版本对不上,pip 装完系统库崩
- 高并发场景下,实时率 RTF 飙到 1.5,CPU 打满还丢包
- 容器化之后,/dev/snd 权限、uid/gid、Pulse 套接字映射,一不留神就“哑巴”
这些坑逼得人只想“找个能一次跑通、还能横向扩容的方案”。去年做客服质检项目时,团队把阿里、腾讯、DeepSpeech、Wenet 都拉出来跑分,最后留在牌桌上的就是 cosyvoice——纯离线、可商用、Linux First Class。下面把完整落地过程拆给大家,能抄作业就抄,别客气。
技术选型:为什么留下 cosyvoice
先给出当时打分表(满分 5):
| 维度 | cosyvoice | 某云 SDK | DeepSpeech | Wenet |
|---|---|---|---|---|
| 离线可用 | 5 | 1 | 5 | 5 |
| 中文效果 | 4.5 | 4 | 3.5 | 4 |
| 安装复杂度 | 3 | 5 | 2 | 3 |
| 并发吞吐 | 4 | 3 | 2 | 3.5 |
| 社区资料 | 3 | 2 | 4 | 4 |
某云 SDK 胜在“一键包”,但强制走公网,延迟 400 ms+;DeepSpeech 已停止维护;Wenet 效果好,但 GPU 依赖重,边缘节点扛不动。cosyvoice 折中后最均衡:CPU 实时、模型 < 200 MB、支持流式、Apache-2.0 协议可商用。一句话——“能私有化、能横向扩、不挑卡”。
核心实现:30 分钟跑通全流程
1. 最小系统要求
- Ubuntu 20.04+/CentOS 8+
- Python 3.8–3.10(3.11 会触发 onnxruntime 冲突)
- 4 核 CPU / 8 GB RAM 起(16 GB 可稳跑 50 并发)
2. 安装步骤
# 1. 系统依赖 sudo apt update && sudo apt install -y \ portaudio19-dev ffmpeg cmake libatlas-base-dev # 2. 建虚拟环境 python3 -m venv ~/cosyenv source ~/cosyenv/bin/activate # 3. 拉代码+模型(国内镜像快) git clone https://gitee.com/mirrors/cosyvoice.git cd cosyvoice pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 4. 下载预训练模型(≈180 MB) bash scripts/download_model.sh zh3. 目录结构速览
├── cosyvoice/ │ ├── bin/recognize.py # 官方入口 │ └── runtime/ # C++ 解码器 ├── model/ │ ├── zh/ # 中文声学+语言模型 │ └── config.yaml └── scripts/ ├── download_model.sh └── server.py # 我们追加的高并发服务4. 一次最简识别
# recognise_once.py import cosyvoice as cv model = cv.load_model('model/zh') wav = cv.read_wav('demo_16k.wav', sr=16000) text, _ = model.transcribe(wav, language='zh') print('识别结果:', text)跑通后,命令行python recognise_once.py应秒级返回文本。注意音频必须 16 kHz、单通道;不是的话先 ffmpeg:
ffmpeg -i input.mp3 -ac 1 -ar 16000 -sample_fmt s16 input_16k.wav5. 封装成微服务
生产环境得拆成“无状态服务”,方便 k8s 横向扩。下面给一份基于 FastAPI 的完整代码,含异步队列、缓存、日志。
# scripts/server.py import asyncio, uvicorn, cosyvoice as cv from fastapi import FastAPI, File, Form, UploadFile from concurrent.futures import ThreadPoolExecutor app = FastAPI(title="cosyvoice-svc") model = cv.load_model('model/zh') # 启动时一次性加载 pool = ThreadPoolExecutor(max_workers=4) # 绑定 CPU 核数 @app.post("/asr") async def asr(language: str = Form("zh"), audio: UploadFile = File(...)): wav = cv.read_wav(await audio.read(), sr=16000) loop = asyncio.get_event_loop() text, _ = await loop.run_in_executor(pool, model.transcribe, wav, language) return {"text": text} if __name__ == "__main__": uvicorn.run("server:app", host="0.0.0.0", port=8000)把服务打到镜像:
FROM python:3.9-slim COPY cosyvoice /app/cosyvoice WORKDIR /app RUN pip install -r requirements.txt EXPOSE 8000 CMD ["python", "scripts/server.py"]build & run:
docker build -t cosy:1.0 . docker run --rm -p 8000:8000 --device /dev/snd cosy:1.0性能优化:高并发压测与调参
1. 压测脚本
# 50 路并发,每路 10 s 音频 ffmpeg -f lavfi -i "sine=frequency=1000:duration=10" -ar 16000 -ac 1 bench.wav locust -f locustfile.py --host=http://127.0.0.1:8000 -u 50 -r 10 -t 60slocustfile.py 核心:
def trans_task(self): with open('bench.wav', 'rb') as f: self.client.post("/asr", files={"audio": f})2. 关键指标
| 并发 | RTF | CPU 占用 | 内存 | 平均延迟 |
|---|---|---|---|---|
| 10 | 0.31 | 320 % | 2.1 G | 0.35 s |
| 30 | 0.58 | 720 % | 3.8 G | 0.62 s |
| 50 | 1.02 | 1200 % | 5.5 G | 1.10 s |
RTF>1 代表实时率崩了。优化三板斧:
- 线程池 = CPU 核数,防止线程抖动
- 把
transcribe里的 VAD 前置,静音段直接丢弃,减少 30 % 计算量 - onnxruntime 编译带 openmp,加
export OMP_NUM_THREADS=1,避免多实例抢核
调完再测,50 并发 RTF 降到 0.78,延迟 0.85 s,基本可接受。
3. 容器级优化
- 给 Pod 绑核:
resources.limits.cpu: "4"resources.requests.memory: "4Gi" - 镜像里加
ENV OMP_NUM_THREADS=1 - 如果节点有 NUMA,用
taskset -c 0-3把进程钉在同一 NUMA 节点,缓存命中率提升 8 %
避坑指南:生产环境血泪总结
采样率不对直接崩
报错RuntimeError: sr must be 16000——所有入口一定统一转 16 kHz,建议 nginx 前置 lua 脚本自动转码。容器没声音
默认镜像缺失/dev/snd,docker 加--group-add $(getent group audio | cut -d: -f3),k8s 用hostPath + privileged=true。长音频 OOM
30 min 以上录音一次塞进内存会炸。官方流式接口model.transcribe_stream(),按 0.5 s 切片推,内存稳在 1 GB 以下。中文数字转字母
结果里“一二三”→“123”?把config.yaml中convert_number_to_digits=false即可保持原文。并发过高出现“线程泄漏”
ThreadPoolExecutor 未守护,Pod 重启后线程残留。改max_workers=cpu_count()*1,并在on_shutdown回调pool.shutdown(wait=True)。日志把磁盘打满
识别结果每路都写 debug,一天 200 GB。开logging.WARNING,只记录异常段,其余走 ELK 采样。
小结
整套流程下来,cosyvoice 在 Linux 私有化场景里算是“能用、好用、敢用”:安装依赖少、模型轻、协议友好,再配合线程池+流式,边缘节点也能跑到 0.7 实时率。上面代码和压测脚本都已在 GitLab CI 跑通,镜像每日构建,版本锁在 requirements.txt,升级只需改 tag。下步准备把热词 & 语言模型动态替换做成 ConfigMap,实现业务侧零重启更新。希望这篇笔记能帮你少踩几个坑,把语音能力早点搬上线。祝编译一次过,压测永不崩。