ChatGPT解决这个技术问题 Extra ChatGPT

了解 Keras LSTM

我试图调和我对 LSTM 的理解,并在 Keras 中实现的 this post by Christopher Olah 中指出。我正在关注 Keras 教程的 blog written by Jason Brownlee。我主要困惑的是,

将数据系列重塑为 [样本、时间步长、特征] 和有状态 LSTM

让我们参考下面粘贴的代码专注于上述两个问题:

# reshape into X=t and Y=t+1
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)

# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], look_back, 1))
testX = numpy.reshape(testX, (testX.shape[0], look_back, 1))
########################
# The IMPORTANT BIT
##########################
# create and fit the LSTM network
batch_size = 1
model = Sequential()
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(100):
    model.fit(trainX, trainY, nb_epoch=1, batch_size=batch_size, verbose=2, shuffle=False)
    model.reset_states()

注意:create_dataset 采用长度为 N 的序列并返回一个 N-look_back 数组,其中每个元素都是一个 look_back 长度的序列。

什么是时间步长和特征?

https://i.stack.imgur.com/kwhAP.jpg

当我们考虑多元序列时,特征参数是否变得相关?例如同时模拟两只金融股?

有状态的 LSTM

有状态 LSTM 是否意味着我们在批次运行之间保存单元记忆值?如果是这种情况,batch_size 是 1,并且在训练运行之间会重置内存,那么说它是有状态的有什么意义。我猜这与训练数据没有被打乱的事实有关,但我不确定如何。

有什么想法吗?图片参考:http://karpathy.github.io/2015/05/21/rnn-effectiveness/

编辑1:

https://i.stack.imgur.com/sW207.jpg

编辑2:

对于已经完成 Udacity 的深度学习课程但仍然对 time_step 参数感到困惑的人,请查看以下讨论:https://discussions.udacity.com/t/rnn-lstm-use-implementation/163169

更新:

原来 model.add(TimeDistributed(Dense(vocab_len))) 是我要找的。这是一个示例:https://github.com/sachinruk/ShakespeareBot

更新2:

我在这里总结了我对 LSTM 的大部分理解:https://www.youtube.com/watch?v=ywinX5wgdEU

第一张照片应该是 (batch_size, 5, 1);第二张照片应该是 (batch_size, 4, 3) (如果没有以下序列)。为什么输出仍然是“X”?应该是“Y”吗?
这里我假设 X_1, X_2 ... X_6 是一个数字。三个数 (X_1, X_2, X_3) 构成一个形状为 (3,) 的向量。一个数字 (X_1) 构成一个形状为 (1,) 的向量。
@Van,你的假设是正确的。这很有趣,所以基本上模型不会学习超出 time_steps 数量的模式。因此,如果我有一个长度为 1000 的时间序列,并且每 100 天可以直观地看到一个模式,我应该使 time_steps 参数至少为 100。这是一个正确的观察吗?
是的。如果您每天可以收集 3 个相关特征,那么您可以将特征大小设置为 3,就像在第二张照片中所做的那样。在这种情况下,输入形状将是 (batch_size, 100, 3)。
并回答您的第一个问题,这是因为我采用了一个时间序列。例如股票价格,所以 X 和 Y 来自同一个系列。

D
Daniel Möller

作为已接受答案的补充,此答案显示了 keras 行为以及如何实现每张图片。

一般 Keras 行为

标准的 keras 内部处理始终是多对多的,如下图所示(我使用 features=2,压力和温度,仅作为示例):

https://i.stack.imgur.com/vTYa5.jpg

在这张图片中,我将步数增加到 5,以避免与其他维度混淆。

对于这个例子:

我们有N个油箱

我们花了 5 小时每小时采取措施(时间步长)

我们测量了两个特征: 压力 P 温度 T

压力P

温度 T

然后我们的输入数组应该是形状为 (N,5,2) 的东西:

        [     Step1      Step2      Step3      Step4      Step5
Tank A:    [[Pa1,Ta1], [Pa2,Ta2], [Pa3,Ta3], [Pa4,Ta4], [Pa5,Ta5]],
Tank B:    [[Pb1,Tb1], [Pb2,Tb2], [Pb3,Tb3], [Pb4,Tb4], [Pb5,Tb5]],
  ....
Tank N:    [[Pn1,Tn1], [Pn2,Tn2], [Pn3,Tn3], [Pn4,Tn4], [Pn5,Tn5]],
        ]

