【语音相关ASR】FunASR 离线语音识别与FunASR热词优化

FunASR 离线语音识别与FunASR热词优化

FunASR 是阿里巴巴达摩院开源的语音识别工具库,提供了多种模型和丰富的参数配置,支持离线和实时语音识别。


在查阅 FunASR 热词功能相关资料时,发现网络上关于 FunASR 热词识别 的完整代码示例非常少,大部分文章只介绍了热词格式,却缺少:

  1. 完整的可运行代码:没有模型加载、音频处理的完整流程
  2. 热词模型选择:未说明只有特定模型支持热词优化
  3. 实际效果对比:缺少热词添加前后的识别效果对比测试

本文将从 FunASR 的基础功能出发,重点介绍 热词识别功能 的完整实现,并提供所有可运行的代码示例,帮助读者快速上手 FunASR 热词优化。


一、FunASR 支持的音频输入格式

FunASR 的 generate() 方法支持多种音频输入格式,非常灵活:

1. 基础格式

格式 示例 说明
单文件路径 "audio.wav" 字符串路径,最常用
多文件路径 ["a.wav", "b.wav"] 列表形式,批量处理
URL "http://xxx/audio.mp3" 网络地址,自动下载

2. 数据格式

格式 示例 说明
Numpy 数组 np.array([...]) 已加载的音频数据,需 float32 类型
原始字节流 open("a.wav", "rb").read() 二进制数据(含文件头)
Base64 解码 base64.b64decode(str) 需先解码为字节流

3. 支持的音频文件格式

  • .wav - 最推荐,无压缩
  • .mp3 - 支持,需 ffmpeg
  • .flac - 支持
  • .ogg / .opus - 支持
  • .m4a - 支持

4. 音频格式要求

  • 采样率:推荐 16kHz(模型默认)
  • 声道:单声道最佳,多声道会自动转换
  • Numpy 输入:必须是 float32 类型,值域 [-1, 1]

5. 各种格式支持代码示例

python 复制代码
import os
import time
import base64
import wave
import numpy as np
import soundfile as sf
from dotenv import load_dotenv
from funasr import AutoModel
from pydub import AudioSegment


# 加载环境变量
load_dotenv()
root_path = os.environ.get("model_root_dir", "")
print(f"root_path: {root_path}")

# 模型路径
asr_model_path = root_path + "paraformer-zh"
vad_mode_path = root_path + "fsmn-vad"

# 加载模型
model = AutoModel(
    model=asr_model_path,
    vad_model=vad_mode_path,
    punc_model="ct-punc-c",
)


# ========== 音频格式转换工具函数 ==========

def load_audio_as_numpy(audio_path: str) -> tuple[np.ndarray, int]:
    """加载音频为 numpy 数组(返回数据和采样率)"""
    audio_data, sr = sf.read(audio_path)
    return audio_data, sr


def load_audio_as_bytes(audio_path: str) -> bytes:
    """加载音频为原始字节流"""
    with open(audio_path, "rb") as f:
        return f.read()


def load_audio_as_base64(audio_path: str) -> str:
    """加载音频为 base64 编码字符串"""
    audio_bytes = load_audio_as_bytes(audio_path)
    return base64.b64encode(audio_bytes).decode()


def load_wav_to_pcm_data(wav_path: str):
    """保存pcm_data数据为音频文件"""
    with wave.open(wav_path, "rb") as wf:
        # 获取元信息
        n_channels = wf.getnchannels()      # 声道数
        sample_width = wf.getsampwidth()    # 采样宽度(字节):1→8bit, 2→16bit, 3→24bit(注意!)
        sample_rate = wf.getframerate()     # 采样率 Hz
        n_frames = wf.getnframes()          # 总采样点数(每声道)
        
        print(f"声道数: {n_channels}")
        print(f"采样宽度: {sample_width} 字节 ({sample_width * 8}-bit)")
        print(f"采样率: {sample_rate} Hz")
        print(f"总帧数: {n_frames}")
        print(f"时长: {n_frames / sample_rate:.2f} 秒")

        # 读取原始字节数据
        raw_data = wf.readframes(n_frames)
        return raw_data

