本教程系统讲解自动语音识别(ASR)技术,详细介绍开源多语言语音识别模型 SenseVoiceSmall 及其推理框架 FunASR。通过完整示例项目 voice2text,你将学会在本地部署 SenseVoiceSmall 模型,用 FastAPI 构建语音识别后端服务,实现"用户上传音频或用麦克风录音,输出识别文字",并对外暴露 OpenAI 兼容的语音识别接口。

前置教程

如想快速开始学习本教程,你可能需要先完成以下前置教程:

资源下载

1. ASR 技术概览

1.1 什么是 ASR

ASR(Automatic Speech Recognition,自动语音识别) 是指将人类的语音音频自动转换为对应文字文本的技术,是人工智能语音领域的核心基础能力之一,与 TTS(文本转语音)恰好互为反向任务。

ASR 的应用场景非常广泛:

  • 语音输入:手机输入法、语音笔记、会议纪要的自动转写
  • 智能交互:语音助手、智能音箱、客服系统的语音理解前端
  • 字幕生成:视频、直播、网课的自动字幕
  • 语音翻译:先识别再翻译,构成语音翻译链路
  • 无障碍服务:为听障人群提供实时字幕

现代 ASR 系统的核心处理流程如下:

graph LR A[语音音频] --> B[音频预处理
采样率统一、降噪] B --> C[声学特征提取
如梅尔频谱] C --> D[声学模型与解码
音频映射为文字] D --> E[输出文本] classDef input fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px classDef process fill:#d5f5e3,stroke:#1e8449,stroke-width:2px classDef model fill:#ffecd6,stroke:#ca6f1e,stroke-width:2px classDef output fill:#ebdef0,stroke:#6c3483,stroke-width:2px class A input class B process class C,D model class E output
阶段 作用 说明
音频预处理 统一采样率、静音切除 保证输入质量一致
声学特征提取 将音频波形转为特征向量 常用梅尔频谱、特征滤波器组
声学模型与解码 将声学特征映射为文字序列 决定识别准确率与语种支持

ASR 的本质是"音频到文字"的映射,工程上的重点是选择合适的识别模型,并把它封装成易于调用的服务。

1.2 在线 ASR 与本地部署 ASR 的对比

调用 ASR 有两种主流方式:调用在线 ASR 服务本地部署 ASR 模型。两者的对比如下:

对比项 在线 ASR 服务 本地部署 ASR(SenseVoiceSmall)
联网要求 需要联网调用云端服务 无需联网,完全离线可用
调用成本 按量计费或订阅 模型开源免费,需自备硬件
数据隐私 音频上传至云端 数据不出本地,隐私可控
部署门槛 低,注册即可调用 较高,需要配置深度学习环境
语种支持 视服务商而定 支持中英粤日韩等 50+ 语言
识别速度 受网络影响 极低延迟,10 秒音频约 70ms 推理

1.3 本教程的选型

  • SenseVoiceSmall:开源多语言音频理解模型,由阿里通义实验室(FunAudioLLM)开源,支持多语言语音识别、情感识别与音频事件检测,推理延迟极低。
  • FunASR:阿里巴巴开源的语音识别工具包,负责加载与推理 SenseVoice 等模型,提供统一、简洁的调用接口。

选择建议:需要离线、隐私可控、低延迟的场景选择本地部署 SenseVoiceSmall;快速原型或语音量小的场景可选用在线服务。本教程的示例项目将本地模型封装为 OpenAI 兼容接口,便于与既有系统集成。

2. SenseVoiceSmall 模型详解

2.1 模型简介

SenseVoiceSmall 是由阿里通义实验室开源的多语言音频理解基础模型,采用非自回归端到端框架,参数量与 Whisper-Small 相当,但推理速度比 Whisper-Small 快 5 倍、比 Whisper-Large 快 15 倍,10 秒音频推理仅需约 70ms。

模型采用超过 40 万小时数据训练,支持中、粤、英、日、韩五语种的高精度语音识别。

graph LR A[语音音频] --> B[语音编码器
SenseVoiceEncoderSmall] B --> C[非自回归解码
并行输出文字序列] C --> D[富文本结果
含情感/事件标签] classDef input fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px classDef model fill:#ffecd6,stroke:#ca6f1e,stroke-width:2px classDef process fill:#d5f5e3,stroke:#1e8449,stroke-width:2px classDef output fill:#ebdef0,stroke:#6c3483,stroke-width:2px class A input class B,C model class D output

