You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

吉他和弦梅尔频谱图转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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 12:53:14