如何用多线程优化librosa/opensmile/essentia音频特征提取?
音频特征提取优化方案(针对4303个30秒WAV文件)
一、基础串行代码优化(快速降低耗时)
这些修改无需并行,就能大幅压缩单文件处理时间:
- 重复初始化对象移到循环外:每次循环创建
opensmile.Smile、es.GFCC会产生额外开销,把初始化逻辑放到循环前:
# 循环前只初始化一次,复用对象 smile = opensmile.Smile(feature_set=opensmile.FeatureSet.eGeMAPSv02) run_gfcc = es.GFCC(numberCoefficients=12)
- 单文件只加载一次:当前每个音频被3个库分别读取,改成一次加载后共享数据:
file_path = os.path.join(path, f) # 用librosa加载原始音频,再转换为其他库兼容格式 y_, sr = librosa.load(file_path, sr=None) # 保留原采样率 audio = essentia.array(y_) # 转换为essentia支持的数组格式 # opensmile直接处理numpy数组,避免重复读文件 y = smile.process_signal(y_, sr)
- 延迟DataFrame创建:循环内每次调用
pd.json_normalize会随着列表增大越来越慢,改成循环结束后一次性生成:
# 循环内仅收集特征到列表 voiceFeatures.append(features) # 循环结束后再生成DataFrame df = pd.json_normalize(voiceFeatures)
二、Colab多进程并行处理
Colab免费版提供2核CPU,用多进程可以并行处理多个文件,直接翻倍处理速度:
import pandas as pd import numpy as np import os import re import opensmile import essentia import essentia.standard as es import librosa from multiprocessing import Pool, cpu_count def process_single_file(f): # 每个子进程内初始化对象(避免多进程序列化问题) smile = opensmile.Smile(feature_set=opensmile.FeatureSet.eGeMAPSv02) run_gfcc = es.GFCC(numberCoefficients=12) file_path = os.path.join(path, f) y_, sr = librosa.load(file_path, sr=None) audio = essentia.array(y_) # 提取opensmile特征 y = smile.process_signal(y_, sr) # 提取GFCC特征 gfccs = run_gfcc(audio) # 提取MFCC特征 mfcc = librosa.feature.mfcc(y=y_, sr=sr, n_mfcc=16) # 组装特征字典(和原逻辑一致) features = { "title": f, "f1Mean": y.F1frequency_sma3nz_amean[0], "f1STD": y.F1frequency_sma3nz_stddevNorm[0], "f1BandMean": y.F1bandwidth_sma3nz_amean[0], "f1BandSTD": y.F1bandwidth_sma3nz_stddevNorm[0], "f2Mean": y.F2frequency_sma3nz_amean[0], "f2STD": y.F2frequency_sma3nz_stddevNorm[0], "f2BandMean": y.F2bandwidth_sma3nz_amean[0], "f2BandSTD": y.F2bandwidth_sma3nz_stddevNorm[0], "f3Mean": y.F3frequency_sma3nz_amean[0], "f3STD": y.F3frequency_sma3nz_stddevNorm[0], "f3BandMean": y.F3bandwidth_sma3nz_amean[0], "f3BandSTD": y.F3bandwidth_sma3nz_stddevNorm[0], "voicedMean": y.MeanVoicedSegmentLengthSec[0], "voiceSTD": y.StddevVoicedSegmentLengthSec[0], "unvoicedMean": y.MeanUnvoicedSegmentLength[0], "unvoicedSTD": y.StddevUnvoicedSegmentLength[0], "f0Mean": y['F0semitoneFrom27.5Hz_sma3nz_amean'][0], "f0STD": y['F0semitoneFrom27.5Hz_sma3nz_stddevNorm'][0], "hnrMean": y.HNRdBACF_sma3nz_amean[0], "hnrSTD": y.HNRdBACF_sma3nz_stddevNorm[0], "jitterMean": y.jitterLocal_sma3nz_amean[0], "jitterSTD": y.jitterLocal_sma3nz_stddevNorm[0], "shitterMean": y.shimmerLocaldB_sma3nz_amean[0], "shitterSTD": y.shimmerLocaldB_sma3nz_stddevNorm[0], "gfccsMean": np.mean(gfccs[1]), "gfccsSTD": np.std(gfccs[1]), } # 批量添加MFCC特征,避免重复代码 for i in range(16): features[f"mfcc{i+1}Mean"] = np.mean(mfcc[i]) features[f"mfcc{i+1}STD"] = np.std(mfcc[i]) return features if __name__ == "__main__": path = '/content/drive/MyDrive/vocal' files = os.listdir(path) files.sort(key=lambda f: int(re.sub('\D', '', f))) # 使用1核(Colab免费版2核,留1核给系统) with Pool(1) as pool: voiceFeatures = pool.map(process_single_file, files) # 生成并保存结果到Drive df = pd.json_normalize(voiceFeatures) df.to_csv('/content/drive/MyDrive/audio_features.csv', index=False)
- 注意:如果遇到多进程序列化错误,就把对象初始化放到子进程函数内,虽然有一点开销,但总比串行快。
三、GPU加速尝试(效果视场景而定)
音频特征提取的GPU加速对短音频提升有限,但可以尝试:
- librosa GPU支持:安装GPU版本的librosa,部分特征会自动使用GPU:
!pip install librosa-gpu
- essentia GPU版本:安装带TensorFlow GPU支持的essentia,适合批量处理:
!pip install essentia-tensorflow-gpu
- 提示:30秒短音频的GPU加速可能被数据传输开销抵消,建议优先用多进程。
四、避免Colab会话中断的方案
- 定时保存中间结果:分批次处理并保存,避免会话中断丢失全部数据:
batch_size = 200 voiceFeatures = [] path = '/content/drive/MyDrive/vocal' files = os.listdir(path) files.sort(key=lambda f: int(re.sub('\D', '', f))) for i in range(0, len(files), batch_size): batch_files = files[i:i+batch_size] with Pool(1) as pool: batch_features = pool.map(process_single_file, batch_files) voiceFeatures.extend(batch_features) # 每批保存一次临时文件 pd.json_normalize(voiceFeatures).to_csv('/content/drive/MyDrive/features_temp.csv', index=False) # 最终保存完整结果 df = pd.json_normalize(voiceFeatures) df.to_csv('/content/drive/MyDrive/audio_features.csv', index=False)
- 会话保持技巧:在浏览器控制台运行以下代码,每60秒点击一次连接按钮,防止超时:
function ClickConnect(){console.log("Clicked");document.querySelector("colab-connect-button").click()}setInterval(ClickConnect,60000)
内容的提问来源于stack exchange,提问作者PM92
相关产品推荐
相关产品推荐

