这篇指南从零开始,引导用户完成从环境搭建到最终部署一个离线语音识别工具的全过程。
本指南将详细介绍如何利用百度的 PaddleSpeech 语音技术库,一步步构建一个强大的命令行工具。该工具能够批量、递归地将文件夹内的 .wav 音频文件转换为 .txt 文本文件,并最终打包成一个无需网络、无需 Python 环境的独立可执行文件,方便在任何离线服务器上部署和使用。
参考文献:
PaddleSpeech 官方 GitHub
在这一部分,我们将在本地开发机上配置好所需环境,并验证 PaddleSpeech 的基本语音识别功能。
为了保持项目环境的纯净,我们推荐使用 Conda 创建一个独立的虚拟环境。
# 创建一个名为 gj,使用 Python 3.9 的环境
conda create -n gj python=3.9
# 激活该环境
conda activate gj
在已激活的环境中,我们分步安装 PaddleSpeech。
# 第一步:安装 PaddlePaddle 框架 (CPU版本)
# 注意:若您有 NVIDIA GPU 并已配置好 CUDA,可选择安装 GPU 版本
pip install paddlepaddle==0.0.0 -f https://www.paddlepaddle.org.cn/whl/linux/cpu-mkl/develop.html
# 第二步:安装 PaddleSpeech 工具包及其依赖
pip install pytest-runner
pip install paddlespeech
可选:从源码安装
如果您希望使用最新的功能或进行二次开发,可以选择从源码安装:git clone https://github.com/PaddlePaddle/PaddleSpeech.git cd PaddleSpeech pip install .
安装完成后,我们下载一个官方的中文音频文件进行快速测试。
# 下载测试音频
wget -c https://paddlespeech.cdn.bcebos.com/PaddleAudio/zh.wav
创建一个 Python 文件 demo.py,并写入以下代码:
from paddlespeech.cli.asr.infer import ASRExecutor
# 初始化 ASR 执行器 (首次运行会自动从网络下载模型)
asr = ASRExecutor()
# 对下载的音频文件进行识别
result = asr(audio_file="zh.wav")
# 打印识别结果
print(result)
运行脚本 python demo.py,如果一切正常,您应该能看到类似如下的输出:
我认为跑步最重要的就是给我带来了身体健康
单个文件的处理远不能满足实际需求。接下来,我们将逐步增强脚本功能,以实现对整个文件夹的自动化处理。
此脚本可以遍历指定文件夹内的所有 .wav 文件,并生成对应的 .txt 文件。
核心思路:
指定一个输入文件夹。
初始化一次 ASRExecutor,避免重复加载模型,提高效率。
遍历文件夹内的所有文件,筛选出 .wav 文件。
对每个 .wav 文件进行识别,并将结果保存到同名的 .txt 文件中。
使用 try...except 块来捕获可能发生的错误,确保单个文件的失败不会中断整个流程。
完整代码 (batch_asr.py):
import os
import time
from paddlespeech.cli.asr.infer import ASRExecutor
# 1. 用户配置: 指定存放 WAV 文件的文件夹
INPUT_FOLDER = './audio_files/'
# 2. 初始化 ASR 执行器
try:
print("正在初始化 ASR 模型... (首次运行需要下载模型,请耐心等待)")
asr = ASRExecutor()
print("模型初始化完成。")
model_ready = True
except Exception as e:
print(f"模型初始化失败: {e}")
model_ready = False
# 3. 核心处理逻辑
if model_ready:
if not os.path.isdir(INPUT_FOLDER):
print(f"错误:文件夹 '{INPUT_FOLDER}' 不存在。")
else:
wav_files = [f for f in os.listdir(INPUT_FOLDER) if f.lower().endswith('.wav')]
if not wav_files:
print("在指定文件夹中没有找到任何 .wav 文件。")
else:
total_files = len(wav_files)
print(f"找到 {total_files} 个 .wav 文件,准备开始转换...")
for i, filename in enumerate(wav_files):
print(f"\n--- [{i+1}/{total_files}] 正在处理: {filename} ---")
audio_file_path = os.path.join(INPUT_FOLDER, filename)
base_name = os.path.splitext(filename)[0]
output_txt_path = os.path.join(INPUT_FOLDER, f"{base_name}.txt")
try:
result_text = asr(audio_file=audio_file_path)
print(f" 识别结果: {result_text}")
with open(output_txt_path, 'w', encoding='utf-8') as f:
f.write(result_text)
print(f" 成功保存到: {output_txt_path}")
except Exception as e:
print(f" 错误: 处理文件 {filename} 时发生错误: {e}")
print("\n--- 所有文件处理完毕! ---")
这是我们最终采用的方案,它能自动扫描一个根目录及其所有子目录,处理所有发现的 .wav 文件。
核心思路:
使用 os.walk() 递归遍历整个目录树。
先收集所有 .wav 文件的完整路径,便于统计和显示进度。
在音频文件所在的原目录下生成同名的 .txt 文件。
完整代码 (recursive_asr.py):
import os
import time
from paddlespeech.cli.asr.infer import ASRExecutor
# 1. 用户配置: 指定要处理的根文件夹
ROOT_FOLDER = './my_audios/'
# 2. 初始化 ASR 执行器
try:
print("正在初始化 ASR 模型...")
asr = ASRExecutor()
print("模型初始化完成。")
model_ready = True
except Exception as e:
print(f"模型初始化失败: {e}")
model_ready = False
# 3. 核心处理逻辑
if model_ready:
if not os.path.isdir(ROOT_FOLDER):
print(f"错误:根文件夹 '{ROOT_FOLDER}' 不存在。")
else:
# 步骤 3a: 递归扫描并收集所有 .wav 文件
print(f"\n正在扫描文件夹 '{ROOT_FOLDER}' 及其所有子文件夹...")
wav_file_list = []
for dirpath, _, filenames in os.walk(ROOT_FOLDER):
for filename in filenames:
if filename.lower().endswith('.wav'):
full_path = os.path.join(dirpath, filename)
wav_file_list.append(full_path)
# 步骤 3b: 遍历列表,处理每一个 .wav 文件
total_files = len(wav_file_list)
if total_files == 0:
print("扫描完成,未找到任何 .wav 文件。")
else:
print(f"扫描完成,共找到 {total_files} 个 .wav 文件,准备开始转换...")
start_time = time.time()
for i, audio_file_path in enumerate(wav_file_list):
print(f"\n--- [{i+1}/{total_files}] 正在处理: {audio_file_path} ---")
base_name, _ = os.path.splitext(audio_file_path)
output_txt_path = f"{base_name}.txt"
if os.path.exists(output_txt_path):
print(f" 跳过: 文本文件 '{output_txt_path}' 已存在。")
continue
try:
result_text = asr(audio_file=audio_file_path)
print(f" 识别结果: {result_text}")
with open(output_txt_path, 'w', encoding='utf-8') as f:
f.write(result_text)
print(f" 成功保存到: {output_txt_path}")
except Exception as e:
print(f" 错误: 处理文件 {audio_file_path} 时发生错误: {e}")
print(f"\n--- 所有文件处理完毕!总用时: {time.time() - start_time:.2f} 秒。 ---")
为了在没有网络和Python环境的服务器上运行,我们将使用 PyInstaller 把脚本和模型打包成一个文件。
核心挑战: PaddleSpeech 默认从网络下载模型。我们需要将模型预先下载好,并与脚本一同打包,再修改脚本以加载本地模型。
安装 PyInstaller:
pip install pyinstaller
下载模型:
只需运行一次 recursive_asr.py 脚本。它会自动下载所需的模型文件到你的用户主目录。
找到模型文件夹:
模型会被存放在一个隐藏文件夹中,找到它并记下其完整路径。
Windows: C:\Users\你的用户名\.paddlespeech\models
Linux/macOS: /home/你的用户名/.paddlespeech/models
我们需要让脚本在作为可执行文件运行时,能找到被打包到内部的模型。将 recursive_asr.py 的内容修改为以下版本:
最终脚本 (recursive_asr_for_exe.py):
import os
import sys
import time
from paddlespeech.cli.asr.infer import ASRExecutor
def get_model_path(model_name: str):
"""
智能获取模型路径。
在打包后的可执行文件中,它指向由PyInstaller创建的临时文件夹。
在作为普通脚本运行时,它使用PaddleSpeech的默认缓存路径。
"""
if hasattr(sys, '_MEIPASS'):
# _MEIPASS 是 PyInstaller 运行时的临时路径
return os.path.join(sys._MEIPASS, 'paddlespeech', 'models', model_name)
return os.path.join(os.path.expanduser('~'), '.paddlespeech', 'models', model_name)
# --- 用户配置 ---
ROOT_FOLDER = './my_audios/'
# --- 初始化 ASR 执行器 (关键改动) ---
model_ready = False
try:
print("正在准备 ASR 模型...")
# 指定我们下载的模型文件夹名 (conformer_wenetspeech 是常用中文模型)
model_name = 'conformer_wenetspeech-zh-16k'
model_dir = get_model_path(model_name)
print(f"使用的模型路径: {model_dir}")
if not os.path.exists(model_dir):
raise FileNotFoundError(f"错误: 找不到模型文件夹 '{model_dir}'。请检查打包命令和模型名称。")
# 初始化时直接指定本地模型路径,阻止网络下载
asr = ASRExecutor(model_dir=model_dir)
print("ASR 模型加载成功。")
model_ready = True
except Exception as e:
print(f"ASR 模型初始化失败: {e}")
import traceback
traceback.print_exc()
# --- 核心处理逻辑 (与之前版本相同,此处省略,请从上方复制) ---
# [ ... 此处粘贴上一节的 "核心处理逻辑" 代码 ... ]
# 在脚本末尾添加,防止可执行文件窗口一闪而过
input("\n全部任务完成,按 Enter 键退出。")
打开终端,进入脚本所在目录,执行以下命令。请务必将模型源路径替换为你的真实路径!
对于 Windows 用户:
pyinstaller --onefile --add-data "C:\Users\你的用户名\.paddlespeech\models;paddlespeech/models" recursive_asr_for_exe.py
对于 Linux/macOS 用户: (注意路径分隔符是 :)
pyinstaller --onefile --add-data "/home/你的用户名/.paddlespeech/models:paddlespeech/models" recursive_asr_for_exe.py
命令解析:
--onefile: 将所有依赖打包成一个单独的可执行文件。
--add-data "源路径:目标路径": 核心命令。它将 源路径 (你本地的模型文件夹) 的内容,在打包文件中放置到 目标路径 (paddlespeech/models)下,这个目标路径必须与脚本中 get_model_path 函数的逻辑相匹配。
找到可执行文件:
打包成功后,在当前目录下会生成一个 dist 文件夹。里面就是最终的可执行文件(例如 Linux 上的 recursive_asr_for_exe 或 Windows 上的 recursive_asr_for_exe.exe)。
部署到离线服务器:
将 dist 文件夹中的可执行文件复制到服务器。
在服务器上,根据脚本中 ROOT_FOLDER 的设置,在可执行文件同级目录下创建一个 my_audios 文件夹。
将所有需要处理的 .wav 文件(可包含任意子文件夹结构)放入 my_audios 文件夹。
运行:
在 Linux/macOS 上:
# 赋予执行权限
chmod +x recursive_asr_for_exe
# 运行
./recursive_asr_for_exe
在 Windows 上:
直接双击 recursive_asr_for_exe.exe 或在 CMD/PowerShell 中运行它。
程序启动后,会显示处理进度,并在 my_audios 文件夹内的相应位置生成所有 .txt 文件。至此,你已成功构建并部署了一个完全离线的语音识别工具。