了解Keras LSTM

sac*_*ruk 259 python deep-learning lstm keras

我试图调和我对LSTM的理解,并在克里斯托弗·奥拉在克拉拉斯实施的这篇文章中指出.我正在关注Jason Brownlee为Keras教程撰写博客.我主要困惑的是,

  1. 将数据系列重塑为[samples, time steps, features]和,
  2. 有状态的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()
Run Code Online (Sandbox Code Playgroud)

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

什么是时间步骤和功能?

可以看出,TrainX是一个三维数组,其中Time_steps和Feature分别是最后两个维度(在此特定代码中为3和1).关于下图,这是否意味着我们正在考虑many to one粉红色盒子数量为3的情况?或者它的字面意思是链长是3(即只考虑3个绿色框).在此输入图像描述

当我们考虑多元系列时,特征参数是否相关?例如同时建模两个金融股?

有状态的LSTM

有状态LSTM是否意味着我们在批次运行之间保存单元格内存值?如果是这种情况,那batch_size就是一个,并且在训练运行之间重置内存,所以说它是有状态的.我猜这与训练数据没有改组的事实有关,但我不确定如何.

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

编辑1:

关于@ van关于红色和绿色盒子相等的评论有点困惑.所以,为了确认,以下API调用是否与展开的图表相对应?特别注意第二个图(batch_size任意选择): 在此输入图像描述 在此输入图像描述

编辑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

UPDATE2:

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

Van*_*Van 141

首先,你选择伟大的教程(1,2)开始.

什么时间步骤意味着:Time-steps==3在X.shape(描述数据形状)中意味着有三个粉红色框.由于在Keras中每个步骤都需要输入,因此绿色框的数量通常应该等于红色框的数量.除非你破解结构.

多对多对多对多:在keras中,return_sequences初始化时有一个参数LSTMGRUSimpleRNN.当return_sequencesFalse(默认情况下),那么它是多对一如图所示的画面.它的返回形状(batch_size, hidden_unit_length)代表最后一个状态.如果return_sequencesTrue,那么它是多对多.它的回归形状是(batch_size, time_step, hidden_unit_length)

features参数是否相关:Feature参数表示"红色框有多大"或每个步骤的输入维度是什么.如果您想从8种市场信息中预测,那么您可以使用生成数据feature==8.

有状态:您可以查找源代码.初始化状态时,if stateful==True,则将上次训练的状态用作初始状态,否则将生成新状态.我还没打开stateful.但是,我不同意那batch_size只能是1时stateful==True.

目前,您使用收集的数据生成数据.图像您的股票信息是作为流而来,而不是等待一天收集所有顺序,您希望在线培训/预测时在线生成输入数据.如果您有400只股票共享同一个网络,那么您可以设置batch_size==400.

  • 的确。检查文档:`stateful: Boolean(默认为False)。如果为 True,则批次中索引 i 处每个样本的最后状态将用作下一批中索引 i 样本的初始状态。` (2认同)
  • 除了`stateful=True`:批量大小可以是任何你喜欢的,但你必须坚持下去。如果您以 5 的批次大小构建模型,则所有 `fit()`、`predict()` 和相关方法都需要批次 5。但是请注意,此状态不会使用 `model.save( )`,这可能看起来不受欢迎。但是,如果需要,您可以手动将状态添加到 hdf5 文件中。但实际上,这允许您通过保存和重新加载模型来更改批量大小。 (2认同)

Dan*_*ler 137

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

凯拉斯将军的行为

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

多对多

在此图像中,我将步数增加到5,以避免与其他维度混淆.

对于这个例子:

  • 我们有N个油箱
  • 我们花了5个小时每小时采取措施(时间步骤)
  • 我们测量了两个特征:
    • 压力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]],
        ]
Run Code Online (Sandbox Code Playgroud)

滑动窗口的输入

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

在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]],
  ....
        ]
Run Code Online (Sandbox Code Playgroud)

请注意,在这种情况下,您最初只有一个序列,但是您要在许多序列中对其进行划分以创建窗口.

"什么是序列"的概念是抽象的.重要的部分是:

  • 你可以拥有许多单独序列的批次
  • 使序列成为序列的原因是它们逐步演变(通常是时间步长)

用"单层"实现每个案例

实现多对多的标准:

StandardManyToMany

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

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

#output_shape -> (batch_size, steps, units)
Run Code Online (Sandbox Code Playgroud)

实现多对一:

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

多对一

outputs = LSTM(units)(inputs)

#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned
Run Code Online (Sandbox Code Playgroud)

实现一对多