def pcm_bytes_to_numpy(pcm_bytes: bytes) -> np.ndarray:
    """PCM 原始字节转 numpy 数组(16bit PCM,需确保是 16kHz 单声道)"""
    pcm_array = np.frombuffer(pcm_bytes, dtype=np.int16)
    return pcm_array.astype(np.float32) / 32768.0


def opus_to_numpy(opus_path: str) -> np.ndarray:
    """Opus 文件转 numpy 数组(自动转 16kHz 单声道)"""
    audio = AudioSegment.from_file(opus_path, format="opus")
    audio = audio.set_frame_rate(16000).set_channels(1)
    samples = np.array(audio.get_array_of_samples(), dtype=np.float32)
    return samples / 32768.0


def base64_to_numpy(base64_str: str) -> np.ndarray:
    """Base64 编码转 numpy 数组(假设是 WAV 格式)"""
    audio_bytes = base64.b64decode(base64_str)
    # WAV 字节流可以直接传给 model.generate
    return audio_bytes  # FunASR 支持字节流,无需再转 numpy


# ========== ASR 识别函数 ==========

def recognize(audio_input, description: str = "") -> str:
    """统一识别接口,打印结果"""
    res = model.generate(
        input=audio_input,
        disable_pbar=True,
    )
    text = res[0]["text"]
    print(f"{description}: {text}")
    return text


# ========== 测试所有支持的输入格式 ==========
if __name__ == "__main__":
    audio_file_path = "audio_data/01.wav"           # 本地音频16khz
    url_audio_path = "https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3"
    audio_files = ["audio_data/01.wav", "audio_data/02.wav"]

    print("\n========== FunASR 支持的音频输入格式测试 ==========\n")

    # 1. 单文件路径
    recognize(audio_file_path, "1. 单文件路径")

    # 2. URL 路径
    recognize(url_audio_path, "2. URL 路径")

    # 3. 多文件批量
    start = time.time()
    res_list = model.generate(input=audio_files, disable_pbar=True)
    elapsed = time.time() - start
    print(f"3. 多文件批量 ({elapsed:.2f}s):")
    for i, res in enumerate(res_list):
        print(f"   [{i+1}] {res['text']}")

    # 4. Numpy 数组
    audio_numpy, sr = load_audio_as_numpy(audio_file_path)         # 我这个是16kHz的wav音频
    print(f"   采样率: {sr}Hz")
    recognize(audio_numpy, "4. Numpy 数组")

    # 5. 原始字节流
    audio_bytes = load_audio_as_bytes(audio_file_path)
    recognize(audio_bytes, "5. 原始字节流")

    # 6. Base64 编码(需解码后传入)
    base64_str = load_audio_as_base64(audio_file_path)       # 先构造的到base64数据
    audio_bytes_from_base64 = base64.b64decode(base64_str)   # audio_bytes == audio_bytes_from_base64
    recognize(audio_bytes_from_base64, "6. Base64 解码")

    # ========== 其他格式示例(注释,需对应文件) ==========

    # 7. PCM 原始数据(假设已有 pcm 文件), pcm_bytes ---> numpy.array
    pcm_bytes = load_wav_to_pcm_data(audio_file_path)
    pcm_numpy = pcm_bytes_to_numpy(pcm_bytes)
    recognize(pcm_numpy, "7. PCM 数据")

    # 8. Opus 文件(需 pydub + ffmpeg)  opus也是转化为pcm_data --> numpy.array在识别
    # 略

    # print("\n========== 测试完成 ==========\n")

二、三大模型组件详解

FunASR 采用流水线架构,由三个核心模型组成,分工明确:

模型 参数名 是否必需 功能职责 适用场景
ASR 模型 model ✅ 必需 核心语音识别,音频 → 文本转换 所有语音识别场景
VAD 模型 vad_model ❌ 可选 语音活动检测,分割静音与有效语音 长音频、实时流式、会议录音
标点模型 punc_model ❌ 可选 自动添加标点符号,提升可读性 需要完整文本输出的场景

三模型协作流程

复制代码
音频输入 → [VAD: 检测语音片段] → [ASR: 语音转文本] → [标点: 添加标点] → 输出结果
              ↑ 可选                  ↑ 必需               ↑ 可选