滑动窗口的输入

通常,LSTM 层应该处理整个序列。划分窗口可能不是最好的主意。该层具有关于序列在前进时如何演变的内部状态。窗口消除了学习长序列的可能性,将所有序列限制在窗口大小。

在 windows 中,每个窗口都是一个长的原始序列的一部分,但在 Keras 中,它们将被视为一个独立的序列:

        [     Step1    Step2    Step3    Step4    Step5
Window  A:  [[P1,T1], [P2,T2], [P3,T3], [P4,T4], [P5,T5]],
Window  B:  [[P2,T2], [P3,T3], [P4,T4], [P5,T5], [P6,T6]],
Window  C:  [[P3,T3], [P4,T4], [P5,T5], [P6,T6], [P7,T7]],
  ....
        ]

请注意,在这种情况下,您最初只有一个序列,但您将其划分为多个序列以创建窗口。

“什么是序列”的概念是抽象的。重要的部分是:

你可以有许多单独的序列批次

使序列成为序列的原因是它们逐步演化(通常是时间步长)

用“单层”实现每个案例

达到标准多对多:

https://i.stack.imgur.com/RXYW2.jpg

您可以使用 return_sequences=True 通过简单的 LSTM 层实现多对多:

outputs = LSTM(units, return_sequences=True)(inputs)

#output_shape -> (batch_size, steps, units)

实现多对一:

使用完全相同的层,keras 将执行完全相同的内部预处理,但是当您使用 return_sequences=False(或简单地忽略此参数)时,keras 将自动丢弃上一步之前的步骤:

https://i.stack.imgur.com/GMe8r.jpg

outputs = LSTM(units)(inputs)

#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned

实现一对多

现在,仅 keras LSTM 层不支持这一点。您将必须创建自己的策略来增加步骤。有两种很好的方法:

通过重复张量创建一个恒定的多步输入

使用 stateful=True 循环获取一个步骤的输出并将其作为下一步的输入(需要 output_features == input_features)

具有重复向量的一对多

为了适应 keras 标准行为,我们需要分步输入,因此,我们只需按照我们想要的长度重复输入:

https://i.stack.imgur.com/ZADR6.jpg

outputs = RepeatVector(steps)(inputs) #where inputs is (batch,features)
outputs = LSTM(units,return_sequences=True)(outputs)

#output_shape -> (batch_size, steps, units)

理解有状态 = True

现在是 stateful=True 的一种可能用法(除了避免一次加载无法满足您计算机内存的数据)

有状态允许我们分阶段输入序列的“部分”。区别在于:

在 stateful=False 中,第二批包含全新的序列,独立于第一批

在 stateful=True 中,第二批继续第一批,扩展相同的序列。

这就像在窗口中划分序列一样,主要有以下两个区别:

这些窗口不重叠!

stateful=True 将看到这些窗口连接为一个长序列

stateful=True 中,每个新批次都将被解释为继续上一个批次(直到您调用 model.reset_states())。

批次 2 中的序列 1 将继续批次 1 中的序列 1。

批次 2 中的序列 2 将继续批次 1 中的序列 2。

批次 2 中的序列 n 将继续批次 1 中的序列 n。

输入示例,第 1 批包含第 1 步和第 2 步,第 2 批包含第 3 到第 5 步:

                   BATCH 1                           BATCH 2
        [     Step1      Step2        |    [    Step3      Step4      Step5
Tank A:    [[Pa1,Ta1], [Pa2,Ta2],     |       [Pa3,Ta3], [Pa4,Ta4], [Pa5,Ta5]],
Tank B:    [[Pb1,Tb1], [Pb2,Tb2],     |       [Pb3,Tb3], [Pb4,Tb4], [Pb5,Tb5]],
  ....                                |
Tank N:    [[Pn1,Tn1], [Pn2,Tn2],     |       [Pn3,Tn3], [Pn4,Tn4], [Pn5,Tn5]],
        ]                                  ]

注意第 1 批和第 2 批坦克的对齐方式!这就是我们需要 shuffle=False 的原因(当然,除非我们只使用一个序列)。