2.2 核心能力

  • 多语言识别:支持中文、粤语、英语、日语、韩语,识别效果优于 Whisper 模型
  • 富文本识别:输出不仅包含文字,还包含情感标签(如 <|HAPPY|>)与音频事件标签(如 <|BGM|><|Applause|>
  • 情感识别:可辨识高兴、悲伤、生气、害怕等多种情绪
  • 事件检测:可检测音乐、掌声、笑声、哭声、咳嗽、喷嚏等常见人机交互事件
  • 逆文本正则化:可将口语化的"20度C"自动恢复为"20°C"等规范写法
  • 低延迟推理:非自回归架构,推理耗时不随音频时长明显增加

2.3 本地模型目录

本教程假设 SenseVoiceSmall 模型已放置在 C:\models\SenseVoiceSmall,目录中应包含以下文件:

文件 作用
config.yaml 模型配置(编码器结构、特征参数等)
model.pt 模型权重文件
am.mvn 声学特征均值方差归一化参数
chn_jpn_yue_eng_ko_spectok.bpe.model 多语言文本词表
demo.py 官方推理示例脚本
example/ 官方示例音频(含中英粤日韩五语)

本地模型目录示意图

2.4 使用 FunASR 调用

SenseVoiceSmall 使用 FunASR 框架推理。核心代码如下:

from funasr import AutoModel
from funasr.utils.postprocess_utils import rich_transcription_postprocess

# 加载本地模型,可选用 VAD 切分长音频
model = AutoModel(
    model="C:/models/SenseVoiceSmall",
    device="cuda",                     # 无 GPU 时改为 "cpu"
    vad_model="fsmn-vad",              # VAD 用于长音频切分
    vad_kwargs={"max_single_segment_time": 30000},
)

res = model.generate(
    input="audio.mp3",                 # 任意格式音频
    cache={},
    language="auto",                   # auto / zh / en / yue / ja / ko
    use_itn=True,                      # 是否包含标点与逆文本正则化
    batch_size_s=60,                   # 动态 batch 总音频时长(秒)
    merge_vad=True,                    # 合并 VAD 切分的短音频
    merge_length_s=15,
)
text = rich_transcription_postprocess(res[0]["text"])
print(text)

AutoModel 可以直接传入本地模型目录;generate() 返回的原始结果包含语言、情感、事件等富文本标签,使用 rich_transcription_postprocess() 处理后即可得到纯文本。首次启用 VAD 会联网下载 fsmn-vad 模型,仅需一次。

3. 环境准备

3.1 创建环境

ASR 使用独立的 conda 环境 voice2text,依赖统一使用 uv 安装:

# 创建并激活 conda 环境
conda create -n voice2text python=3.10 -y
conda activate voice2text

# 安装 lzma 运行库(conda 的 Python 可能缺少 _lzma 模块,避免后续报错)
conda install -c conda-forge liblzma -y

# 安装 uv(Python 包管理工具)
pip install uv

3.2 安装依赖

先安装 CUDA 版 PyTorch,国内推荐从阿里云镜像直链下载 cu128 的 wheel(速度快);网络好的读者也可改用官方源。注意先装 torch 再装 funasr,避免 funasr 把 torch 解析成 CPU 版:

# 方法一:阿里云镜像直链(国内推荐)
uv pip install \
  "https://mirrors.aliyun.com/pytorch-wheels/cu128/torch-2.8.0%2Bcu128-cp310-cp310-win_amd64.whl" \
  "https://mirrors.aliyun.com/pytorch-wheels/cu128/torchvision-0.23.0%2Bcu128-cp310-cp310-win_amd64.whl" \
  "https://mirrors.aliyun.com/pytorch-wheels/cu128/torchaudio-2.8.0%2Bcu128-cp310-cp310-win_amd64.whl"

# 方法二:PyTorch 官方源
uv pip install torch==2.8.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu128

# 安装 funasr 与服务依赖(含 fastapi、uvicorn、soundfile 等)
cd voice2text
uv pip install -r requirements.txt --index-url https://pypi.tuna.tsinghua.edu.cn/simple

提示:无 GPU 时跳过 CUDA torch 安装即可,funasr 会使用 CPU 版 torch 推理,识别速度较慢但可运行。mp3/m4a 等格式由 torchaudio 内置的 FFmpeg 解码,无需单独安装 ffmpeg。

3.3 下载模型权重

SenseVoiceSmall 模型可通过 ModelScope 或网盘下载。国内用户推荐使用 ModelScope:

# 使用 ModelScope 下载(推荐国内用户)
modelscope download --model iic/SenseVoiceSmall --local_dir C:/models/SenseVoiceSmall

也可以从本教程资源下载区块的网盘地址直接下载模型压缩包,解压后将文件放到 C:\models\SenseVoiceSmall 目录即可。

4. 综合示例:voice2text 语音识别服务

4.1 项目目标与架构

示例项目 voice2text 将 SenseVoiceSmall 封装为一个完整的语音识别服务,实现以下目标:

  • 用户上传一段音频,或用麦克风录音,服务识别并输出文字
  • 对外暴露 OpenAI 兼容的 POST /v1/audio/transcriptions 接口
  • 构建前端页面,支持文件上传与浏览器内麦克风录音

整体架构如下:

graph LR U[前端页面
static/index.html] -->|上传音频 / 录音 WAV| A[FastAPI 后端
main.py] A --> S[asr_service.py
FunASR 封装] S --> M[SenseVoiceSmall 模型
C:/models/SenseVoiceSmall] S -->|识别文字| U classDef frontend fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px classDef backend fill:#d5f5e3,stroke:#1e8449,stroke-width:2px classDef engine fill:#ebdef0,stroke:#6c3483,stroke-width:2px classDef model fill:#ffecd6,stroke:#ca6f1e,stroke-width:2px class U frontend class A backend class S engine class M model

4.2 项目结构

voice2text/
├── config.py                  # 服务配置(端口、模型路径、设备)
├── main.py                    # FastAPI 后端,识别接口
├── asr_service.py             # ASR 识别服务(FunASR 封装)
├── requirements.txt           # 依赖管理
├── static/
│   └── index.html             # 前端页面(上传 + 录音)
└── README.md                  # 项目说明

4.3 配置文件 config.py

所有配置统一在 config.py 中修改,包括监听端口、模型路径、推理设备、识别语言等:

# config.py(核心片段,完整代码请查看 voice2text/config.py)
PORT = 8000

# SenseVoiceSmall 模型目录(需包含 config.yaml 与 model.pt 等文件)
ASR_MODEL_DIR = "C:/models/SenseVoiceSmall"

# 推理设备:cuda / cpu
ASR_DEVICE = "cuda"

# 识别语言:auto / zh / en / yue / ja / ko
ASR_LANGUAGE = "auto"

# 是否启用 VAD 长音频切分(首次使用会联网下载 fsmn-vad 模型)
ASR_USE_VAD = True

# 输出是否包含标点与逆文本正则化
ASR_USE_ITN = True

4.4 ASR 识别服务

asr_service.py 封装了 FunASR 的调用逻辑。考虑到模型加载耗时,采用懒加载方式在首次请求时才加载模型;同时 FunASR 模型非线程安全,使用全局锁串行化识别请求:

# asr_service.py(核心片段,完整代码请查看 voice2text/asr_service.py)
def _get_model():
    """懒加载 FunASR 模型(首次调用时加载)"""
    global _model
    if _model is None:
        from funasr import AutoModel
        kwargs = {"model": ASR_MODEL_DIR, "device": ASR_DEVICE}
        if ASR_USE_VAD:
            kwargs["vad_model"] = "fsmn-vad"
            kwargs["vad_kwargs"] = {"max_single_segment_time": 30000}
        _model = AutoModel(**kwargs)
    return _model

def transcribe(audio_path, language=None):
    """将音频文件识别为文字,返回去除富文本标签的转写结果"""
    model = _get_model()
    with _recog_lock:                    # 串行化识别请求
        res = model.generate(
            input=audio_path, cache={},
            language=language or ASR_LANGUAGE,
            use_itn=ASR_USE_ITN,
            batch_size_s=60, merge_vad=True, merge_length_s=15,
        )
    return rich_transcription_postprocess(res[0]["text"])

4.5 后端接口

main.py 是服务的核心入口,提供两个识别接口:自定义的 /api/transcribe 与 OpenAI 兼容的 /v1/audio/transcriptions

# main.py(核心片段,完整代码请查看 voice2text/main.py)
@app.post("/api/transcribe")
async def transcribe(file: UploadFile = File(...)):
    """自定义识别接口:上传音频文件,返回识别文字"""
    audio_path = _save_audio(file)               # 保存为临时文件
    try:
        text = asr_service.transcribe(audio_path)
    finally:
        os.unlink(audio_path)                    # 识别后清理临时文件
    return {"text": text}

@app.post("/v1/audio/transcriptions")
async def transcriptions(
    file: UploadFile = File(...),
    model: str = Form("SenseVoiceSmall"),
    language: str = Form("auto"),
):
    """OpenAI 兼容的语音识别接口"""
    audio_path = _save_audio(file)
    try:
        text = asr_service.transcribe(audio_path, language=language or None)
    finally:
        os.unlink(audio_path)
    return {"text": text}                        # OpenAI 兼容返回格式

代码要点说明:

功能模块 实现方式 说明
音频接收 UploadFile(multipart) 前端通过表单上传音频文件
临时文件 tempfile.NamedTemporaryFile 识别结束后清理,不落盘
自定义接口 POST /api/transcribe 前端使用的简单接口
兼容接口 POST /v1/audio/transcriptions 与 OpenAI 规范一致,返回 {"text": ...}

4.6 前端页面

前端为单页 HTML 应用,位于 static/index.html。你可以前往 voice2text/static/index.html 查看完整的示例代码。页面主要功能:

  1. 上传音频:选择本地音频文件(wav/mp3/m4a/flac 等)作为识别输入
  2. 麦克风录音:调用浏览器麦克风录音,通过 Web Audio API 实时采集并编码为 16kHz WAV
  3. 音频预览:识别前可播放已选音频或录音
  4. 识别展示:点击"开始识别",通过 fetch 以 multipart 表单上传音频,展示返回的文字

麦克风录音采用 Web Audio API 采集原始 PCM 并编码为 WAV,不依赖 WebM/Opus,因此无需 ffmpeg 即可在服务端解码。

4.7 运行与测试

第一步:创建环境并安装依赖

若已按第 3 章完成环境准备(voice2text 环境已创建),直接激活即可;否则先按第 3 章创建环境并安装依赖。

conda activate voice2text
cd voice2text

# 安装服务依赖(若第 3 章已安装可跳过)
uv pip install -r requirements.txt --index-url https://pypi.tuna.tsinghua.edu.cn/simple

第二步:确认模型位置

确保模型位于 C:\models\SenseVoiceSmall(第 3.3 节),如需自定义路径,修改 config.py 中的 ASR_MODEL_DIR

第三步:启动服务

python main.py

启动后访问 http://localhost:8000,即可看到语音识别界面。

第四步:验证功能

  • 在页面中上传一段音频或点击录音,点击"开始识别",确认能正确输出文字
  • 也可使用 curl 直接验证接口:
curl.exe -X POST http://localhost:8000/api/transcribe -F "file=@C:/path/to/audio.wav"

安装服务依赖(若第 3 章已安装可跳过)示意图

4.8 使用 OpenAI SDK 验证接口兼容性

由于服务暴露了 OpenAI 兼容接口,可以直接使用 openai 官方 Python SDK 调用:

# 使用 openai SDK 调用本地 voice2text 服务
from openai import OpenAI

client = OpenAI(
    base_url="http://127.0.0.1:8000/v1",  # 本地服务地址
    api_key="not-needed",                 # 本地服务不校验 Key
)

with open("audio.wav", "rb") as f:
    result = client.audio.transcriptions.create(
        model="SenseVoiceSmall",
        file=f,
        language="auto",
    )
print(result.text)

5. 总结

5.1 核心内容回顾

  • ASR 技术:语音转文字,核心流程为音频预处理、声学特征提取、声学模型与解码
  • SenseVoiceSmall:开源多语言音频理解模型,支持中英粤日韩识别、情感识别、事件检测,低延迟高精度
  • FunASR 推理AutoModel 加载本地模型,generate() 识别,rich_transcription_postprocess 清理富文本
  • 服务封装:FastAPI 实现 /api/transcribe 与 OpenAI 兼容的 /v1/audio/transcriptions 接口
  • 前端应用:支持上传音频与麦克风录音,录音通过 Web Audio 编码为 WAV

5.2 常见问题与解答

问:首次识别时为什么会联网?

答:启用 VAD 时,首次运行会联网下载 fsmn-vad 模型(仅需一次,之后使用本地缓存)。关闭 VAD(ASR_USE_VAD = False)可完全离线,但长音频的切分效果会变差。

问:识别结果包含奇怪符号(如 <|HAPPY|>)?

答:这是 SenseVoice 的富文本标签(情感、事件、语种)。示例代码已使用 rich_transcription_postprocess() 清理,只保留纯文本。

问:麦克风录音后识别为空或失败?

答:确认浏览器允许麦克风权限;录音时长过短可能识别为空,建议录制 1 秒以上。也可改用上传音频方式测试。

问:无 GPU 如何运行?

答:跳过 CUDA torch 安装,并将 config.pyASR_DEVICE 改为 "cpu"。SenseVoiceSmall 参数量小,CPU 推理也可接受。

问:上传 mp3/m4a 无法识别?

答:torchaudio 内置 FFmpeg 支持常见音频格式。若仍失败,可先转码为 wav 再上传,或确认 torchaudio 版本与后端正常。