作为公认答案的补充,这个答案展示了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”的质疑