作为已接受答案的补充,此答案显示了 keras 行为以及如何实现每张图片。
一般 Keras 行为
标准的 keras 内部处理总是多对多,如下图所示(这里我用了features=2,压力和温度,仅作为示例):
在这张图片中,我将步数增加到 5,以避免与其他维度混淆。
对于这个例子:
- 我们有N个油箱
- 我们花了 5 个小时每小时采取措施(时间步长)
- 我们测量了两个特征:
我们的输入数组应该是(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 层应该处理整个序列。划分窗口可能不是最好的主意。该层具有关于序列在前进时如何演变的内部状态。窗口消除了学习长序列的可能性,将所有序列限制在窗口大小。
在 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]],
....
]
请注意,在这种情况下,您最初只有一个序列,但您将其划分为多个序列以创建窗口。
“什么是序列”的概念是抽象的。重要的部分是:
- 您可以拥有包含许多单独序列的批次
- 使序列成为序列的原因在于它们按步骤(通常是时间步骤)演化
用“单层”实现每个案例
达到标准多对多:
你可以通过一个简单的 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)
了解有状态 = 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]],
] ]
注意第 1 批和第 2 批中坦克的对齐方式!这就是我们需要shuffle=False 的原因(当然,除非我们只使用一个序列)。
您可以无限地拥有任意数量的批次。 (对于每个批次的可变长度,请使用input_shape=(None,features)。
stateful=True 一对多
对于我们这里的例子,我们将只使用每批 1 个步骤,因为我们希望获得一个输出步骤并将其作为输入。
请注意,图片中的行为不是“由”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()
stateful=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()
这些答案和文件中使用了这种方法:
实现复杂的配置
在上面的所有示例中,我都展示了“一层”的行为。
当然,您可以将许多层堆叠在一起,不必全部遵循相同的模式,然后创建自己的模型。
一个有趣的例子是“自动编码器”,它有一个“多对一编码器”,后跟一个“一对多”解码器:
编码器:
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)
与fit(X,X)一起训练
补充说明
如果您想了解有关如何在 LSTM 中计算步数的详细信息,或者有关上述 stateful=True 案例的详细信息,您可以在此答案中阅读更多信息:Doubts regarding `Understanding Keras LSTMs`