长音频离线语音识别系统——基于Whisper.cpp的本地部署方案

背景

前段时间接到一个任务,需要将老板的会议录音转为文本,方便助理整理会议纪要。由于是内部会议,对内容保密性要求较高,因此不能使用第三方平台的商业化语音识别API,只能通过本地或公司内网部署的模型来实现。

经过调研,公司内部已经部署了ASR(语音识别)模型,并提供了现成的API。但该API仅支持60秒以内的短音频,而我们的会议录音通常长达2小时左右。如果采用分段切割音频、循环调用API、最后合并结果的方案,虽然可行,但实现起来较为复杂且不够高效。

于是,我开始寻找本地化的语音识别方案。在咨询纳米AI 上的DeepSeek后,他们推荐了Whisper.cpp,这是一个能在本地高效运行的语音转文本方案,完美符合我们的需求。

本地部署Whisper

1. 简介

Whisper 是 OpenAI 推出的高性能语音识别模型,支持多语言转写。whisper.cpp 是其 C++ 实现版本,优化了推理效率,尤其适合本地部署和低资源环境。

它具有以下优势:

  • 高性能:基于 C/C++ 的高效实现,显著提升语音转文本处理速度。
  • 跨平台支持:兼容 macOS、Linux、Windows 等多种操作系统。
  • 离线运行:无需依赖第三方平台,适合对数据隐私有要求的场景。
  • 低资源占用:适用于资源受限的设备,如嵌入式系统。
2.克隆Whisper项目

从 GitHub 克隆 whisper.cpp 仓库。终端运行以下命令:

bash 复制代码
git clone https://github.com/ggerganov/whisper.cpp.git
3.**安装必要依赖 **
  • 安装CMake : whisper.cpp 项目使用 CMake 进行构建(编译)。你需要先安装一些必要的工具,比如 CMake 和 Make,它们可以通过 Homebrew 安装:

    go 复制代码
    brew install cmake make
  • 安装ffmpeg:进行语音识别,免不了要对多媒体文件进行处理,安装ffmpeg

    复制代码
    brew install ffmpeg
4.构建&编译whisper.cpp

在 whisper.cpp 目录中,运行以下命令来构建项目:

css 复制代码
cmake -B build
cmake --build build --config Release

完成后会在/build/bin/目录下面 生成一个whisper-cli可执行文件

pic

5.下载Whisper模型

model

whisper.cpp 需要使用预训练的 Whisper 模型进行语音识别。你可以从 OpenAI 提供的链接下载模型文件:

huggingface.co/ggerganov/w...

ggml.ggerganov.com

下载后,将模型文件放到其子目录models中。当然你也可以通过Whisper项目自带的脚本来下载模型

bash 复制代码
cd whisper.cpp
bash ./models/download-ggml-model.sh medium

Whisper 提供多个模型版本,主要差异在于参数数量、所需内存、处理速度和识别准确率。以下是各模型的简要对比:

模型版本 参数量 所需内存 相对速度 特点
tiny 39M ~1GB ~10x 速度快,准确率较低
base 74M ~1GB ~7x 平衡速度与准确率
small 244M ~2GB ~4x 准确率提升,速度适中
medium 769M ~5GB ~2x 高准确率,速度较慢
large 1550M ~10GB 1x 最高准确率,速度最慢
turbo 809M ~6GB ~8x 与 large 相比,速度提升约 5.4 倍,准确率略有下降

此外,.en 版本的模型专为英语优化,适用于仅处理英语的场景。以-q5 或是 -q8结尾的模型是进行了量化的版本。(量化是通过降低模型参数的位数来减小模型体积和加快处理速度的技术)

具体选择什么模块可以根据自己的电脑配置来选择,我这里选择的是medium版本的模型。

