transformers音频实战02-基于 Speech Commands 数据集的语音识别实战项目全流程
一、项目背景与目标
(一)背景
Speech Commands 数据集是语音识别领域经典的开源数据集,包含 35 个类别(如数字、指令词等)的短语音片段,适合用于基础语音识别模型的开发与验证。本项目基于该数据集,聚焦数字 0-9 的识别任务,搭建并训练语音识别模型,学习从数据准备到模型部署的完整流程。
(二)目标
- 实现 Speech Commands 数据集的自动下载与预处理,筛选出数字 0-9 的语音数据。
- 构建基于 CNN + LSTM 的混合语音识别模型,完成模型训练与评估。
- 借助 Gradio 搭建简单交互界面,实现语音识别功能的快速演示。
二、环境准备
(一)依赖库安装
torch安装
datasets默认使用torchcodec加载音频,请确保torch和python和torchcodec版本匹配。
我这里使用kaggle跑,默认kaggle使用torch2.6版本太低导致加载音频各种不兼容,为了兼容升级torch到2.8并且安装torchcodec=0.7
在终端执行以下命令,安装项目所需 Python 依赖库:
#这是cpu的版本
!pip uninstall -y torch torchvision torchaudio torchcodec
!pip install torch==2.8.0 torchvision torchaudio torchcodec==0.7 datasets matplotlib gradio
如果是gpu可以到pytorch官网 首先查看gpu cuda版本:nvidia-smi,然后官网找到版本后的运行命令安装
我这里
!pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
依赖说明:
torch
:深度学习框架,用于模型构建与训练。torchaudio
:处理音频数据,提供特征提取等功能。datasets
:加载与处理 Hugging Face 数据集。matplotlib
:可视化数据分布、训练曲线等。gradio
:快速构建交互演示界面。
查看torchcodec下加载ffpmeg的so
!ls /usr/local/lib/python3.11/dist-packages/torchcodec
_core libtorchcodec_custom_ops5.so
decoders libtorchcodec_custom_ops6.so
encoders libtorchcodec_custom_ops7.so
_frame.py libtorchcodec_pybind_ops4.so
__init__.py libtorchcodec_pybind_ops5.so
_internally_replaced_utils.py libtorchcodec_pybind_ops6.so
libtorchcodec_core4.so libtorchcodec_pybind_ops7.so
libtorchcodec_core5.so __pycache__
libtorchcodec_core6.so _samplers
libtorchcodec_core7.so samplers
libtorchcodec_custom_ops4.so version.py
安装ffmpeg
注意ffmpeg只支持4-7版本
使用conda安装
!curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba
!./bin/micromamba shell init -s bash --root-prefix ./micromamba
!./bin/micromamba install "ffmpeg<8" -c conda-forge -y
查看版本
! ffmpeg --version
使用apt安装
!apt update
!apt install ffmpeg -y
查看版本
! ffmpeg --version
诊断环境是否正常
import os
import sys
import platform
import torch
import torch
print(torch.__config__.show())
print("=== PyTorch GPU 支持检查 ===")
print(f"PyTorch 版本: {torch.__version__}")
print(f"CUDA 可用: {torch.cuda.is_available()}")
print(f"CUDA 版本: {torch.version.cuda}")if torch.cuda.is_available():print(f"GPU 设备数量: {torch.cuda.device_count()}")for i in range(torch.cuda.device_count()):print(f"GPU {i}: {torch.cuda.get_device_name(i)}")
else:print("✗ 没有可用的 CUDA GPU")
def deep_diagnosis():print("=== 深度诊断 ===")print(f"Python 版本: {sys.version}")print(f"平台: {platform.system()} {platform.release()}")print(f"os 模块文件: {os.__file__}")# 检查 os 模块的所有属性print(f"os 模块属性数量: {len(dir(os))}")# 检查特定于 Windows 的方法是否存在windows_specific = ['add_dll_directory', 'GetLongPathName', 'GetShortPathName']for method in windows_specific:exists = hasattr(os, method)print(f"os.{method}: {'✓' if exists else '✗'}")# 检查平台标识print(f"sys.platform: {sys.platform}")print(f"os.name: {os.name}")# 检查模块加载路径print(f"os 模块来自: {os.__spec__ if hasattr(os, '__spec__') else 'N/A'}")deep_diagnosis()import os
import sysdef setup_linux_library_path():"""设置 Linux 库路径"""ffmpeg_bin_path = r"/usr/lib/x86_64-linux-gnu"# 找到 PyTorch 库路径try:import torchpytorch_lib_path = os.path.join(os.path.dirname(torch.__file__), 'lib')print(f"PyTorch 库路径: {pytorch_lib_path}")# 添加到 LD_LIBRARY_PATHcurrent_ld_path = os.environ.get('LD_LIBRARY_PATH', '')if pytorch_lib_path not in current_ld_path:os.environ['LD_LIBRARY_PATH'] = pytorch_lib_path + os.pathsep + current_ld_pathprint("✓ 已设置 LD_LIBRARY_PATH")current_ld_path = os.environ.get('LD_LIBRARY_PATH', '') os.environ['LD_LIBRARY_PATH'] = ffmpeg_bin_path + os.pathsep + current_ld_path except ImportError:print("✗ 未找到 PyTorch")# 应用设置
setup_linux_library_path()
print(os.environ['LD_LIBRARY_PATH'])
# 现在尝试加载你的库
import ctypes
try:lib = ctypes.CDLL('/usr/local/lib/python3.11/dist-packages/torchcodec/libtorchcodec_core4.so')print("✓ 库加载成功!")
except Exception as e:print(f"✗ 库加载失败: {e}")
(二)硬件要求
- CPU 环境:普通 4 核及以上 CPU(如 i5 系列)可完成训练,训练 10 轮约 1 - 2 小时;若 CPU 性能较低(如 2 核),时间会相应增加。
- GPU 环境(可选):支持 CUDA 的 NVIDIA GPU(如 GTX 1050 及以上)可加速训练,10 轮训练时间可缩短至 10 - 20 分钟,需额外安装对应 CUDA 版本的 PyTorch。
三、数据集处理
(一)数据集介绍
Speech Commands 数据集包含 train
(训练集 )、validation
(验证集 )、test
(测试集 )三个划分,每个语音片段为 1 秒左右的单词语音,采样率 16000Hz,涵盖 35 个类别,本项目聚焦其中数字 0-9 的识别。
(二)数据集下载与加载
1. 数据集下载函数
import os
import tarfile
import urllib.request
from datasets import load_dataset# Speech Commands 官方下载 URL 模板
DL_URL = "https://s3.amazonaws.com/datasets.huggingface.co/SpeechCommands/{version}/{version}_{split}.tar.gz"
# 支持的版本
VERSIONS = ["v0.01", "v0.02"]def download_and_extract(version="v0.02", data_dir="./speech_commands"):"""自动下载并解压 Speech Commands 数据集"""os.makedirs(data_dir, exist_ok=True)splits = ["train", "validation", "test"]extracted_dirs = {}for split in splits:url = DL_URL.format(version=version, split=split)filename = os.path.join(data_dir, f"{version}_{split}.tar.gz")extract_dir = os.path.join(data_dir, f"{version}_{split}")if not os.path.exists(extract_dir):print(f"⬇️ Downloading {url} ...")urllib.request.urlretrieve(url, filename)print(f"📦 Extracting {filename} ...")with tarfile.open(filename, "r:gz") as tar:tar.extractall(extract_dir)extracted_dirs[split] = extract_dirreturn extracted_dirs
函数说明:
- 根据
version
(默认v0.02
)和data_dir
(默认./speech_commands
),自动下载train
、validation
、test
三个划分的数据集压缩包。 - 检查本地是否已解压,未解压则先下载再用
tarfile
解压,返回各划分的解压目录。
2. 数据集加载函数
def load_speech_commands(version="v0.02", data_dir="./speech_commands"):"""返回 HuggingFace DatasetDict(train/validation/test)"""extracted_dirs = download_and_extract(version, data_dir)dataset = {split: load_dataset("audiofolder", data_dir=extracted_dirs[split], split="train")for split in extracted_dirs}return dataset
函数说明:利用 datasets
库的 load_dataset
,以 audiofolder
方式加载各划分解压目录的音频数据,返回包含 train
、validation
、test
数据集的字典。
3. 主程序调用与数据集筛选
if __name__ == "__main__":# 加载完整数据集dataset = load_speech_commands("v0.02")print("完整训练集信息:", dataset["train"])print("完整验证集信息:", dataset["validation"])print("完整测试集信息:", dataset["test"])# 只筛选数字 0-9 的类别digits = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}digits_train = dataset["train"].filter(lambda x: x["label"] in digits)digits_validation = dataset["validation"].filter(lambda x: x["label"] in digits)digits_test = dataset["test"].filter(lambda x: x["label"] in digits)print("数字训练集信息:", digits_train)print("数字验证集信息:", digits_validation)print("数字测试集信息:", digits_test)
代码说明:
- 先加载完整数据集,打印各划分基本信息。
- 通过
filter
方法,依据label
字段筛选出数字 0-9 对应的语音数据,构建数字识别专用数据集。
(三)数据预处理(特征提取)
1. 定义预处理函数
import torch
import torchaudio.transforms as T# 统一采样率
sample_rate = 16000
# 定义梅尔频谱图转换器
mel_transform = T.MelSpectrogram(sample_rate=sample_rate,n_fft=1024, n_mels=64, hop_length=256
)def preprocess_function(examples):"""预处理函数:将音频转换为梅尔频谱图并标准化参数:examples: 数据集样本,包含音频数据等字段返回:处理后的梅尔频谱图(mel)和标签(labels)"""# 提取音频张量,若数据集音频存储格式不同,需调整获取方式wavs = [torch.tensor(wav["array"], dtype=torch.float32) for wav in examples["audio"]] # 统一采样率(若原始采样率不同,需更完善处理,这里假设原始为 16000Hz 简单示例)resampler = T.Resample(sample_rate, sample_rate) wavs = [resampler(wav) for wav in wavs]# 提取梅尔频谱图,增加通道维度mels = [mel_transform(wav).unsqueeze(0) for wav in wavs] # 标准化:计算整体均值和标准差mel_cat = torch.cat(mels)mel_mean = torch.mean(mel_cat)mel_std = torch.std(mel_cat)mels = [(mel - mel_mean) / mel_std for mel in mels]return {"mel": mels, "labels": examples["label"]}
函数说明:
- 从数据集中提取音频张量,统一采样率(实际需根据原始数据调整,这里简化处理 )。
- 利用
MelSpectrogram
将音频转换为梅尔频谱图,增加通道维度适配模型输入。 - 对频谱图进行标准化,提升模型训练稳定性。
2. 应用预处理
# 对数字数据集应用预处理
digits_train = digits_train.map(preprocess_function,batched=True,batch_size=32,remove_columns=digits_train.column_names
)
digits_validation = digits_validation.map(preprocess_function,batched=True,batch_size=32,remove_columns=digits_validation.column_names
)
digits_test = digits_test.map(preprocess_function,batched=True,batch_size=32,remove_columns=digits_test.column_names
)# 转换为 PyTorch 张量格式
digits_train.set_format("torch", columns=["mel", "labels"])
digits_validation.set_format("torch", columns=["mel", "labels"])
digits_test.set_format("torch", columns=["mel", "labels"])
代码说明:
- 以批次方式(
batch_size=32
)对数字数据集各划分应用预处理,移除原始无关字段。 - 将处理后数据集转换为 PyTorch 张量格式,方便后续模型训练。
四、模型构建
(一)模型架构选择(CNN + LSTM 混合模型)
import torch.nn as nnclass SpeechRecognitionModel(nn.Module):def __init__(self, num_classes=10):"""初始化模型参数:num_classes: 分类数量,数字 0-9 共 10 类"""super().__init__()# 1. 卷积层:提取局部频率特征self.cnn = nn.Sequential(nn.Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),nn.ReLU(),nn.MaxPool2d(kernel_size=(2, 2)), nn.Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)),nn.ReLU(),nn.MaxPool2d(kernel_size=(2, 2)) )# 2. LSTM 层:处理时间序列特征self.lstm = nn.LSTM(input_size=64 * 16, hidden_size=128, num_layers=2, batch_first=True )# 3. 分类头:输出分类概率self.fc = nn.Linear(128, num_classes)def forward(self, x):"""前向传播参数:x: 输入的梅尔频谱图,形状 [batch, 1, 64, time]返回:分类预测结果"""# CNN 处理x = self.cnn(x) batch_size, channels, freq, time = x.shape# 调整形状适配 LSTM 输入:[batch, time, channels×freq]x = x.permute(0, 3, 1, 2).reshape(batch_size, time, channels * freq)# LSTM 处理x, _ = self.lstm(x) # 取最后一个时间步特征x = x[:, -1, :] # 分类输出logits = self.fc(x) return logits# 初始化模型,数字识别共 10 类
model = SpeechRecognitionModel(num_classes=10)
模型说明:
- CNN 部分:通过两层卷积提取音频频谱的局部特征,池化层压缩维度,减少计算量。
- LSTM 部分:处理 CNN 输出的时间序列特征,捕捉语音的时序依赖关系。
- 分类头:将 LSTM 输出映射到 10 个数字类别的预测概率。
这个语音识别模型使用LSTM是非常关键且合理的设计,主要原因如下:
-
语音数据的时序特性
语音是时间序列数据:音频信号本质上是随时间变化的连续信号 -
前后依赖关系:一个音素的识别依赖于前面的音素和后面的音素
-
上下文信息:数字发音中,各个部分之间存在强关联
五、模型训练与评估
(一)训练参数设置
# 训练参数
batch_size = 32
lr = 1e-3 # 学习率
epochs = 10 # 训练轮数# 数据加载器
train_loader = torch.utils.data.DataLoader(digits_train, batch_size=batch_size, shuffle=True)
validation_loader = torch.utils.data.DataLoader(digits_validation, batch_size=batch_size)
test_loader = torch.utils.data.DataLoader(digits_test, batch_size=batch_size)# 损失函数(交叉熵,适合分类任务)
criterion = nn.CrossEntropyLoss()
# 优化器(Adam,自适应调整学习率)
optimizer = optim.Adam(model.parameters(), lr=lr)# 设备设置,自动检测 GPU/CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
(二)训练循环
import torch.optim as optimfor epoch in range(epochs):# 训练模式:启用 dropout、批量归一化等训练专属行为model.train() train_loss = 0.0for batch in train_loader:# 加载数据到设备(GPU/CPU)mels = batch["mel"].to(device) labels = batch["labels"].to(device)# 梯度清零optimizer.zero_grad() # 前向传播outputs = model(mels) # 计算损失loss = criterion(outputs, labels) # 反向传播计算梯度loss.backward() # 更新模型参数optimizer.step() train_loss += loss.item() * mels.size(0)# 计算训练集平均损失train_loss /= len(train_loader.dataset)# 验证集评估:切换为评估模式,关闭 dropout 等model.eval() validation_loss = 0.0correct = 0with torch.no_grad(): for batch in validation_loader:mels = batch["mel"].to(device)labels = batch["labels"].to(device)outputs = model(mels)loss = criterion(outputs, labels)validation_loss += loss.item() * mels.size(0)# 取预测类别_, preds = torch.max(outputs, 1) correct += torch.sum(preds == labels.data)validation_loss /= len(validation_loader.dataset)validation_acc = correct.double() / len(validation_loader.dataset)# 打印训练轮次日志print(f"Epoch {epoch+1}/{epochs}")print(f"Train Loss: {train_loss:.4f} | Validation Loss: {validation_loss:.4f} | Validation Acc: {validation_acc:.4f}")
流程说明:
- 训练阶段:迭代训练集,计算损失、反向传播更新模型参数,累计训练损失。
- 验证阶段:切换模型为评估模式,关闭梯度计算,计算验证集损失与准确率,评估模型泛化能力。
(三)测试集评估
# 测试集评估
model.eval()
test_loss = 0.0
correct = 0
with torch.no_grad():for batch in test_loader:mels = batch["mel"].to(device)labels = batch["labels"].to(device)outputs = model(mels)loss = criterion(outputs, labels)test_loss += loss.item() * mels.size(0)_, preds = torch.max(outputs, 1)correct += torch.sum(preds == labels.data)test_loss /= len(test_loader.dataset)
test_acc = correct.double() / len(test_loader.dataset)
print(f"测试集结果:Loss={test_loss:.4f}, Acc={test_acc:.4f}")
说明:在测试集上执行与验证集类似的评估流程,得到模型最终的泛化性能指标(损失与准确率 )。
六、模型部署与演示(Gradio 交互界面)
(一)保存模型(可选)
# 保存训练好的模型
torch.save(model.state_dict(), "speech_recognition_model.pth")
(二)构建交互界面
import gradio as gr# 加载模型(若之前保存过,可直接加载;也可使用训练好的模型实例)
# model.load_state_dict(torch.load("speech_recognition_model.pth", map_location=device))
model.eval()def recognize_speech(audio):"""语音识别函数,对接 Gradio 界面参数:audio: Gradio 传入的音频数据,格式为 (采样率, 波形数组)返回:识别结果文本"""if audio is None:return "请录制或上传语音"sr, wav = audiowav = torch.tensor(wav, dtype=torch.float32)# 预处理(与训练时一致)resampler = T.Resample(sr, sample_rate)wav = resampler(wav)mel = mel_transform(wav).unsqueeze(0).unsqueeze(0) # 增加批次和通道维度# 注意:需使用训练时的均值和标准差,这里需提前保存并加载,示例简化处理mel_mean = torch.mean(torch.cat([batch["mel"] for batch in train_loader])) mel_std = torch.std(torch.cat([batch["mel"] for batch in train_loader])) mel
七、数据集大小、训练时长、GPU 需求
(一)、数据集大小(以 Speech Commands v0.02 为例)
1. 原始数据规模
- 总样本量:约 10 万条语音(
v0.02
版本含 35 个类别,每个类别约几千条) - 单条语音:1 秒时长,采样率 16000Hz → 单条音频大小约
16000 * 2 bytes = 32KB
(PCM 16 位编码) - 总容量:plaintext
10万条 * 32KB ≈ 3.2GB(未压缩)
实际下载为 tar.gz 压缩包,解压后约 2GB 左右
2. 数字子集(0-9)规模
- 样本量:每个数字约 4000-5000 条 → 总约 4-5 万条
- 容量:约
4万 * 32KB ≈ 1.28GB
(解压后)
(二)、训练时长估算
训练时长受 模型复杂度、batch size、GPU 性能 影响极大,以下分场景对比:
1. 模型复杂度(以你的 CNN+LSTM 为例)
- 参数量:约 100 万参数(轻量级模型,适合入门)
- 计算量:每轮训练需处理约 5 万条样本(数字子集),属于低计算量任务
2. 不同 GPU 训练时长(数字子集,10 轮)
GPU 型号 | 典型场景 | 单轮时长 | 10 轮总时长 | 显存需求 |
---|---|---|---|---|
无 GPU(纯 CPU) | 4 核笔记本 CPU | 30-60 分钟 | 5-10 小时 | — |
NVIDIA GTX 1060 | 入门游戏显卡 | 3-5 分钟 | 30-50 分钟 | 2-3GB |
NVIDIA RTX 3060 | 中端显卡 | 1-2 分钟 | 10-20 分钟 | 3-4GB |
NVIDIA A100 | 数据中心显卡 | 10-20 秒 | 2-3 分钟 | 5-6GB |
关键影响因素
- batch size:越大越快,但受显存限制(你的代码用
batch_size=32
,很保守) - 数据增强:若添加音频加噪、变速等,时长会增加 20%-50%
- 模型深度:若换成 Wav2Vec 等大模型,时长会飙升 10 倍以上
(三)、GPU 需求与选型建议
1. 最低需求:无需 GPU
- 纯 CPU 可跑:你的轻量级模型 + 小 batch size(32),4 核 CPU 能跑通(只是慢)
- 适合场景:仅想验证流程、样本量极小(<1 万条)
2. 推荐 GPU:千元级显卡足够
- 性价比之选:GTX 1060 / RTX 3050(2000 元以内二手卡)
- 显存 6GB,能轻松容纳
batch_size=128
(比 32 快 3 倍) - 10 轮训练仅需 30 分钟,适合个人学习
- 显存 6GB,能轻松容纳
3. 企业级需求:按需扩容
- 大模型 / 大数据:A100(40GB 显存)或多卡集群
- 但你的场景不需要:数字识别任务简单,千元卡足够
(四)、完整训练成本总结
需求类型 | 硬件建议 | 训练时长(10 轮) | 成本(硬件 / 云 GPU) |
---|---|---|---|
纯体验(能跑通) | 4 核 CPU | 5-10 小时 | 0(已有电脑) |
快速验证 | GTX 1060(二手) | 30-50 分钟 | 1500 元(显卡) |
极致效率 | RTX 3060 | 10-20 分钟 | 3000 元(显卡)/ 10 元(云 GPU 按次) |