作为对已接受答案的补充,本答案展示了Keras的行为以及如何实现每个图片。
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]],
]
滑动窗口的输入
通常,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)
实现多对一:
使用完全相同的层,Keras将执行完全相同的内部预处理,但当您使用return_sequences=False
(或简单地忽略此参数)时,Keras会自动丢弃前面的步骤并保留最后一个步骤:
outputs = LSTM(units)(inputs)
实现一对多
现在,这不仅仅是由keras LSTM层支持的。您将不得不创建自己的策略来复制步骤。有两种好方法:
- 通过重复张量创建一个恒定的多步输入
- 使用
stateful=True
,以循环地获取一步的输出,并将其作为下一步的输入提供(需要output_features == input_features
)
使用重复向量的一对多
为了适应keras的标准行为,我们需要按步骤输入,因此,我们只需重复要求长度的输入即可:
outputs = RepeatVector(steps)(inputs)
outputs = LSTM(units,return_sequences=True)(outputs)
了解stateful = True
现在是stateful=True
的可能用法之一(除了避免一次性加载无法适应计算机内存的数据)
Stateful允许我们分阶段输入序列的“部分”。不同之处在于:
- 在
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
是允许我们停止序列,操纵我们想要的内容,并从我们停止的地方继续的“允许”。
说实话,对于这种情况,重复的方法可能是更好的选择。但由于我们正在研究 "stateful=True",这是一个很好的例子。使用它的最佳方式是下一个 "many to many" 情况。
层:
outputs = LSTM(units=features,
stateful=True,
return_sequences=True,
input_shape=(None,features))(inputs)
现在,我们需要手动循环进行预测:
input_data = someDataWithShape((batch, 1, features))
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
model.reset_states()
使用stateful=True的多对多
现在,我们有一个非常好的应用程序:给定一个输入序列,尝试预测其未来的未知步骤。
我们使用与上面“一对多”相同的方法,不同之处在于:
- 我们将使用序列本身作为目标数据,提前一步
- 我们知道部分序列(因此我们会丢弃这部分结果)。
图层(同上):
outputs = LSTM(units=features,
stateful=True,
return_sequences=True,
input_shape=(None,features))(inputs)
培训:
我们将对模型进行训练,以预测序列的下一步:
totalSequences = someSequencesShaped((batch, steps, features))
X = totalSequences[:,:-1]
Y = totalSequences[:,1:]
for epoch in range(epochs):
model.reset_states()
model.train_on_batch(X,Y)
预测:
我们的预测的第一阶段涉及“调整状态”。这就是为什么我们要再次预测整个序列,即使我们已经知道其中的一部分:
model.reset_states()
predicted = model.predict(totalSequences)
firstNewStep = predicted[:,-1:]
现在我们像处理一对多的情况那样进入循环。但是
不要在这里重置状态!我们希望模型知道序列中的哪个步骤(并且它知道它在第一个新步骤,因为我们刚刚做出了预测)。
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
model.reset_states()
这种方法被用于以下答案和文件:
实现复杂配置
在以上所有示例中,我展示了“一层”的行为。
当然,您可以将许多层堆叠在一起,不一定都遵循相同的模式,并创建自己的模型。
一个有趣的例子是“自编码器”,它具有“多对一编码器”后跟“一对多解码器”:
编码器:
inputs = Input((steps,features))
outputs = LSTM(hidden1,return_sequences=True)(inputs)
outputs = LSTM(hidden2,return_sequences=True)(outputs)
outputs = LSTM(hidden3)(outputs)
encoder = Model(inputs,outputs)
解码器:
使用“重复”方法;
inputs = Input((hidden3,))
outputs = RepeatVector(steps)(inputs)
outputs = LSTM(hidden4,return_sequences=True)(outputs)
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
情况的详细信息,可以在此答案中阅读更多内容:关于理解Keras LSTMs的疑问。