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

如何用LSTM神经网络处理三维分类输入输出实现Keras音符预测?

我来帮你一步步搞定用LSTM预测音符序列的问题!针对你这种每个音符包含三个分类特征的场景,我们可以从数据预处理、数据集格式化、模型搭建与训练这几个核心环节入手,下面是具体的代码和思路解析:


一、先把原始数据解析成结构化格式

首先需要把你给出的文本格式数据,转换成模型能理解的结构化数组。这里写了一个简单的解析函数,你可以直接适配自己的数据集:

import numpy as np
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, LSTM, Dense, Embedding, Concatenate
from tensorflow.keras.utils import to_categorical
from sklearn.preprocessing import LabelEncoder

# 你的原始数据示例(实际使用时可以替换成文件读取逻辑)
raw_data = [
    "1 [A,2,#] [B,3,b] [C,4,#] [A,2,b] | [B,3,b]",
    "2 [B,2,#] [B,2,b] [B,5,#] [A,2,#] | [A,3,#]",
    "3 [C,2,#] [B,5,#] [A,4,#] [A,1,#] | [A,8,b]"
]

def parse_raw_data(raw_data):
    sequences = []
    targets = []
    for line in raw_data:
        # 分割输入序列和目标音符
        input_part, target_part = line.split("|")
        # 提取输入的4个音符(跳过开头的序号)
        input_notes = input_part.strip().split()[1:]
        # 解析每个音符的三个特征
        parsed_input = []
        for note_str in input_notes:
            features = note_str.strip("[]").split(",")
            parsed_input.append([f.strip() for f in features])
        # 解析目标音符
        target_note = target_part.strip().strip("[]").split(",")
        target_note = [f.strip() for f in target_note]
        sequences.append(parsed_input)
        targets.append(target_note)
    return np.array(sequences, dtype=object), np.array(targets, dtype=object)

# 得到结构化的输入序列X和目标音符y
X, y = parse_raw_data(raw_data)

二、对分类特征进行编码

你的三个特征(音高、八度、升降号)都是分类变量,不能直接输入神经网络,需要先转换成数字编码。这里用LabelEncoder把每个特征的字符串映射成数字,后续可以通过嵌入层或独热编码处理:

# 收集所有特征的取值,用于拟合编码器
all_pitches = []
all_octaves = []
all_accidentals = []

for seq in X:
    for note in seq:
        all_pitches.append(note[0])
        all_octaves.append(note[1])
        all_accidentals.append(note[2])
for target in y:
    all_pitches.append(target[0])
    all_octaves.append(target[1])
    all_accidentals.append(target[2])

# 初始化并拟合编码器
le_pitch = LabelEncoder()
le_pitch.fit(all_pitches)
le_octave = LabelEncoder()
le_octave.fit(all_octaves)
le_accidental = LabelEncoder()
le_accidental.fit(all_accidentals)

# 获取每个特征的类别数量
num_pitches = len(le_pitch.classes_)
num_octaves = len(le_octave.classes_)
num_accidentals = len(le_accidental.classes_)

三、构建LSTM所需的输入输出格式

LSTM要求输入形状为(样本数, 时间步长, 特征维度),这里时间步长是你输入的音符序列长度(4个)。推荐用多输入嵌入层的方式,对每个分类特征单独做嵌入,能更好地捕捉分类特征的语义:

# 转换输入数据:每个特征对应一个序列数组
X_pitch = le_pitch.transform(X.reshape(-1, 1)[:,0]).reshape(X.shape[0], X.shape[1])
X_octave = le_octave.transform(X.reshape(-1, 1)[:,1]).reshape(X.shape[0], X.shape[1])
X_accidental = le_accidental.transform(X.reshape(-1, 1)[:,2]).reshape(X.shape[0], X.shape[1])

# 转换目标数据:每个特征单独作为一个输出(多任务预测)
y_pitch = to_categorical(le_pitch.transform(y[:,0]), num_classes=num_pitches)
y_octave = to_categorical(le_octave.transform(y[:,1]), num_classes=num_octaves)
y_accidental = to_categorical(le_accidental.transform(y[:,2]), num_classes=num_accidentals)

四、搭建并训练LSTM模型

因为要预测下一个音符的三个特征,模型采用多输出头结构,每个输出头对应一个特征的预测,损失函数用交叉熵,优化器选用Adam:

