【问题标题】:On training LSTMs efficiently but well, parallelism vs training regime关于有效但良好地训练 LSTM,并行与训练机制
【发布时间】:2018-08-08 19:20:00
【问题描述】:

对于我打算自发生成序列的模型,我发现逐个样本对其进行训练并保持其间的状态感觉最自然。在阅读了许多有用的资源后,我设法在 Keras 中构建了它。 (SO:Q and two fantastic answers,Macine Learning Mastery 123

首先构造一个序列(在我的例子中也是单热编码)。通过将 Y 向前移动一个时间步长,由此序列产生 X 和 Y。训练以一个样本和一个时间步的批次进行。

对于 Keras,这看起来像这样:

data = get_some_data()   # Shape (samples, features)
Y = data[1:, :]          # Shape (samples-1, features)
X = data[:-1, :].reshape((-1, 1, data.shape[-1])) # Shape (samples-1, 1, features)

model = Sequential()
model.add(LSTM(256, batch_input_shape=(1, 1, X.shape[-1]), stateful=True))
model.add(Dense(Y.shape[-1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['categorical_accuracy'])

for epoch in range(10):
    model.fit(X, Y, batch_size=1, shuffle=False)
    model.reset_states()

确实有效。然而,在咨询了我的任务管理器之后,它似乎只使用了我的 GPU 资源的约 10%,这已经非常有限了。我想改进这一点以加快训练速度。增加批量大小将允许并行计算。

即使从训练序列开始,网络在其当前状态下也可能会“记住”事物。对于批量训练,首先需要设置序列,然后预测一个值 - 并对多个值执行此操作。要在完整序列上进行训练,需要生成 (samples-steps, steps, features) 形状的数据。我想有一个跨越至少几百个时间步的序列并不少见。所以这将意味着数据量的巨大增加。

在稍微不同地构建问题和需要将更多数据存储在内存中以及仅使用少量处理资源之间,我必须问:

  • 我对自然训练方式和状态的直觉是否正确?
  • 每批一个样本的这种训练还有其他缺点吗?
  • 能否通过其他方式解决利用率问题?
  • 最后,是否有一种可接受的方式来执行这种训练以生成长序列?

非常感谢任何帮助,我是 LSTM 的新手。

【问题讨论】:

    标签: python machine-learning neural-network keras lstm


    【解决方案1】:

    我不知道您的具体应用程序,但是只发送一个时间步长的数据肯定不是一个好主意。相反,您应该给LSTM 之前给定的单热向量(可能是单词)的整个序列,并在必要时给pre-pad(带零),因为看起来您正在处理不同长度的序列。如果这些确实是单词,还可以考虑在 LSTM 之前使用embedding 层。仔细阅读文档。

    GPU 利用率低不是问题。您根本没有足够的数据来充分利用每批中的所有资源。批量训练是一个连续的过程,没有真正的方法可以并行化这个过程,至少是一种介绍性的方法,对你的目标看起来是有益的。但是,如果您确实在每个时间步为 LSTM 提供更多数据,那么这肯定会提高您的利用率。

    LSTM 中的statefull 不会做你认为的那样。 LSTM 总是会在更新其内部隐藏状态hc 时记住它正在迭代的序列。此外,“建立”这些内部状态的权重转换是在训练期间学习的。 stateful 所做的是从最后一批索引中保留先前的隐藏状态。意思是,批次中第三个元素的最终隐藏状态作为下一批第三个元素的初始隐藏状态发送,依此类推。我不认为这对您的应用程序有用。

    使用每批一个样本来训练 LSTM 有一些缺点。一般来说,使用min-batches increases stability 进行培训。但是,您似乎没有使用 每批一个样本 进行训练,而是使用 每个样本一个时间步长


    编辑(来自 cmets)

    如果您使用有状态并在前一批​​的相同索引中发送序列的下一个“字符”,这类似于发送每个样本的完整序列时间步长。我仍然会推荐上述初始方法,以提高应用程序的速度并更符合其他 LSTM 应用程序。我认为发送每个样本的完整序列而不是每批都发送的方法没有任何缺点。但是,速度的优势,能够每批打乱您的输入数据,以及更具可读性/一致性,值得 IMO 进行更改。

    【讨论】:

    • 感谢您的回答!我试图说清楚,但似乎我还不能很好地表达这些事情。我的具体应用是 - 不完全但类似于 - 一次生成一个字符的文本。多采摘你的大脑。阅读关于有状态的 github 文章,我不确定我是听不懂还是没有字。拥有一个有状态的网络就是在批次之间保留状态。我使用它一次向网络提供一个样本和标签(是的,一个时间步,因为在这种情况下时间步是批次)。正确的?待续..
    • ... 现在。我在您的回答中没有得到的是“第三个元素作为下一批第三个元素中的初始隐藏状态发送”。为什么不让最后一个索引成为下一批的第一个状态?我就是这么想象的。但是在大小为 1 的情况下,这将是等效的,对吗?非常感谢您与新手交流。
    • 但我确实有不同的独立序列可以批量处理!这将是一个值得研究的想法。
    • @Felix 回答已更新以尝试回答您的问题。
    • 有道理。让我试着澄清最后一件事。我的知识是我必须将序列切成形状X: (batches, steps, features) Y: (batches, 1, features)。正如我在原始问题中概述的那样,这将大大增加内存消耗,因为每个预测的 Y 必须有一个完整的 X 数组来训练。可以改写一下,使这种使 X 和 Y 移动一步并同时预测一个序列的方法可以在一批内实现吗?哇,对于一个 SO 条目来说,这是一大堆问题。我几乎感到尴尬。
    猜你喜欢
    • 1970-01-01
    • 2018-01-08
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-25
    • 2020-03-08
    • 2018-02-16
    相关资源
    最近更新 更多