理解Keras lstm

我试图调和我对LSTMs的理解,并在Keras中实现的本文作者:Christopher Olah中指出。我在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()

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

什么是时间步长和特征?

可以看到,TrainX是一个三维数组,Time_steps和Feature分别是最后两个维度(在这个特定的代码中是3和1)。对于下图,这是否意味着我们正在考虑many to one情况,其中粉红色盒子的数量是3?或者它字面上的意思是链长为3(即只考虑3个绿色盒子)。enter image description here

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

有状态LSTMs

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

< p >任何想法吗? 图片参考:http://karpathy.github.io/2015/05/21/rnn-effectiveness/

编辑1:

@van关于红盒子和绿盒子相等的评论有点困惑。为了确认,下面的API调用是否与展开的图表相对应?特别注意第二个图(batch_size是任意选择的。): enter image description here enter image description here < / p >

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

75773 次浏览

首先,你选择好的教程(12)开始。

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

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

特征论点变得相关了吗:特征参数表示“你的红盒子有多大?”或每一步的输入维度。如果你想从8种市场信息中进行预测,那么你可以用feature==8生成数据。

有状态:你可以查源代码。初始化状态时,如果stateful==True,则上次训练的状态将被用作初始状态,否则将生成一个新状态。我还没有打开stateful。然而,我不同意stateful==Truebatch_size只能为1。

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

当你在RNN的最后一层中有return_sequences时,你不能使用简单的稠密层,而是使用timedidistributed。

下面是一段示例代码,可能会对其他人有所帮助。

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))

作为公认答案的补充,这个答案展示了keras的行为以及如何实现每个图片。

一般Keras行为

标准keras内部处理总是多对多,如下图所示(其中我使用features=2,压力和温度,只是作为一个例子):

ManyToMany .

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

对于这个例子:

  • 我们有N个油罐
  • 我们花了5个小时每小时采取措施(时间步骤)
  • 我们测量了两个特征:
    • 压力P
    • 温度
    • 李< / ul > < / >

    输入数组的形状应该是(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层应该处理整个序列。把窗户分开可能不是最好的主意。该层具有关于序列在前进过程中如何演变的内部状态。窗口消除了学习长序列的可能性,将所有序列限制在窗口大小。

    在窗口中,每个窗口都是一个长原始序列的一部分,但通过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]],
    ....
    ]
    

    注意,在本例中,您最初只有一个序列,但是您将它划分为许多序列来创建窗口。

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

    • 你可以有很多单独序列的批次
    • 序列之所以是序列,是因为它们是一步一步进化的(通常是时间步)

    用“单层”实现每个案例

    实现标准多对多:

    StandardManyToMany .

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

    outputs = LSTM(units, return_sequences=True)(inputs)
    
    
    #output_shape -> (batch_size, steps, units)
    

    多对一:

    使用完全相同的层,keras将做完全相同的内部预处理,但当你使用return_sequences=False(或简单地忽略这个参数)时,keras将自动丢弃最后一个步骤:

    ManyToOne .

    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的标准行为,我们需要按步骤输入,因此,我们只需重复输入所需的长度:

    OneToManyRepeat

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

    理解有状态=正确

    现在是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)

    有状态的一对多=True

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

    请注意,图中的行为不是“由”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)
    

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

    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()
    

    有状态的多对多=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)
    

    培训:

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

    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()
    

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

    实现复杂配置

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

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

    一个已经出现的有趣的例子是“自动编码器”,它有一个“多对一编码器”,后面跟着一个“一对多”解码器:

    编码器:

    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)
    

    Autoencoder:

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

    fit(X,X)训练

    额外的解释

    如果你想了解lstm中如何计算步骤的详细信息,或者上面的stateful=True情况的详细信息,你可以在这个答案中阅读更多:关于“理解Keras LSTMs”的疑问

更多细节请参考这个博客动画RNN, LSTM和GRU

下图为您提供了LSTM的更好视图。这是一个LSTM细胞。 This figure

.

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

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

This image

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

现在为了更好地理解,我再次使用丹尼尔·莫勒的例子: 我们有10个油罐。对于每一个,我们测量2个特征:温度,压力,每小时测量5次。 现在参数是:

  • batch_size =在一次正向/向后传递中使用的样本数量(默认=32)——>例如,如果你有1000个样本,你将batch_size设置为100,那么模型将需要10次迭代来通过网络(1 epoch)传递所有的样本。批处理大小越大,所需的内存空间就越大。因为本例中的样本数量较少,所以我们认为batch_size等于所有样本= 10
  • __abc0 = 5
  • __abc0 = 2
  • 单位 =它是一个正整数,决定隐藏状态和单元格状态的维度,换句话说,传递给下一个LSTM单元格的参数数量。它可以根据特征和时间步骤任意或经验地选择。使用更多的单位将导致更高的精度和更多的计算时间。但可能会导致过度拟合。
  • input_shape = (batch_size, timesteps, features) = (10,5,2)
  • <李> output_shape:
    • (batch_size, timesteps, units)如果return_sequences = True
    • (batch_size, units)如果return_sequences = False