组合配置建议

音频类型 推荐配置 说明
短音频(<2分钟) model 仅 ASR 模型即可
长音频(>5分钟) model + vad_model VAD 防显存溢出
完整输出 model + vad_model + punc_model 三模型组合
实时流式 model + vad_model VAD 是必需组件

VAD 模型的特殊效果

重要结论 :添加 VAD 模型后,即使没有 punc_model,识别结果中的短句之间也会用空格分隔开。

复制代码
无 VAD + 无标点: 在异步编程中协程可以高效处理大量并发任务
有 VAD + 无标点: 在 异 步 编 程 中 协 程 可 以 高 效 处 理 大 量 并 发 任 务
有 VAD + 有标点: 在异步编程中,协程可以高效处理大量并发任务。

原因:VAD 将音频切分为多个语音片段,每个片段单独识别后拼接,自然形成空格分隔。

1. ASR 模型(必需)

作用:核心语音识别模型,将音频转换为文本。

python 复制代码
model = AutoModel(
    model="paraformer-zh",  # ASR 模型(必需)
)

选择建议

  • 短音频(<30秒):只用 ASR 模型即可
  • 热词需求:使用 speech_seaco_paraformer_large 热词优化版

2. VAD 模型(可选)

作用:语音活动检测,分割静音和有效语音片段。

python 复制代码
model = AutoModel(
    model="paraformer-zh",
    vad_model="fsmn-vad",  # VAD 模型(可选)
)

适用场景

场景 是否需要 VAD
短音频(<2分钟) 可选
长音频(>5分钟) 推荐(防显存溢出)
会议录音(大量静音) 推荐(节省算力)
实时流式识别 必需

重要限制 :有 VAD 模型时,batch_size 必须为 1!

python 复制代码
# 有 VAD,batch_size 必须为 1
model = AutoModel(model="paraformer-zh", vad_model="fsmn-vad")
res = model.generate(input=["a.wav", "b.wav"], batch_size=1)  # 正确

# 无 VAD,batch_size 可以 > 1
model = AutoModel(model="paraformer-zh")
res = model.generate(input=["a.wav", "b.wav"], batch_size=8)  # 正确

3. 标点模型(可选)

作用:为识别结果自动添加标点符号。

python 复制代码
model = AutoModel(
    model="paraformer-zh",
    punc_model="ct-punc-c",  # 标点模型(可选)
)

效果对比

配置 输出结果
无标点模型 在异步编程中协程可以高效处理大量并发任务
有标点模型 在异步编程中,协程可以高效处理大量并发任务。

4. 三模型组合使用

python 复制代码
# 短音频 - 最简配置
model = AutoModel(model="paraformer-zh")

# 长音频 - 加 VAD 切分
model = AutoModel(
    model="paraformer-zh",
    vad_model="fsmn-vad",
)

# 长音频 + 标点 - 完整配置
model = AutoModel(
    model="paraformer-zh",
    vad_model="fsmn-vad",
    punc_model="ct-punc-c",
)

5. 单个文件与批量识别代码示例

python 复制代码
import os
import time
from dotenv import load_dotenv
from funasr import AutoModel

# 加载环境变量
load_dotenv()
root_path = os.environ.get("model_root_dir", "")
print(f"root_path: {root_path}")

# 模型路径
# https://huggingface.co/JunHowie/speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch
# https://huggingface.co/funasr/paraformer-zh
# https://huggingface.co/FunAudioLLM/SenseVoiceSmall
# ["paraformer-zh", "SenseVoiceSmall", "speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch"]
asr_model_path = os.path.join(root_path, "paraformer-zh")
vad_mode_path = os.path.join(root_path, "fsmn-vad")

# 加载模型
model = AutoModel(
    model=asr_model_path,           # ASR 模型(必需)                                                                                                 
    vad_model=vad_mode_path,        # VAD 模型(可选)  长音频,主要作用于实时流式语音中的识别,加了VAD识别在asrtext,就算没有punc_mode, 短句之间也会有空格区分开来                                                                                                
    punc_model="ct-punc-c",         # 标点模型(可选)   是否针对asr识别文本添加标点符号,不加这个asr文本中不会有标点符号
    # 常用参数(可选)
    # device="cuda",      # 设备:cuda / cpu / cuda:0 / cuda:1
    # ncpu=4,             # CPU 线程数,默认 4
    # disable_update=True,  # 禁用自动下载更新
    # disable_log=True,     # 禁用日志
)


