数据集在LSTM训练过程中无法适应内存。

3

我试图创建一个模型,它是在大型音乐数据集上训练的。将midi文件转换为numpy数组。由于LSTM需要顺序数据,因此将数据集转换为LSTM序列后,数据集大小变得非常庞大。

我将midi音符转换为基于半音和持续时间的索引,所以我获得C4键的6个类。同样地,我得到了C3到B5,总共288个类,还有休息时间的类。

单个midi的转换格式如下:

midi = [0,23,54,180,23,45,34,.....];

为了训练LSTM,x和y变成了:

x = [[0,23,54..45],[23,54,..,34],...];

y=[[34],[76],...]

x和y中的值进一步转换为one-hot编码。

因此,仅使用60个小mid文件,数据的大小就变得巨大了,但我有1700个文件,我该如何训练这么多的文件呢?我检查了ImageGenerator,但它需要将数据放在单独的类目录中。该如何解决这个问题?


使用独热编码有什么特定的原因吗? - Darshit
没有特别的原因,是否可能只使用x = [[0,23,54..45],[23,54,..,34],...];y=[[34],[76],...]进行训练,而不是将其转换为独热编码? - R Nanthak
一位熟练的程序员应该知道,一种叫做“独热编码”的技术可以将文本数据转换成数字形式,方便机器学习模型进行处理。如果你的数据集已经是数字形式了,那么就不需要使用独热编码了。 另外,如果数据集中包含大量的唯一单词,那么使用独热编码会占用大量内存。此时,建议使用TF-IDF或Word2Vec转换技术来代替。 - Darshit
如果我使用独热编码,训练所需的时间会增加,并且验证损失会很高。为什么会这样? - R Nanthak
随着数据量的增加(在与其他转换相比的一位有效编码中表示大小增长),训练时间也会增加。LSTM模型是一个序列模型,其预测取决于数据的先前序列,因此我怀疑使用一位有效编码可能会得到一系列长时间为零的序列,这就是导致问题的原因。 - Darshit
添加嵌入层的模型在训练一个 epoch 时比直接使用 one-hot 编码输入模型需要更多时间。 - R Nanthak
2个回答

4

您应该在训练过程中即时生成训练数据。根据tf文档,您可以编写自己的生成器作为训练数据,或从Sequence继承。

第一种选项应该看起来像:

def create_data_generator(your_files):
    raw_midi_data = process_files(your_files)
    seq_size = 32

    def _my_generator():
        i = 0 
        while True:
            x = raw_midi_data[i:i + seq_size]
            y = raw_midi_data[i + seq_size]
            i = (i + 1) % (len(raw_midi_data) - seq_size)
            yield x, y

    return _my_generator()

然后使用以下代码调用(假设 tf 的版本号大于等于 2.0):

generator = create_data_generator(your_files)
model.fit(x=generator, ...)

如果您正在使用“旧版” Keras(在tensorflow 2.0之前),而Keras团队本身并不建议使用,那么您应该使用fit_generator代替:

model.fit_generator(generator, ...)

使用这种解决方案,您只需要一次将数据存储在内存中,由于重叠序列而不会出现重复。


谢谢回答,我正在实现您的答案,很快会给您回复。 - R Nanthak
这是 model.fit_generator 吗?@Théo Rubenach - R Nanthak
你的解决方案不起作用,它需要在参数中输入y。 - R Nanthak
我正在开发一个convLSTM,其中我有大约500GB的数据。这些数据无法适应我的内存可用性,因此我正在尝试基于您上面的设计(“第一选项”)编写自定义数据加载器。然而,据我所知,这仍会将所有数据加载到内存中,只是以不同的批次进行。当我根据上述自定义数据生成器设计训练模型时,我仍然会因为加载了所有数据而耗尽内存。是否有一种方法可以一次加载一条数据,以便我不会耗尽内存? - gkroiz
@Gerson 与其在生成器初始化期间加载数据,不如尝试在 while 循环中加载数据。这样,每次只有一小部分数据加载到内存中,并且应该可以正常工作。 - Théo Rubenach
显示剩余2条评论

0
我在这个问题中使用了一个生成器类,并使用了以下代码。该生成器已经被修改以适应我的需求,内存使用量大大降低。
class Generator(Sequence):

    def __init__(self, x_set, y_set, batch_size=4):
        self.x, self.y = x_set, y_set

        self.batch_size = batch_size
        self.indices = np.arange(len(self.x))

    def __len__(self):
        return int(np.ceil(len(self.x) / self.batch_size))

    def __getitem__(self, idx):
        inds = self.indices[idx * self.batch_size:(idx + 1) * self.batch_size]
        batch_x = []
        batch_y = []
        for ind in inds:
            ip = []

            for q in self.x[ind]:
                o = np.zeros(323)
                o[int(q)] = 1
                ip.append(o)
            batch_x.append(ip)
            hot_encoded = []
            for val in self.y[ind]:

                t = np.zeros(323)
                t[int(val)] = 1
                hot_encoded.append(t)
            batch_y.append(hot_encoded)

        return np.array(batch_x), np.array(batch_y)

    def on_epoch_end(self):
        # np.random.shuffle(self.indices)
        np.random.shuffle(self.indices)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接