您可以无限地拥有任意数量的批次。 (要在每批中具有可变长度,请使用 input_shape=(None,features)

stateful=True 一对多

对于我们这里的例子,我们将只使用每批 1 个步骤,因为我们想要获得一个输出步骤并将其作为输入。

请注意,图片中的行为不是“由”stateful=True 引起的。我们将在下面的手动循环中强制执行该行为。在此示例中,stateful=True 是“允许”我们停止序列、操纵我们想要的内容并从我们停止的地方继续的东西。

https://i.stack.imgur.com/ihAFT.jpg

老实说,对于这种情况,重复方法可能是更好的选择。但由于我们正在研究 stateful=True,这是一个很好的例子。使用它的最佳方式是下一个“多对多”案例。

层:

outputs = LSTM(units=features, 
               stateful=True, 
               return_sequences=True, #just to keep a nice output shape even with length 1
               input_shape=(None,features))(inputs) 
    #units = features because we want to use the outputs as inputs
    #None because we want variable length

#output_shape -> (batch_size, steps, units) 

现在,我们将需要一个手动循环来进行预测:

input_data = someDataWithShape((batch, 1, features))

#important, we're starting new sequences, not continuing old ones:
model.reset_states()

output_sequence = []
last_step = input_data
for i in steps_to_predict:

    new_step = model.predict(last_step)
    output_sequence.append(new_step)
    last_step = new_step

 #end of the sequences
 model.reset_states()

多对多与 stateful=True

现在,在这里,我们得到了一个非常好的应用程序:给定一个输入序列,尝试预测它未来的未知步骤。

我们使用与上述“一对多”相同的方法,不同之处在于:

我们将使用序列本身作为目标数据,领先一步

我们知道序列的一部分(所以我们丢弃这部分结果)。

https://i.stack.imgur.com/4HPZB.jpg

层(同上):

outputs = LSTM(units=features, 
               stateful=True, 
               return_sequences=True, 
               input_shape=(None,features))(inputs) 
    #units = features because we want to use the outputs as inputs
    #None because we want variable length

#output_shape -> (batch_size, steps, units) 

训练:

我们将训练我们的模型来预测序列的下一步:

totalSequences = someSequencesShaped((batch, steps, features))
    #batch size is usually 1 in these cases (often you have only one Tank in the example)

X = totalSequences[:,:-1] #the entire known sequence, except the last step
Y = totalSequences[:,1:] #one step ahead of X

#loop for resetting states at the start/end of the sequences:
for epoch in range(epochs):
    model.reset_states()
    model.train_on_batch(X,Y)

预测:

我们预测的第一阶段涉及“调整状态”。这就是为什么我们要再次预测整个序列,即使我们已经知道它的这一部分:

model.reset_states() #starting a new sequence
predicted = model.predict(totalSequences)
firstNewStep = predicted[:,-1:] #the last step of the predictions is the first future step

现在我们像一对多的情况一样进入循环。但不要在这里重置状态!我们希望模型知道它在序列的哪一步(并且由于我们刚刚在上面所做的预测,它知道它处于第一个新步骤)

output_sequence = [firstNewStep]
last_step = firstNewStep
for i in steps_to_predict:

    new_step = model.predict(last_step)
    output_sequence.append(new_step)
    last_step = new_step

 #end of the sequences
 model.reset_states()

这些答案和文件中使用了这种方法:

使用 LSTM 预测时间序列的多个前向时间步

如何使用 Keras 模型来预测未来的日期或事件?

https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb

实现复杂的配置

在上面的所有示例中,我都展示了“一层”的行为。

当然,您可以将许多层堆叠在一起,不一定都遵循相同的模式,并创建自己的模型。

一个有趣的例子是“自动编码器”,它有一个“多对一编码器”,后跟一个“一对多”解码器:

编码器:

inputs = Input((steps,features))

#a few many to many layers:
outputs = LSTM(hidden1,return_sequences=True)(inputs)
outputs = LSTM(hidden2,return_sequences=True)(outputs)    

#many to one layer:
outputs = LSTM(hidden3)(outputs)

encoder = Model(inputs,outputs)

解码器:

使用“重复”方法;

inputs = Input((hidden3,))

#repeat to make one to many:
outputs = RepeatVector(steps)(inputs)

#a few many to many layers:
outputs = LSTM(hidden4,return_sequences=True)(outputs)

#last layer
outputs = LSTM(features,return_sequences=True)(outputs)

decoder = Model(inputs,outputs)

自动编码器:

inputs = Input((steps,features))
outputs = encoder(inputs)
outputs = decoder(outputs)

autoencoder = Model(inputs,outputs)

fit(X,X) 一起训练

附加说明

如果您想了解有关如何在 LSTM 中计算步数的详细信息,或者有关上述 stateful=True 案例的详细信息,您可以在此答案中阅读更多信息:Doubts regarding `Understanding Keras LSTMs`


这是一个很好的答案。不过,我仍然缺少一件事。假设 LSTM 应该学习 2 个序列 I:[1,2,3]->O:[4,5,6] 和 I:[1,2,0]->O:[0,0,0] .它应该预测 3 步,但为了预测第 3 步,必须先预测第 1 步和第 2 步。问题是,第 1 步和第 2 步本身就有模棱两可的结果。那么当第 3 步被输入网络时会发生什么?步骤 1 和 2 的输出得到纠正?
使用输出作为输入非常有趣地使用有状态。作为附加说明,另一种方法是使用功能性 Keras API(就像您在这里所做的那样,尽管我相信您可以使用顺序 API),并且在每个时间步骤中简单地重用相同的 LSTM 单元,同时将结果状态和输出从单元格传递给自身。即my_cell = LSTM(num_output_features_per_timestep, return_state=True),后跟一个循环a, _, c = my_cell(output_of_previous_time_step, initial_states=[a, c])
单元格和长度是完全独立的值。没有一张图片代表“细胞”的数量。他们都是为了“长度”。
@DanielMöller 我知道有点晚了,但是您的回答确实引起了我的注意。你的观点打破了我对 LSTM 批次的理解的一切。您提供了 N 个坦克、五个步骤和两个特征的示例。我相信,如果批次是例如两个,这意味着两个样本(具有 5 步 2 特征的坦克)将被输入网络,然后将调整权重。但是,如果我理解正确,您说第 2 批意味着样本的时间步长将被划分为 2,所有样本的前半部分将被提供给 LSTM-> 权重更新,然后是第二个。
是的。在有状态 = True 时,第 1 批 = 样本组,更新。然后批次 2 = 同一组样本的更多步骤,更新。
V
Van

首先,您选择优秀的教程(12)开始。

Time-step 的含义:X.shape(描述数据形状)中的 Time-steps==3 表示有三个粉色框。由于在 Keras 中每一步都需要输入,因此绿色框的数量通常应该等于红色框的数量。除非你破解结构。

多对多 vs. 多对一:在 keras 中,初始化 LSTMGRUSimpleRNN 时有一个 return_sequences 参数。当 return_sequencesFalse(默认)时,则为 多对一,如图所示。它的返回形状是(batch_size, hidden_unit_length),代表最后一个状态。当 return_sequencesTrue 时,它是多对多。它的返回形状是 (batch_size, time_step, hidden_unit_length)

特征参数是否相关:特征参数表示“你的红框有多大”或每一步的输入维度是多少。如果您想根据 8 种市场信息进行预测,则可以使用 feature==8 生成数据。

有状态:您可以查找 the source code。初始化状态时,若为stateful==True,则将上次训练的状态作为初始状态,否则生成新状态。我还没有打开 stateful。但是,我不同意 stateful==Truebatch_size 只能为 1。

目前,您使用收集的数据生成数据。想象您的股票信息以流的形式出现,而不是等待一天来收集所有顺序,您希望在使用网络进行训练/预测时生成输入数据在线。如果您有 400 只股票共享同一个网络,那么您可以设置 batch_size==400


对为什么红色和绿色框必须相同感到有些困惑。你能看看我所做的编辑(主要是新图片)并发表评论吗?
的确。检查文档:stateful: Boolean (default False). If True, the last state for each sample at index i in a batch will be used as initial state for the sample of index i in the following batch.
@Van 如果我有一个多元时间序列,我还应该使用 lookback = 1 吗?
为什么输出空间(32)的 LSTM 维度与神经元(LSTM 单元)的数量不同?
stateful=True 的补充:批量大小可以是任何你喜欢的,但你必须坚持。如果您以 5 的批次大小构建模型,则所有 fit()predict() 和相关方法都需要 5 个批次。但请注意,此状态不会与 model.save() 一起保存,这似乎是不可取的。但是,如果需要,您可以手动将状态添加到 hdf5 文件。但实际上,这允许您通过保存和重新加载模型来更改批量大小。
S
Sanjay Krishna

当您在 RNN 的最后一层中有 return_sequences 时,您不能使用简单的 Dense 层,而是使用 TimeDistributed。

这是一段可能对其他人有帮助的示例代码。

words = keras.layers.Input(batch_shape=(None, self.maxSequenceLength), name = "input")

    # Build a matrix of size vocabularySize x EmbeddingDimension 
    # where each row corresponds to a "word embedding" vector.
    # This layer will convert replace each word-id with a word-vector of size Embedding Dimension.
    embeddings = keras.layers.embeddings.Embedding(self.vocabularySize, self.EmbeddingDimension,
        name = "embeddings")(words)
    # Pass the word-vectors to the LSTM layer.
    # We are setting the hidden-state size to 512.
    # The output will be batchSize x maxSequenceLength x hiddenStateSize
    hiddenStates = keras.layers.GRU(512, return_sequences = True, 
                                        input_shape=(self.maxSequenceLength,
                                        self.EmbeddingDimension),
                                        name = "rnn")(embeddings)
    hiddenStates2 = keras.layers.GRU(128, return_sequences = True, 
                                        input_shape=(self.maxSequenceLength, self.EmbeddingDimension),
                                        name = "rnn2")(hiddenStates)

    denseOutput = TimeDistributed(keras.layers.Dense(self.vocabularySize), 
        name = "linear")(hiddenStates2)
    predictions = TimeDistributed(keras.layers.Activation("softmax"), 
        name = "softmax")(denseOutput)  

    # Build the computational graph by specifying the input, and output of the network.
    model = keras.models.Model(input = words, output = predictions)
    # model.compile(loss='kullback_leibler_divergence', \
    model.compile(loss='sparse_categorical_crossentropy', \
        optimizer = keras.optimizers.Adam(lr=0.009, \
            beta_1=0.9,\
            beta_2=0.999, \
            epsilon=None, \
            decay=0.01, \
            amsgrad=False))

M
Masoud Gheisari

有关详细信息,请参阅此博客 Animated RNN, LSTM and GRU

https://i.stack.imgur.com/Csa3E.png

如您所见,X 有 3 个 features(绿色圆圈),因此该单元格的输入是一个维度为 3 的向量,隐藏状态有 2 个 units(红色圆圈),因此该单元格的输出(以及单元格状态)是一个维度为 2 的向量。

一个具有 3 个时间步长(3 个 LSTM 单元)的 LSTM 层的示例如下图所示:

https://i.stack.imgur.com/7n2Fc.png

** 一个模型可以有多个 LSTM 层。

现在我再次使用 Daniel Möller 的例子来更好地理解:我们有 10 个油箱。对于它们中的每一个,我们测量 2 个特征:温度、压力,每隔一小时测量 5 次。现在参数是:

batch_size = 一次前向/后向传递中使用的样本数(默认值 = 32)--> 例如,如果您有 1000 个样本并且您将 batch_size 设置为 100,那么模型将需要 10 次迭代才能将所有样本通过一次网络(1 个时代)。批处理大小越大,您需要的内存空间就越多。由于本例中的样本数量较少,我们认为 batch_size 等于所有样本 = 10

时间步长 = 5

特征 = 2

units = 它是一个正整数,确定隐藏状态和单元状态的维度,或者换句话说,传递给下一个 LSTM 单元的参数数量。它可以根据特征和时间步任意或凭经验选择。使用更多单位将导致更高的准确性和更多的计算时间。但这可能会导致过度拟合。

input_shape = (batch_size, timesteps, features) = (10,5,2)

output_shape: (batch_size, timesteps, units) if return_sequences=True (batch_size, units) if return_sequences=False

(batch_size, timesteps, units) if return_sequences=True

(batch_size, 单位) 如果 return_sequences=False


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