我试图调和我对lstm的理解,并在Christopher Olah在Keras中实现的这篇文章中指出。我正在关注Jason Brownlee写的关于Keras教程的博客。我最困惑的是,

将数据序列重塑为[样本,时间步长,特征], 有状态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数组,其中每个元素都是一个长度为N的look_back序列。

什么是时间步长和特征?

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

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

有状态LSTMs

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

任何想法吗? 图片参考: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(timedidistributeddense (vocab_len))就是我要找的。这里有一个例子:https://github.com/sachinruk/ShakespeareBot

更新2:

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


当前回答

首先,你要选择优秀的教程(1,2)作为开始。

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

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

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

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

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

其他回答

首先,你要选择优秀的教程(1,2)作为开始。

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

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

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

有状态的:您可以查找源代码。初始化状态时,如果stateful==True,则使用上次训练的状态作为初始状态,否则将生成一个新的状态。我还没有开启有状态。然而,我不同意当有状态==True时,batch_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,压力和温度,只是作为一个例子):

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

对于这个例子:

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

输入数组的形状应该是(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]],
  ....
        ]

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

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

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

用“单层”实现每个案例

实现标准多对多:

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

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

#output_shape -> (batch_size, steps, units)

多对一:

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

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

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“允许”我们停止序列,操作我们想要的,并从我们停止的地方继续。

老实说,对于这种情况,重复方法可能是更好的选择。但由于我们正在研究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

现在,我们有一个很好的应用:给定一个输入序列,试着预测它未来的未知步骤。

我们使用与上面的“一对多”相同的方法,不同的是:

我们将使用序列本身作为目标数据,提前一步 我们知道部分序列(所以我们放弃这部分结果)。

图层(同上):

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)

配合训练(X,X)

额外的解释

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

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

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

正如你所看到的,X有3个特征(绿色圆圈),所以这个单元格的输入是一个3维的向量,隐藏状态有2个单位(红色圆圈),所以这个单元格的输出(也是单元格状态)是一个2维的向量。

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

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

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

batch_size = number of samples used in one forward/backward pass (default=32) --> for example if you have 1000 samples and you set up the batch_size to 100 then the model will take 10 iterations to pass all of the samples once through network (1 epoch). The higher the batch size, the more memory space you'll need. Because the number of samples in this example are low, we consider batch_size equal to all of samples = 10 timesteps = 5 features = 2 units = It's a positive integer and determines the dimension of hidden state and cell state or in other words the number of parameters passed to next LSTM cell. It can be chosen arbitrarily or empirically based on the features and timesteps. Using more units will result in more accuracy and also more computational time. But it may cause over fitting. 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