实现语音识别

  • 格式转换

    接下来利用可执行文件whisper-cli进行语音识别,whisper-cli 目前仅适用于 16 位 WAV 文件。如果不知道音频信息,可以通过ffmpeg来进行音频资源信息的查看:

    css 复制代码
    ffmpeg -i ***.wav

    可以看到类似下面的输出信息

    less 复制代码
    Input #0, wav, from '会议记录.wav':
      Duration: 00:00:05.00, bitrate: 705 kb/s
      Stream #0:0: Audio: pcm_s16le, 44100 Hz, mono, s16, 705 kb/s

    简单解释一下:

    编码格式:pcm_s16le 表示音频使用的是 16 位有符号小端 PCM 编码。

    采样率:44100 Hz 表示音频的采样率为 44.1 kHz。

    声道数:mono 表示单声道音频。

    采样格式:s16 表示每个样本使用 16 位。

    如果您看到的编码格式是 pcm_s16le,那么该文件确实是 16 位的 WAV 文件。如果文件格式不符合类型要求,就需要使用ffmpeg 进行格式转换,方式如下:

    css 复制代码
    ffmpeg -i input.mp3 -ar 16000 -ac 1 -c:a pcm_s16le output.wav

    当然如果是视频格式的文件,例如mov类型,使用下面命令提取音频文件:

    css 复制代码
    ffmpeg -i input.mov -vn -acodec pcm_s16le -ar 16000 -ac 1 output.wav
  • 语音识别

    使用whisper-cli进行语音识别:

    xml 复制代码
    ./whisper-cli -f <audio-file> -m <model-path> -osrt -l zh -t 4

    • **-f **:要转换的音频文件路径

    -m:模型文件路径

    -osrt -otxt -ovtt:需要输出字幕文件(如 SRT、TXT 或 VTT 格式)

    -l : 指定识别的语言(默认为自动检测)

    -t:指定使用的线程数(根据你的 CPU 核心数量调整)

    完成语音识别。

存在问题

1. 识别后输出的是繁体中文的内容

截屏2025-05-23 14.41.20

这个问题的解决方案,可以通过在命令行后面加prompt参数搞定:

bash 复制代码
./whisper-cli -m models/ggml-whisper-large-zh-cv11-Q5_0.bin -f output.wav -l zh -t 4 --output-txt --prompt "以下是普通话的句子。"
2. 提升中文内容的识别准确度,使用专门的中文模型

可以通过链接huggingface.co/second-stat...

截屏2025-05-23 14.44.45

3. 长音频文件识别错误

有时候,处理较长的音频文件(> 20 分钟)时,可能会遇到识别错误,导致输出重复的句子。特别是当音频的声音不清晰,存在较长空白、杂音等内容时,就容易出现文本重复出现的问题。例如:

erlang 复制代码
你好,今天天气不错
我们一起去海边旅游吧
我们一起去海边旅游吧
我们一起去海边旅游吧
...

解决方案一:

添加参数:--condition_on_previous_text False

--condition_on_previous_text 控制是否在处理一个新音频段(chunk)时,将前一段的识别结果作为上下文传递给当前段落。

  • True(默认) :使用前一段的文字作为上下文,有助于维持上下文一致性,尤其是处理完整对话、语境相关内容时,会更自然、连贯。

  • False每个段落独立识别不会依赖之前的识别内容。这有助于避免:

    • 重复句子问题
    • 上下文"污染"造成的识别错误
    • 更适合批量短音频较长音频中的独立片段识别

解决方案二:

利用ffmpeg将音频资源进行分割,分割成小于20分钟的小片段,针对每个小片段进行语音识别,最后将识别的内容进行拼接。

假设你知道音频总长是 120 秒,你可以执行下面的命令将音频分割为两段60S的分段:

css 复制代码
ffmpeg -i input.wav -ss 0 -t 60 output_part1.wav
ffmpeg -i input.wav -ss 60 -t 60 output_part2.wav

解释:

  • -ss:起始时间(单位:秒)
  • -t:持续时长(单位:秒)

脚本自动化实现

