吉他和弦梅尔频谱图转NumPy数组遇形状不匹配报错求助
解决梅尔频谱图形状不匹配无法转NumPy数组的问题
问题根源
你遇到的错误是因为不同音频文件的时长不一致,导致生成的梅尔频谱图时间维度(第二个轴)长度不同。比如有的频谱图是(128,98),有的可能是(128, 105)这类其他长度,NumPy无法将这种不规则的嵌套列表转换为统一形状的数组,因此抛出广播错误。
解决方案
下面是三种实用的解决方法,按场景选择:
1. 统一频谱图的时间维度长度(截断/补零)
先遍历所有生成的频谱图,找到最大的时间步长,然后对每个频谱图进行截断(超过最大长度时取前N步)或补零(不足时在末尾补0),确保所有样本形状一致。
import numpy as np # 计算所有频谱图的最大时间步长 max_time_steps = max([spec.shape[1] for spec in X]) # 统一所有频谱图的形状 X_processed = [] for spec in X: current_steps = spec.shape[1] if current_steps < max_time_steps: # 补零:在时间维度末尾补0,保持特征维度(128)不变 padded_spec = np.pad(spec, ((0, 0), (0, max_time_steps - current_steps)), mode='constant') X_processed.append(padded_spec) else: # 截断:只保留前max_time_steps个时间步 truncated_spec = spec[:, :max_time_steps] X_processed.append(truncated_spec) # 转换为统一形状的NumPy数组 X = np.array(X_processed)
如果不想用最大长度,也可以直接设定一个固定的目标时间步(比如根据大部分音频的时长设定为200),这样更可控。
2. 加载音频时统一截取固定时长
从源头上解决问题:加载音频时直接截取固定时长的片段,这样生成的频谱图自然形状一致。比如统一截取前2秒的音频:
import librosa import os import numpy as np fixed_duration = 2 # 固定截取2秒音频 for g in genres: genre_path = os.path.join(data_path, g) for filename in os.listdir(genre_path): song_path = os.path.join(genre_path, filename) print(f"{song_path}") y.append(g) X_song, sr = librosa.load(song_path) # 计算固定时长对应的采样点数量 fixed_samples = int(sr * fixed_duration) # 统一音频长度 if len(X_song) > fixed_samples: X_song = X_song[:fixed_samples] else: X_song = np.pad(X_song, (0, fixed_samples - len(X_song)), mode='constant') # 生成梅尔频谱图和分贝谱 spectrogram = librosa.feature.melspectrogram(y=X_song, sr=sr, n_fft=2048, hop_length=512) db_spectrogram = librosa.power_to_db(spectrogram, ref=np.max) X.append(db_spectrogram) # 此时X中的所有元素形状一致,可直接转NumPy数组 X = np.array(X)
这种方法能避免后续处理,适合对音频时长要求不高的分类任务。
3. 使用支持可变长度输入的模型
如果需要保留音频的完整时长信息,可以使用RNN(LSTM/GRU)或Transformer这类支持可变序列长度的模型,配合Masking层忽略补零部分的影响:
from keras.models import Sequential from keras.layers import Masking, LSTM, Dense # 先对X进行补零处理(参考方法1) X = np.array(X_processed) # 调整输入形状为(时间步, 特征数)(LSTM通常要求这种输入格式) X = np.transpose(X, (0, 2, 1)) model = Sequential() # 添加Masking层,忽略值为0的补零部分 model.add(Masking(mask_value=0., input_shape=(None, 128))) model.add(LSTM(64)) model.add(Dense(len(genres), activation='softmax')) # 后续编译、训练代码...
这种方法复杂度较高,适合需要利用完整时序信息的场景。
内容的提问来源于stack exchange,提问作者Biff
相关产品推荐
相关产品推荐