# ========== ASR 识别函数 ==========

def recognize(audio_input, hotword: str = "", description: str = "") -> str:
    """统一识别接口"""
    res = model.generate(
        input=audio_input,
        hotword=hotword,
        disable_pbar=True,                  # 不限时rtf
    )
    text = res[0]["text"]
    print(f"{description}: {text}")
    return text


def recognize_batch(audio_files: list, hotword: str = "", description: str = "") -> list:
    """批量识别接口"""
    start = time.time()
    res_list = model.generate(
        input=audio_files,
        hotword=hotword,
        batch_size=1,
        disable_pbar=True,
    )
    elapsed = time.time() - start
    print(f"{description} ({elapsed:.2f}s):")
    for i, res in enumerate(res_list):
        print(f"   [{i+1}] {res['text']}")
    return res_list


# ========== 测试 ==========

if __name__ == "__main__":
    audio_file = "audio_data/01.wav"
    audio_files = ["audio_data/01.wav", "audio_data/02.wav"]

    print("\n========== FunASR 识别测试 ==========\n")

    # 1. 单文件识别(无热词)
    recognize(audio_file, description="1. 单文件识别")

    # 2. 多文件批量识别
    recognize_batch(audio_files, description="3. 批量识别")
    print("\n========== 测试完成 ==========\n")

    print("模型说明:")
    print(f"  ASR 模型: {asr_model_path}")
    print(f"  VAD 模型: {vad_mode_path}")
    print(f"  标点模型: ct-punc-c")
    print()
    print("注意事项:")
    print("  - 有 VAD 模型时,batch_size 必须为 1")         # 不为1时候会报错
    print("  - 无 VAD 模型时,batch_size 可以 > 1") 
    print("  - 热词格式: '词1 词2' 或 '词1:权重 词2:权重'")

三、FunASR 支持的参数详解

1. AutoModel 初始化参数

python 复制代码
model = AutoModel(
    model="paraformer-zh",      # ASR 模型(必需)
    vad_model="fsmn-vad",       # VAD 模型(可选)
    punc_model="ct-punc-c",     # 标点模型(可选)
    
    # 设备与性能
    device="cuda",              # 设备:cuda / cpu / cuda:0 / cuda:1
    ncpu=4,                     # CPU 线程数,数据预处理并行度
    
    # 其他配置
    disable_update=True,        # 禁用自动下载更新(离线环境必需)
    disable_log=True,           # 禁用日志
    hub="ms",                   # 模型源:ms(魔搭) / hf(HuggingFace)
)

device 与 ncpu 的区别

参数 作用阶段 任务类型
device 模型推理阶段 GPU 并行计算(神经网络)
ncpu 数据预处理阶段 CPU 密集型(音频加载、特征提取)

2. generate() 方法参数

python 复制代码
res = model.generate(
    input="audio.wav",          # 音频输入
    
    # 解码参数
    batch_size_s=300,           # 每批处理音频秒数(长音频切分)
    hotword="协程 异步",         # 热词
    
    # 输出控制
    disable_pbar=True,          # 关闭进度条
    
    # 并行处理(无 VAD 时有效)
    batch_size=8,               # 同时处理 N 条音频
    num_workers=4,              # 数据加载线程数
    
    # 语言设置
    language="zh",              # 语言:自动检测或指定
)

batch_size 与 batch_size_s 的区别

参数 作用 适用场景
batch_size 同时处理 N 条音频 多音频批量处理
batch_size_s 每批处理 N 秒音频 单条长音频切分

四、主流小模型对比

模型 特点 适用场景 热词支持
paraformer-zh 通用中文 ASR,速度快 常规语音识别 ❌ 较弱
SenseVoiceSmall 多语言、情感识别、语音事件检测 需要多模态信息 ❌ 较弱
speech_seaco_paraformer_large 热词优化版 专业术语、品牌名识别 ✅ 效果好