# 定义每个特征的输入层
input_pitch = Input(shape=(4,))
input_octave = Input(shape=(4,))
input_accidental = Input(shape=(4,))

# 嵌入层:将分类特征映射到低维向量空间
emb_pitch = Embedding(input_dim=num_pitches, output_dim=8)(input_pitch)
emb_octave = Embedding(input_dim=num_octaves, output_dim=4)(input_octave)
emb_accidental = Embedding(input_dim=num_accidentals, output_dim=2)(input_accidental)

# 拼接三个嵌入后的特征序列
concat_features = Concatenate(axis=-1)([emb_pitch, emb_octave, emb_accidental])

# LSTM层捕捉序列依赖关系
lstm_layer = LSTM(64, return_sequences=False)(concat_features)

# 三个输出头,分别预测三个特征
output_pitch = Dense(num_pitches, activation='softmax', name='pitch')(lstm_layer)
output_octave = Dense(num_octaves, activation='softmax', name='octave')(lstm_layer)
output_accidental = Dense(num_accidentals, activation='softmax', name='accidental')(lstm_layer)

# 构建模型
model = Model(inputs=[input_pitch, input_octave, input_accidental], 
              outputs=[output_pitch, output_octave, output_accidental])

# 编译模型
model.compile(optimizer='adam',
              loss={'pitch': 'categorical_crossentropy',
                    'octave': 'categorical_crossentropy',
                    'accidental': 'categorical_crossentropy'},
              metrics={'pitch': 'accuracy',
                       'octave': 'accuracy',
                       'accidental': 'accuracy'})

# 查看模型结构
model.summary()

# 训练模型
history = model.fit([X_pitch, X_octave, X_accidental],
                    [y_pitch, y_octave, y_accidental],
                    epochs=50,
                    batch_size=1)

五、用训练好的模型做预测

训练完成后,输入一个4音符的序列,解码模型的预测结果即可得到下一个音符:

# 取第一个样本作为测试输入
test_seq = X[0]

# 对测试序列进行编码
test_pitch = le_pitch.transform(test_seq[:,0]).reshape(1, 4)
test_octave = le_octave.transform(test_seq[:,1]).reshape(1, 4)
test_accidental = le_accidental.transform(test_seq[:,2]).reshape(1, 4)

# 执行预测
preds = model.predict([test_pitch, test_octave, test_accidental])

# 解码预测结果
pred_pitch = le_pitch.inverse_transform(np.argmax(preds[0], axis=1))[0]
pred_octave = le_octave.inverse_transform(np.argmax(preds[1], axis=1))[0]
pred_accidental = le_accidental.inverse_transform(np.argmax(preds[2], axis=1))[0]

print(f"预测的下一个音符:[{pred_pitch}, {pred_octave}, {pred_accidental}]")

一些优化建议

  1. 如果你的数据集很大,建议改成从文件(如CSV、TXT)批量读取数据,避免内存溢出;
  2. 可以调整LSTM的单元数量(比如64→128)、堆叠多层LSTM(设置return_sequences=True)来提升模型能力;
  3. 如果分类特征的类别较少,也可以用独热编码替代嵌入层,示例代码如下:
# 独热编码方式的输入转换
def encode_note(note):
    pitch_onehot = to_categorical(le_pitch.transform([note[0]]), num_pitches)[0]
    octave_onehot = to_categorical(le_octave.transform([note[1]]), num_octaves)[0]
    accidental_onehot = to_categorical(le_accidental.transform([note[2]]), num_accidentals)[0]
    return np.concatenate([pitch_onehot, octave_onehot, accidental_onehot])

X_onehot = np.array([[encode_note(note) for note in seq] for seq in X])

# 独热编码版本的模型
input_layer = Input(shape=(4, X_onehot.shape[2]))
lstm = LSTM(64)(input_layer)
output_pitch = Dense(num_pitches, activation='softmax')(lstm)
output_octave = Dense(num_octaves, activation='softmax')(lstm)
output_accidental = Dense(num_accidentals, activation='softmax')(lstm)
model = Model(inputs=input_layer, outputs=[output_pitch, output_octave, output_accidental])

内容的提问来源于stack exchange,提问作者Someone Needing Help

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:38:18