针对上面长音频文件识别错误的问题,我利用ffmpeg对音频资源进行了识别前的预处理。主要包括下面几个方面:

  • 音频格式转换

    转为 Whisper 可识别的 16kHz 单声道 16位 WAV 格式

    bash 复制代码
    ffmpeg -y -i "$INPUT_AUDIO" -ar 16000 -ac 1 -sample_fmt s16 whisper_input.wav
  • 提升音频资源的音量

    如果音频资源本身质量不佳,音量过小,可以利用ffmpeg提升音频的音量:

    arduino 复制代码
    ffmpeg -i output_denoised.wav -af "volume=3dB" output_enhanced.wav
  • 针对音频资源进行降噪

    如果音频质量不佳,可以利用 soxnoiseprof对音频资源进行降噪处理:

    • Step 1: 提取背景噪声样本(纯背景噪音段,5秒等)

      css 复制代码
      ffmpeg -i input.wav -ss 0 -t 5 noise_sample.wav
    • Step 2: 生成噪声配置文件(需安装 sox, 可直接通过homebrew安装brew install sox

      sox noise_sample.wav -n noiseprof noise.prof

    • Step 3: 用配置文件进行降噪

      css 复制代码
      sox input.wav output_denoised.wav noisered noise.prof 0.21

      🔹 参数 0.21 是降噪强度,越高越激进(建议 0.2~0.5 之间试试)

  • 音频资源切割

    按照用户的设置,将音频资源分割成固定的片段

    perl 复制代码
    ffmpeg -y -i ***.wav -f segment -segment_time "$SPLIT_DURATION_SECONDS" -c copy part%03d.wav
  • 将分割的片段按照顺序进行语音识别

    bash 复制代码
    i=0
    for part in part*.wav; do
      PART_INDEX=$(printf "%03d" "$i")
      echo "--> 转换 $part 为文本..."
      /Users/chenxiaoming/Desktop/MIng/whisper/whisper.cpp/build/bin/whisper-cli \
        -f "$part" -m "$WHISPER_MODEL" -l zh -t 4 --output-txt \
        --prompt "以下是普通话的句子,使用简体中文输出。"
      mv "${part%.wav}.txt" "part_${PART_INDEX}.txt"
      ((i++))
    done
  • 最后,合并所有识别文本为总文件

    bash 复制代码
    FINAL_TXT="$WORKDIR/${BASENAME}_whisper.txt"
    cat part_*.txt > "$FINAL_TXT"

    最后的脚本文件:

    bash 复制代码
    #!/bin/bash
    SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
    WHISPER_CLI="$SCRIPT_DIR/whisper-cli"
    # 参数检查
    if [ $# -lt 2 ]; then
      echo "用法: $0 <音频文件路径> <whisper模型路径> [--volume <dB>] [--no-denoise] [--split-duration <分钟>]"
      exit 1
    fi
    
    INPUT_AUDIO="$1"
    WHISPER_MODEL="$2"
    shift 2
    
    # 默认参数值
    VOLUME_GAIN=3
    DENOISE_ENABLED=true
    SPLIT_DURATION_MINUTES=20
    
    # 解析可选参数
    while [[ $# -gt 0 ]]; do
      case "$1" in
        --volume)
          VOLUME_GAIN="$2"
          shift 2
          ;;
        --no-denoise)
          DENOISE_ENABLED=false
          shift
          ;;
        --split-duration)
          SPLIT_DURATION_MINUTES="$2"
          shift 2
          ;;
        *)
          echo "❌ 未知参数: $1"
          exit 1
          ;;
      esac
    done
    
    # 设置变量
    WORKDIR=$(dirname "$INPUT_AUDIO")
    BASENAME=$(basename "$INPUT_AUDIO" | sed 's/.[^.]*$//')
    TMP_DIR="$WORKDIR/whisper_temp"
    mkdir -p "$TMP_DIR"
    
    cd "$TMP_DIR" || exit 1
    
    echo "==> 步骤1:转为 Whisper 可识别的 16kHz 单声道 16位 WAV 格式"
    ffmpeg -y -i "$INPUT_AUDIO" -ar 16000 -ac 1 -sample_fmt s16 whisper_input.wav
    
    echo "==> 步骤2:增强音量(+${VOLUME_GAIN}dB)"
    ffmpeg -y -i whisper_input.wav -af "volume=${VOLUME_GAIN}dB" output_enhanced.wav
    
    if [ "$DENOISE_ENABLED" = true ]; then
      echo "==> 步骤3:提取前5秒背景噪声样本"
      ffmpeg -y -i output_enhanced.wav -ss 0 -t 5 noise_sample.wav
    
      echo "==> 步骤4:生成噪声配置文件"
      sox noise_sample.wav -n noiseprof noise.prof
    
      echo "==> 步骤5:使用配置文件降噪"
      sox output_enhanced.wav output_denoised.wav noisered noise.prof 0.1
    else
      echo "==> 跳过降噪处理,直接使用增强后的音频"
      cp output_enhanced.wav output_denoised.wav
    fi
    
    SPLIT_DURATION_SECONDS=$((SPLIT_DURATION_MINUTES * 60))
    
    echo "==> 步骤6:将音频按每 ${SPLIT_DURATION_MINUTES} 分钟分割"
    ffmpeg -y -i output_denoised.wav -f segment -segment_time "$SPLIT_DURATION_SECONDS" -c copy part%03d.wav
    
    echo "==> 步骤7:对分割片段逐个识别"
    i=0
    for part in part*.wav; do
      PART_INDEX=$(printf "%03d" "$i")
      echo "--> 转换 $part 为文本..."
    
      "$WHISPER_CLI" -f "$part" -m "$WHISPER_MODEL" -l zh -t 4 --output-txt \
        --prompt "以下是普通话的句子,使用简体中文输出。"
    
      ORIGINAL_TXT="${part}.txt"
      TARGET_TXT="part_${PART_INDEX}.txt"
    
      if [ -f "$ORIGINAL_TXT" ]; then
        mv "$ORIGINAL_TXT" "$TARGET_TXT"
      else
        echo "⚠️ 警告:没有找到识别结果 $ORIGINAL_TXT"
      fi
    
      ((i++))
    done
    
    echo "==> 步骤8:合并所有识别文本为总文件"
    FINAL_TXT="$WORKDIR/${BASENAME}_whisper.txt"
    cat part_*.txt > "$FINAL_TXT"
    
    echo "✅ 所有步骤完成,最终输出: $FINAL_TXT"
  • 使用脚本:

    参数名 说明 默认值
    --volume 音量增强的分贝数 3
    --no-denoise 是否关闭降噪处理 开启(不传表示开启)
    --split-duration <分钟> 每个音频分段的时长,单位为分钟 20
    复制代码

