我想做的就是将一组数字输入到我的LSTM模型中,然后让LSTM模型输出它自己的数字列表。我的项目是一个程序,它从网上获取MIDI文件,将其转换成数字列表,从LSTM模型中获取新的数字列表,将这些新数字转换成MIDI,然后听这个文件。我遇到问题的地方是从LSTM模型中获取新的数字列表。
这是我目前的主要代码:
from midi_to_text import data_parsefrom split_sequence import split_sequenceimport py_midicsv as pmimport mathfrom numpy import asarrayfrom tensorflow.keras import Sequentialfrom tensorflow.keras.layers import *import tensorflow as tfraw_midi = pm.midi_to_csv('OnlineMidi.mid')data = data_parse(raw_midi)n_steps = 1X, y = split_sequence(data, n_steps)X = X.reshape((X.shape[0], X.shape[1], 1))X = tf.cast(X, dtype='float32')model = Sequential()model.add(LSTM(256, activation='sigmoid', return_sequences=True))model.add(Dropout(0.2))model.add(LSTM(128, activation='sigmoid', return_sequences=True))model.add(Dropout(0.2))model.add(LSTM(128))model.add(Dropout(0.2))model.add(Dense(1, activation='linear'))model.compile(optimizer='adam', loss='mse', metrics=['mae'])model.fit(X, y, epochs=100, batch_size=32, verbose=2)notes = [64]song_length = 10for i in range(song_length): prediction = model.predict(asarray(notes).reshape((-1, 1, 1))) prediction[0][0] = (prediction[0][0] * 384) - (prediction[0][0] * 13) + 13 # 将0到1的浮点数转换回整数 notes.append(prediction[0][0])print(notes)
这是我用于创建训练集和标签的函数:
from numpy import asarraydef split_sequence(data, n_steps): new_data, expected_values = list(), list() for i in range(len(data)): if n_steps + i <= len(data) - 1: new_data.append(data[i:n_steps + i]) expected_values.append(data[n_steps + i]) else: break for i in new_data: i[0] = (i[0] - 13) / (384 - 13) for i in range(len(expected_values)): expected_values[i] = (expected_values[i] - 13) / (384 - 13) # 将值转换为0到1之间的浮点数 return asarray(new_data), asarray(expected_values)
当n_steps = 1时,这是x训练数据:
[[64], [76], [64], [75], [64], [76], [64], [75], [64], [76], [64], [71], [64], [74], [64], [72], [69], [64], [45], [64], [52], [64], [57], [64], [60], [64]]
当n_steps = 1时,这是标签:
[76, 64, 75, 64, 76, 64, 75, 64, 76, 64, 71, 64, 74, 64, 72, 69, 64, 45, 64, 52, 64, 57, 64, 60, 64, 64, 64, 69, 71, 64, 40, 64, 52, 64, 56, 64, 64, 64,]
这是我的数据:
[64, 76, 64, 75, 64, 76, 64, 75, 64, 76, 64, 71, 64, 74, 64, 72, 69, 64, 45, 64, 52, 64, 57, 64, 60, 64, 64, 64]
这是我的模型目前输出的,以64为种子的9个预测值的列表:
[64, 62.63686, 62.636864, 62.636864, 62.636864, 62.636864, 62.636864, 62.636864, 62.636864, 62.636864, 62.636864]
我不明白的是为什么这些预测值基本上都是相同的。当我在主代码的最后一个for循环中打印预测值时,我得到的输出是一个包含x个列表的列表,其中x是输入数据的数量。这是其中一个预测的例子:
[[62.500393] [62.500393] [62.500393] [62.500393] [62.500393] [62.500393] [62.500393] [62.500393] [62.500393] [62.500393]]
这就是为什么在那个for循环中,我只取第一个列表中的值作为预测。总结一下,我有一个程序,它接受一个数字列表,我希望LSTM模型输出一个以64为种子的预测数字列表。我遇到的问题是,我的模型不知为何每次都输出基本相同的预测值,所以我需要在这个预测过程中得到帮助。
**更新:**我尝试将model.fit()和model.predict()放在一个for循环中,并循环10次,看看会发生什么。好消息是:每次预测都与上次不同,这很好。坏消息是:这非常慢,我不确定这是不是最好的方法。有什么建议可以让这些值更接近预期值,或者这种方法是否可行?这似乎非常低效,因为我只是为了10个输出音符(实际上是5个,其余5个值是每个音符的持续时间)而重新训练模型10次。
这是使用这个for循环的新输出:
[64, 56.53626, 58.395187, 61.333992, 59.08212, 58.66997, 55.86058, 59.819744, 54.183216, 55.231224, 53.8824]
这是我的新代码,只是加了一个大for循环,其余部分相同:
from midi_to_text import data_parsefrom split_sequence import split_sequenceimport py_midicsv as pmimport mathfrom numpy import asarrayfrom tensorflow.keras import Sequentialfrom tensorflow.keras.layers import *import tensorflow as tfraw_midi = pm.midi_to_csv('OnlineMidi.mid')data = data_parse(raw_midi)n_steps = 1X, y = split_sequence(data, n_steps)print(X)print(y)X = X.reshape((X.shape[0], X.shape[1], 1))X = tf.cast(X, dtype='float32')notes = [64]model = Sequential()model.add(LSTM(256, activation='linear', return_sequences=True))model.add(Dropout(0.2))model.add(LSTM(128, activation='linear', return_sequences=True))model.add(LSTM(128))model.add(Dropout(0.2))model.add(Dense(1, activation='linear'))model.compile(optimizer='adam', loss='mse', metrics=['mae'])for i in range(10): model.fit(X, y, epochs=5, batch_size=2, verbose=2) prediction = model.predict(asarray(notes).reshape((-1, 1, 1))) prediction[0][0] = (prediction[0][0] * 384) - (prediction[0][0] * 13) + 13 notes.append(prediction[0][0])print(notes)
自定义的midi_to_text数据解析器:
def data_parse(raw_midi): temp = [] final = [] to_remove = [] shift_unit = 20 for i in range(len(raw_midi)): temp.append(raw_midi[i].split(', ')) for i in range(len(temp)): if temp[i][2] != 'Note_on_c': to_remove.append(temp[i]) for i in to_remove: temp.remove(i) for i in temp: i.remove(i[0]) i.remove(i[1]) i.remove(i[1]) i.remove(i[2]) for i in range(len(temp)): if i == len(temp) - 1: temp[i][0] = '64' else: temp[i][0] = str(int(temp[i + 1][0]) - int(temp[i][0])) to_remove.clear() for i in range(len(temp)): if i == len(temp) - 1: break if temp[i + 1][0] == '0': temp[i].append(temp[i + 1][1]) to_remove.append(temp[i + 1]) for i in to_remove: temp.remove(i) for i in temp: for _ in i: final.append(int(_)) return final
谢谢!!
回答:
我的结论是,尽管效率低下,但将model.fit和predict放入一个for循环中,以预测未来一步或一次生成一个信息是可行的。这意味着,是的,你确实需要多次拟合模型,输入它之前生成的数据,但我可以接受这种牺牲。这种方法确实有效,只是需要一些时间,这是我找到的主要解决方案。感谢所有回应的人,使所有步骤对我来说非常清晰,希望这个问题能帮助到其他人!