如何通过Librosa STFT获取相同帧数的数组以构建数据集?
问题:统一音频频谱图数组形状以构建数据集
我正尝试构建由WAV文件生成的Numpy数组数据集,已将单个WAV文件转换为Numpy数组,但因数组形状略有差异(第一维度一致,第二维度即帧数不同),无法合并为单个Numpy数组。我使用librosa.stft进行WAV到Numpy的转换,目前想到的方法是为每个数组补零以匹配最大帧数,但这种方法需要遍历3000个数组的每一行,效率极低,肯定有更好的方法。
预处理代码
def process(directory): # Load file for file in os.listdir(directory): f = os.path.join(directory, file).replace("\\", r"/") x, sr = librosa.load(f, sr=22050) # Convert to spectrogram X = librosa.stft(x) print(X.shape) spectrogram = np.abs(X) # Save file np.save(f"C:/Python/GANs/Sound_Digit_Gan/Data2/{file}", spectrogram) process(directory)
打印出的X形状示例:
(1025, 23) (1025, 29) (1025, 26) (1025, 34) (1025, 26) (1025, 27) (1025, 40) (1025, 22) (1025, 25) (1025, 24) (1025, 35)
合并数据集时的错误
尝试用以下代码创建训练数据集:
directory = "C:/Python/GANs/Sound_Digit_Gan/Data2/" train = [] for f in os.listdir(directory): file = os.path.join(directory, f).replace("\\", r"/") array = np.load(file) train.append(array) train = np.array(train)
出现错误:
ValueError: could not broadcast input array from shape (1025,13) into shape (1025,)
我认为这是因为数组形状不一致导致的,请问如何在不丢失音频信号数据的前提下统一数组形状?
解决方案
方法一:预处理阶段直接统一形状(推荐)
不用先保存单个数组再处理,而是在生成频谱图时直接补零到目标长度,步骤如下:
- 先遍历所有音频文件,计算出最大的帧数(第二维度的最大值)
- 重新遍历文件,生成频谱图后用
np.pad补零到最大帧数,再保存
代码示例:
import os import librosa import numpy as np def get_max_frames(directory, sr=22050): max_frames = 0 for file in os.listdir(directory): f = os.path.join(directory, file).replace("\\", r"/") x, sr = librosa.load(f, sr=sr) X = librosa.stft(x) current_frames = X.shape[1] if current_frames > max_frames: max_frames = current_frames return max_frames def process_with_pad(directory, save_dir, max_frames): for file in os.listdir(directory): f = os.path.join(directory, file).replace("\\", r"/") x, sr = librosa.load(f, sr=22050) X = librosa.stft(x) spectrogram = np.abs(X) # 补零:只在第二维度后面补零到max_frames长度 padded_spectrogram = np.pad(spectrogram, ((0,0), (0, max_frames - spectrogram.shape[1])), mode='constant') np.save(os.path.join(save_dir, file), padded_spectrogram) # 使用流程 source_dir = "你的WAV文件目录" save_dir = "C:/Python/GANs/Sound_Digit_Gan/Data2/" max_frames = get_max_frames(source_dir) process_with_pad(source_dir, save_dir, max_frames)
方法二:对已保存的数组批量补零
如果已经保存了单个数组,也可以高效批量处理,不用逐行遍历:
- 先加载所有数组,记录最大帧数
- 创建一个形状为
(样本数, 1025, max_frames)的空数组 - 遍历每个数组,将其赋值到空数组的对应位置,剩余部分自动补零
代码示例:
import os import numpy as np directory = "C:/Python/GANs/Sound_Digit_Gan/Data2/" arrays = [] max_frames = 0 # 第一步:加载所有数组并找到最大帧数 for f in os.listdir(directory): file_path = os.path.join(directory, f).replace("\\", r"/") arr = np.load(file_path) arrays.append(arr) if arr.shape[1] > max_frames: max_frames = arr.shape[1] # 第二步:创建统一形状的数据集 sample_count = len(arrays) train_data = np.zeros((sample_count, 1025, max_frames), dtype=np.float32) for i, arr in enumerate(arrays): # 将原数组赋值到train_data的对应位置,剩余部分保持0 train_data[i, :, :arr.shape[1]] = arr # 现在train_data就是统一形状的数据集了 print(train_data.shape) # 输出 (3000, 1025, max_frames)
关键说明
np.pad或者直接赋值到空数组的方式都是向量化操作,numpy内部会用C级别的循环处理,完全不用担心3000个样本的性能问题- 补零操作是在频谱图的时间维度(第二维度)末尾补零,不会丢失原有音频的信号数据,只是添加了无意义的静音帧,对后续模型训练影响极小
- 如果不想补零,也可以考虑截断较长的音频到最短帧数,但这样会丢失部分数据,不推荐;或者使用可变长度输入的模型(比如RNN类),但对于GAN这类模型,固定输入形状会更方便
内容的提问来源于stack exchange,提问作者Conweezy
相关产品推荐
相关产品推荐