./voice_whisper.sh 20250419_***.wav ggml-medium-q8_0.bin --volume 5 --no-denoise --split-duration 20

perl 复制代码
#### 总结

以上就是使用Whisper进行语音识别的完整流程,当然除了使用`whisper-cli`进行本地语音识别之外,你可以利用`whisper-server`将其部署为对外的服务。会更多内容可以参考Whisper的项目介绍:https://github.com/openai/whisper
相关推荐
Dawson Zhu7 小时前
AI 辅助调试陷入「反复修改」循环?用证据驱动的四步法破局
人工智能·语言模型·架构·aigc·agi
Behaviour7 小时前
iPhone Duo 来了,苹果 Siri AI 接入定制 Gemini
人工智能·ios·语言模型·aigc·iphone
浩风祭月10 小时前
GPT Image 2.5 Sunburst还是Flare?模型选型、Python接入与成本避坑
python·aigc·openai·图像生成·gpt image 2.5
手写码匠11 小时前
DeepSeek 函数调用实战:从零搭建一个会“动手“的 AI 助手
人工智能·深度学习·算法·aigc
DO_Community15 小时前
RAG 的 Embedding 模型需要微调吗?什么时候值得自己训练?
人工智能·llm·aigc·agent·ai编程
墨风如雪17 小时前
从零开始,开源一本属于自己的 Pi 蓝皮书
aigc
Dawson Zhu17 小时前
Agent 记忆调用的上下文感知:从“能召回“到“敢开口“的决策框架
人工智能·语言模型·架构·aigc·agi
Dawson Zhu1 天前
工作流与 Agent 的工程选型:从“控制权归属“看 LLM 应用架构
人工智能·语言模型·架构·aigc·agi
墨风如雪1 天前
玩遍各种 Agent 后,我反而开始认真研究豆包和飞书
aigc