1. paraformer-zh

最常用的通用中文 ASR 模型,速度快,准确率高。

python 复制代码
model = AutoModel(model="paraformer-zh")

特点

  • 输出分词结果,词之间有空格
  • 支持 VAD、标点模型组合
  • 适合常规语音识别场景

2. SenseVoiceSmall

多功能模型,除 ASR 外还支持:

python 复制代码
model = AutoModel(model="SenseVoiceSmall")

输出示例

复制代码
<|zh|><|NEUTRAL|><|Speech|><|woitn|>在异步编程中携程可以高效处理大量并发任务

特殊标记含义

  • <|zh|> - 语言标识(中文)
  • <|NEUTRAL|> - 情感识别(中性)
  • <|Speech|> - 语音事件检测
  • 文本部分是连续的,无空格

3. speech_seaco_paraformer_large

热词优化版模型,对专业术语识别效果好。

python 复制代码
model = AutoModel(
    model="speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch",
    model_revision="v2.0.4",
)

适用场景

  • 专业领域术语(医疗、法律、金融)
  • 品牌名、人名识别
  • 新词、生僻词识别

五、Funasr热词功能

  • 在线TTS 语音生成测试
    我测试的音频 "audio_data/hot_words_01.wav" 通过tts生成然后下载到本地,你也可以通过录音采集,我测试的音频是
    【"在 异 步 编 程 中 携 程 可 以 高 效 处 理 大 量 并 发 任 务"】, 热词为携程 / 协程 , 案例只是为了演示热词的使用案例,但是针对复杂专业术语受限于模型能力他不一定能能识别到。

1. 为什么需要热词?

普通 ASR 模型对专业术语、人名、品牌名等识别率较低。热词功能可以提升特定词汇的识别准确率。

2. 热词模型选择

重要 :只有 speech_seaco_paraformer_large 对热词响应较好!

模型 热词效果
speech_seaco_paraformer_large ✅ 热词优化版,效果好
paraformer-zh ❌ 响应较弱
SenseVoiceSmall ❌ 响应较弱

3. 热词使用示例完整代码

python 复制代码
import os
from funasr import AutoModel
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()
root_path = os.environ.get("model_root_dir", "")
print(f"root_path: {root_path}")

# 热词优化版 Paraformer 模型(非实时)
# 注意:普通 paraformer-zh 和 SenseVoiceSmall 对热词识别效果较差
model_path = os.path.join(root_path, "speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch")
print(f"model_path: {model_path}")
vad_mode_path = os.path.join(root_path, "fsmn-vad")

# 加载模型
model = AutoModel(
    model=model_path,
    vad_model=vad_mode_path,        # VAD 模型(可选)  长音频,主要作用于实时流式语音中的识别,加了VAD识别在asrtext,就算没有punc_mode, 短句之间也会有空格区分开来                                                                                                
    punc_model="ct-punc-c",         # 标点模型(可选)   是否针对asr识别文本添加标点符号,不加这个asr文本中不会有标点符号
    model_revision="v2.0.4",
)

audio_path = "audio_data/hot_words_01.wav"


# ========== 热词测试函数 ==========

def recognize_with_hotword(audio: str, hotword: str = "", description: str = "") -> str:
    """带热词的识别测试"""
    res = model.generate(
        input=audio,
        hotword=hotword,
        batch_size_s=300,
        disable_pbar=True,
    )
    text = res[0]["text"]
    print(f"{description}: {text}")
    return text