现在,仅keras LSTM层不支持此功能.您必须创建自己的策略来增加步骤.有两种好方法:

  • 通过重复张量创建一个恒定的多步输入
  • 使用a stateful=True来反复获取一步的输出并将其作为下一步的输入(需要output_features == input_features)

重复矢量一对多

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

OneToManyRepeat

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

#output_shape -> (batch_size, steps, units)
Run Code Online (Sandbox Code Playgroud)

了解stateful = 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]],
        ]                                  ]
Run Code Online (Sandbox Code Playgroud)

请注意批次1和批次2中的储罐对齐!这就是我们需要的原因shuffle=False(当然,除非我们只使用一个序列).

您可以无限期地拥有任意数量的批次.(对于每批中的可变长度,请使用input_shape=(None,features).

一对多有状态=真

对于我们这里的情况,我们将每批只使用一步,因为我们想要获得一个输出步骤并使其成为输入.

请注意图片中的行为不是"由"引起的stateful=True.我们将在下面的手动循环中强制执行该行为.在这个例子中,stateful=True是什么"允许"我们停止序列,操纵我们想要的东西,并从我们停止的地方继续.

OneToManyStateful

老实说,对于这种情况,重复方法可能是更好的选择.但是,既然我们正在研究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) 
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

有状态= True的多对多

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

我们使用与上面"一对多"相同的方法,区别在于:

  • 我们将使用序列本身作为目标数据,领先一步
  • 我们知道序列的一部分(所以我们放弃了这部分结果).

ManyToManyStateful

层(与上面相同):

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) 
Run Code Online (Sandbox Code Playgroud)

训练:

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

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)
Run Code Online (Sandbox Code Playgroud)

预测:

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

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
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

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

实现复杂的配置

在上面的所有例子中,我展示了"一层"的行为.

当然,您可以将多个图层堆叠在一起,而不必按照相同的模式进行堆叠,并创建自己的模型.

出现的一个有趣的例子是"自动编码器",它具有"多对一编码器",后跟"一对多"解码器:

编码器:

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)
Run Code Online (Sandbox Code Playgroud)

解码器:

使用"重复"方法;

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)
Run Code Online (Sandbox Code Playgroud)

自动编码:

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

autoencoder = Model(inputs,outputs)
Run Code Online (Sandbox Code Playgroud)

训练 fit(X,X)

补充说明

如果您想了解有关如何在LSTM中计算步骤的详细信息,或有关上述stateful=True案例的详细信息,您可以在此答案中阅读更多内容:关于"了解Keras LSTM"的疑问

  • 有状态的使用非常有趣,使用输出作为输入。作为补充说明,另一种方法是使用功能性 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])` 的循环 (2认同)
  • 单元格和长度是完全独立的值。没有一张图片代表“细胞”的数量。他们都是为了“长度”。 (2认同)
  • 是的。在有状态 = True,批次 1 = 样本组,更新。然后批次 2 = 同一组样本的更多步骤,更新。 (2认同)
  • 我希望我可以投票100次。超级有用的答案。 (2认同)

小智 11

当您在 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))
Run Code Online (Sandbox Code Playgroud)


Mas*_*ari 5

有关动画 RNN、LSTM 和 GRU 的更多详细信息,请参阅此博客。

\n

下图让您更好地了解 LSTM。这是一个 LSTM 单元。\n这个图

\n

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

\n

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

\n

这个图片

\n

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

\n

现在我再次使用Daniel M\xc3\xb6ller的例子来更好地理解:\n我们有 10 个油罐。对于每个参数,我们每隔一小时测量 2 个特征:温度、压力,共 5 次。\n现在参数为:

\n
    \n
  • batch_size = 一次前向/后向传递中使用的样本数(默认=32)--> 例如,如果您有 1000 个样本,并且将 batch_size 设置为 100,则模型将需要 10 次迭代才能传递所有样本一次网络(1 个纪元)。批量大小越大,您需要的内存空间就越大。由于本例中的样本数量较少,因此我们认为batch_size等于所有样本= 10
  • \n
  • 时间步= 5
  • \n
  • 特征= 2
  • \n
  • units = 它是一个正整数,决定隐藏状态和单元状态的维度,或者换句话说,传递给下一个 LSTM 单元的参数数量。它可以根据特征和时间步长任意选择或凭经验选择。使用更多的单位将导致更高的准确性和更多的计算时间。但可能会导致过拟合。
  • \n
  • input_shape = (batch_size, 时间步长, 特征) = (10,5,2)
  • \n
  • 输出形状:\n
      \n
    • (batch_size、时间步长、单位)如果return_sequences=True
    • \n
    • (batch_size, 单位) 如果return_sequences=False
    • \n
    \n
  • \n
\n