# ========== 热词对比测试 ==========
# 我看网上的一些针对funasr的热词识别都不怎么有效,可能原始还是模型本身能力的问题,或者是我用的不规范
# TTS 生成语音参考 网址 我测试的音频是 【"在 异 步 编 程 中 携 程 可 以 高 效 处 理 大 量 并 发 任 务"】, 热词为携程 / 协程
# https://modelscope.cn/studios/Qwen/Qwen3-TTS-Demo
if __name__ == "__main__":
    print("\n========== FunASR 热词识别对比测试 ==========\n")

    # 测试音频说明
    print(f"音频文件: {audio_path}")
    print(f"模型: speech_seaco_paraformer_large (热词优化版)")
    print()

    # 1. 无热词识别
    text_before = recognize_with_hotword(audio_path, hotword="", description="1. 无热词")

    # 2. 单热词识别。这个有效
    text_single = recognize_with_hotword(audio_path, hotword="协程", description="2. 单热词: '协程'")

    # 3. 多热词识别(空格分隔)        效果一般,无法识别, 词多了可能也无法识别
    text_multi = recognize_with_hotword(
        audio_path,
        hotword="协程 并发 123",
        description="3. 多热词: '协程 异步 并发'"
    )

    # 4. 热词权重测试(格式: 热词:权重) 效果一般,无法识别, 可能不支持这种写法
    text_weight = recognize_with_hotword(
        audio_path,
        hotword="协程:50 异步:3",
        description="4. 带权重: '协程:5 异步:3'"
    )

    print("\n========== 测试总结 ==========\n")
    print("热词格式说明:")
    print("  - 单热词: '协程'")
    print("  - 多热词: '协程 异步 并发' (空格分隔)")
    print("  - 带权重: '协程:5 异步:3' (数值越大权重越高)")
    print()
    print("模型选择:")
    print("  ✅ speech_seaco_paraformer_large - 热词优化版,效果好")
    print("  ❌ paraformer-zh / SenseVoiceSmall - 对热词响应较弱")

4. 热词使用注意事项

  1. 模型选择:必须使用热词优化版模型
  2. 热词数量:不宜过多,效果可能下降
  3. 权重格式热词:数值,数值越大优先级越高
  4. 实际效果:单热词效果最好,多热词和权重效果不稳定

六、进阶推荐

FunASR 提供的是轻量级小模型,如果对 ASR 识别准确率和热词识别要求更高,且有足够的计算资源,推荐使用以下开源大模型:

模型 说明 链接
Fun-ASR-Nano-2512 Qwen2.5-32B 微调的 ASR 模型,准确率高,支持热词 HuggingFace
Qwen-ASR 阿里通义千问语音识别模型,准确率高 ModelScope
ChatGLM-ASR 智谱 GLM 语音识别模型,支持多语言 ModelScope

Fun-ASR-Nano-2512 特点

  • 基于 Qwen2.5-32B 微调,识别准确率更高
  • 支持热词功能,效果优于 FunASR 小模型
  • 适合对识别质量有较高要求的场景

七、常见问题

Q1: VAD 和 batch_size 的关系?

有 VAD 模型时,batch_size 必须为 1。VAD 模型不支持批量处理。

Q2: 为什么热词不生效?

检查是否使用了热词优化版模型 speech_seaco_paraformer_large

Q3: Paraformer 输出有空格怎么办?

后处理去掉空格即可:

python 复制代码
text = res[0]["text"].replace(" ", "")

Q4: Base64 音频如何识别?

先解码为字节流:

python 复制代码
audio_bytes = base64.b64decode(base64_str)
res = model.generate(input=audio_bytes)

参考资料


博客为个人学习笔记,如有错误,请指正修改。

相关推荐
七颗糖很甜2 小时前
基于 OpenCV 的 FY2 云顶图云块追踪算法实现
人工智能·opencv·算法
__Wedream__2 小时前
NTIRE 2026 Challenge on Efficient Super-Resolution——冠军方案解读
人工智能·深度学习·算法·计算机视觉·超分辨率重建
FL16238631292 小时前
基于深度学习mediape实现人员跌倒人体姿势跌倒检测算法源码+说明文件
人工智能·深度学习·算法
James5062 小时前
NewAPI使用
人工智能·docker·newapi
FAFU_kyp2 小时前
AP2 执行流程详解
人工智能
AI英德西牛仔2 小时前
手机怎么把AI对话导出
人工智能·ai·智能手机·豆包·deepseek·ds随心转
Old Uncle Tom2 小时前
Claude Code 记忆系统架构分析
人工智能·ai·系统架构·agent
空中湖2 小时前
大模型修炼秘籍 第一卷灵气采集 第一章:天地为炉——海量数据之采集
人工智能
sp_fyf_20242 小时前
【大语言模型】 语言模型学习什么以及何时学习?隐式课程假说
人工智能·学习·语言模型