了解 Keras LSTM
Understanding Keras LSTMs
我试图调和我对 LSTM 的理解,并在 this post by Christopher Olah implemented in Keras. I am following the blog written by Jason Brownlee 中针对 Keras 教程在此处指出。我主要困惑的是,
- 将数据系列重塑为
[samples, time steps, features]
和
- 有状态的 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的序列和returns一个N-look_back
数组,其中每个元素都是一个look_back
长度的序列。
什么是时间步长和特征?
可以看出,TrainX 是一个 3 维数组,Time_steps 和特征分别是最后两个维度(在此特定代码中为 3 和 1)。关于下图,这是否意味着我们正在考虑 many to one
的情况,其中粉红色框的数量为 3?或者它的字面意思是链长为 3(即仅考虑 3 个绿色框)。
当我们考虑多元序列时,特征参数是否变得相关?例如同时为两只金融股建模?
有状态 LSTM
有状态 LSTM 是否意味着我们在批次运行之间保存单元内存值?如果是这种情况,batch_size
就是一个,并且在训练运行之间重置内存,所以说它是有状态的有什么意义。我猜这与训练数据没有被打乱有关,但我不确定是如何打乱的。
有什么想法吗?
图片参考: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(TimeDistributed(Dense(vocab_len)))
正是我要找的。这是一个例子:https://github.com/sachinruk/ShakespeareBot
更新2:
我在这里总结了我对LSTM的大部分理解:https://www.youtube.com/watch?v=ywinX5wgdEU
What Time-step means: Time-steps==3
in X.shape (Descriptioning data shape) 表示有三个粉色方框。由于在 Keras 中每一步都需要一个输入,因此绿色框的数量通常应该等于红色框的数量。除非你破解结构。
多对多与多对一:在keras中,初始化LSTM
或GRU
时有一个return_sequences
参数或 SimpleRNN
。当return_sequences
为False
(默认),则为多对一,如图所示。它的return形状是(batch_size, hidden_unit_length)
,代表最后一个状态。当return_sequences
为True
时,则为多对多。它的 return 形状是 (batch_size, time_step, hidden_unit_length)
特征参数是否变得相关:特征参数意味着"How big is your red box"或者每一步的输入维度是多少。如果您想根据 8 种市场信息进行预测,那么您可以使用 feature==8
.
生成数据
Stateful:可以查the source code。初始化状态时,如果stateful==True
,则使用上次训练的状态作为初始状态,否则生成新的状态。我还没有打开 stateful
。但是,我不同意 batch_size
只能在 stateful==True
时为 1。
目前,您使用收集的数据生成数据。想象一下您的股票信息以流的形式出现,而不是等待一天收集所有顺序,您希望生成输入数据 在线 而 training/predicting 通过网络。如果你有400只股票共享一个网络,那么你可以设置batch_size==400
。
当你在 RNN 的最后一层有 return_sequences 时,你不能使用简单的密集层而是使用 TimeDistributed。
这是一段代码示例,可能会对其他人有所帮助。
字数=keras.layers.Input(batch_shape=(None,self.maxSequenceLength),姓名="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
- 温度 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]],
]
滑动输入windows
通常,LSTM 层应该处理整个序列。划分 windows 可能不是最好的主意。该层具有关于序列在前进时如何演变的内部状态。 Windows 消除学习长序列的可能性,将所有序列限制在 window 大小。
在windows中,每个window都是一个很长的原始序列的一部分,但在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]],
....
]
请注意,在这种情况下,您最初只有一个序列,但您将它分成许多序列以创建 windows。
"what is a sequence"的概念比较抽象。重要的部分是:
- 你可以有许多单独序列的批次
- 使序列成为序列的原因是它们按步骤(通常是时间步骤)进化
用"single layers"
实现每个案例
达到标准多对多:
您可以使用简单的 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 允许我们分阶段输入 "parts" 个序列。区别是:
- 在
stateful=False
中,第二批包含全新的序列,独立于第一批
- 在
stateful=True
中,第二批继续第一批,扩展相同的序列。
这也类似于 windows 中的序列划分,有以下两个主要区别:
- 这些windows不要叠加!!
stateful=True
将看到这些 windows 连接为一个长序列
在 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
对于我们这里的例子,我们将每批只使用 1 个步骤,因为我们想要获得一个输出步骤并将其作为输入。
请注意图中的行为不是"caused by"stateful=True
。我们将在下面的手动循环中强制执行该行为。在这个例子中,stateful=True
是我们 "allows" 停止序列,操纵我们想要的,并从我们停止的地方继续。
老实说,对于这种情况,重复方法可能是更好的选择。但由于我们正在研究 stateful=True
,这是一个很好的例子。使用它的最佳方法是下一个 "many to many" 案例。
图层:
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
现在,在这里,我们得到一个非常好的应用程序:给定一个输入序列,尝试预测其未来的未知步骤。
我们使用与上述 "one to many" 相同的方法,不同之处在于:
- 我们将使用序列本身作为目标数据,领先一步
- 我们知道部分序列(因此我们丢弃这部分结果)。
图层(同上):
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)
预测:
我们预测的第一阶段涉及 "ajusting the states"。这就是为什么我们要再次预测整个序列,即使我们已经知道它的这一部分:
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()
这些答案和文件中使用了这种方法:
- Predicting a multiple forward time step of a time series using LSTM
- https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb
实现复杂配置
在上面的所有示例中,我都展示了 "one layer" 的行为。
当然,您可以将许多层堆叠在一起,不必都遵循相同的模式,并创建您自己的模型。
一个有趣的例子是 "autoencoder" 有一个 "many to one encoder" 后面跟着一个 "one to many" 解码器:
编码器:
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)
解码器:
使用"repeat"方法;
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
个案例的详细信息,您可以在这个答案中阅读更多内容:
有关详细信息,请参阅此博客 Animated RNN, LSTM and GRU。
下图让您更好地了解LSTM。这是一个 LSTM 单元。
如你所见,X 有 3 features
(绿色圆圈)所以这个单元格的输入是一个维度为 3 的向量,隐藏状态有 2 units
(红色圆圈)所以输出此单元格(以及单元格状态)的向量是 2 维向量。
下图显示了一个具有 3 个时间步长(3 个 LSTM 单元)的 LSTM 层的示例:
** 一个模型可以有多个 LSTM 层。
现在我再次使用 Daniel Möller 的示例以便更好地理解:
我们有10个油罐。对于它们中的每一个,我们测量 2 个特征:温度、压力,每隔一小时测量一次,共测量 5 次。
现在参数是:
- batch_size = 一次 forward/backward 遍中使用的样本数(默认值=32)--> 例如,如果您有 1000 个样本并且您将 batch_size 设置为 100,然后模型将进行 10 次迭代以通过网络(1 个纪元)传递所有样本一次。批次大小越大,您需要的内存越多 space。由于本例中的样本数量较少,我们认为batch_size等于所有样本=10
- 时间步长 = 5
- 特征 = 2
- units = 它是一个正整数,决定了隐藏状态和单元状态的维度,或者换句话说,传递给下一个 LSTM 单元的参数数量。它可以根据特征和时间步长任意或凭经验选择。使用更多的单位将导致更高的准确性和更多的计算时间。但是可能会造成过拟合。
- input_shape = (batch_size, 时间步长, 特征) = (10,5,2)
- output_shape:
- (batch_size, 时间步长, 单位) if return_sequences=True
- (batch_size, 单位) if return_sequences=False
我试图调和我对 LSTM 的理解,并在 this post by Christopher Olah implemented in Keras. I am following the blog written by Jason Brownlee 中针对 Keras 教程在此处指出。我主要困惑的是,
- 将数据系列重塑为
[samples, time steps, features]
和 - 有状态的 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的序列和returns一个N-look_back
数组,其中每个元素都是一个look_back
长度的序列。
什么是时间步长和特征?
可以看出,TrainX 是一个 3 维数组,Time_steps 和特征分别是最后两个维度(在此特定代码中为 3 和 1)。关于下图,这是否意味着我们正在考虑 many to one
的情况,其中粉红色框的数量为 3?或者它的字面意思是链长为 3(即仅考虑 3 个绿色框)。
当我们考虑多元序列时,特征参数是否变得相关?例如同时为两只金融股建模?
有状态 LSTM
有状态 LSTM 是否意味着我们在批次运行之间保存单元内存值?如果是这种情况,batch_size
就是一个,并且在训练运行之间重置内存,所以说它是有状态的有什么意义。我猜这与训练数据没有被打乱有关,但我不确定是如何打乱的。
有什么想法吗? 图片参考: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(TimeDistributed(Dense(vocab_len)))
正是我要找的。这是一个例子:https://github.com/sachinruk/ShakespeareBot
更新2:
我在这里总结了我对LSTM的大部分理解:https://www.youtube.com/watch?v=ywinX5wgdEU
What Time-step means: Time-steps==3
in X.shape (Descriptioning data shape) 表示有三个粉色方框。由于在 Keras 中每一步都需要一个输入,因此绿色框的数量通常应该等于红色框的数量。除非你破解结构。
多对多与多对一:在keras中,初始化LSTM
或GRU
时有一个return_sequences
参数或 SimpleRNN
。当return_sequences
为False
(默认),则为多对一,如图所示。它的return形状是(batch_size, hidden_unit_length)
,代表最后一个状态。当return_sequences
为True
时,则为多对多。它的 return 形状是 (batch_size, time_step, hidden_unit_length)
特征参数是否变得相关:特征参数意味着"How big is your red box"或者每一步的输入维度是多少。如果您想根据 8 种市场信息进行预测,那么您可以使用 feature==8
.
Stateful:可以查the source code。初始化状态时,如果stateful==True
,则使用上次训练的状态作为初始状态,否则生成新的状态。我还没有打开 stateful
。但是,我不同意 batch_size
只能在 stateful==True
时为 1。
目前,您使用收集的数据生成数据。想象一下您的股票信息以流的形式出现,而不是等待一天收集所有顺序,您希望生成输入数据 在线 而 training/predicting 通过网络。如果你有400只股票共享一个网络,那么你可以设置batch_size==400
。
当你在 RNN 的最后一层有 return_sequences 时,你不能使用简单的密集层而是使用 TimeDistributed。
这是一段代码示例,可能会对其他人有所帮助。
字数=keras.layers.Input(batch_shape=(None,self.maxSequenceLength),姓名="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
- 温度 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]],
]
滑动输入windows
通常,LSTM 层应该处理整个序列。划分 windows 可能不是最好的主意。该层具有关于序列在前进时如何演变的内部状态。 Windows 消除学习长序列的可能性,将所有序列限制在 window 大小。
在windows中,每个window都是一个很长的原始序列的一部分,但在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]],
....
]
请注意,在这种情况下,您最初只有一个序列,但您将它分成许多序列以创建 windows。
"what is a sequence"的概念比较抽象。重要的部分是:
- 你可以有许多单独序列的批次
- 使序列成为序列的原因是它们按步骤(通常是时间步骤)进化
用"single layers"
实现每个案例达到标准多对多:
您可以使用简单的 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 允许我们分阶段输入 "parts" 个序列。区别是:
- 在
stateful=False
中,第二批包含全新的序列,独立于第一批 - 在
stateful=True
中,第二批继续第一批,扩展相同的序列。
这也类似于 windows 中的序列划分,有以下两个主要区别:
- 这些windows不要叠加!!
stateful=True
将看到这些 windows 连接为一个长序列
在 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
对于我们这里的例子,我们将每批只使用 1 个步骤,因为我们想要获得一个输出步骤并将其作为输入。
请注意图中的行为不是"caused by"stateful=True
。我们将在下面的手动循环中强制执行该行为。在这个例子中,stateful=True
是我们 "allows" 停止序列,操纵我们想要的,并从我们停止的地方继续。
老实说,对于这种情况,重复方法可能是更好的选择。但由于我们正在研究 stateful=True
,这是一个很好的例子。使用它的最佳方法是下一个 "many to many" 案例。
图层:
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
现在,在这里,我们得到一个非常好的应用程序:给定一个输入序列,尝试预测其未来的未知步骤。
我们使用与上述 "one to many" 相同的方法,不同之处在于:
- 我们将使用序列本身作为目标数据,领先一步
- 我们知道部分序列(因此我们丢弃这部分结果)。
图层(同上):
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)
预测:
我们预测的第一阶段涉及 "ajusting the states"。这就是为什么我们要再次预测整个序列,即使我们已经知道它的这一部分:
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()
这些答案和文件中使用了这种方法:
- Predicting a multiple forward time step of a time series using LSTM
- https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb
实现复杂配置
在上面的所有示例中,我都展示了 "one layer" 的行为。
当然,您可以将许多层堆叠在一起,不必都遵循相同的模式,并创建您自己的模型。
一个有趣的例子是 "autoencoder" 有一个 "many to one encoder" 后面跟着一个 "one to many" 解码器:
编码器:
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)
解码器:
使用"repeat"方法;
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
个案例的详细信息,您可以在这个答案中阅读更多内容:
有关详细信息,请参阅此博客 Animated RNN, LSTM and GRU。
下图让您更好地了解LSTM。这是一个 LSTM 单元。
如你所见,X 有 3 features
(绿色圆圈)所以这个单元格的输入是一个维度为 3 的向量,隐藏状态有 2 units
(红色圆圈)所以输出此单元格(以及单元格状态)的向量是 2 维向量。
下图显示了一个具有 3 个时间步长(3 个 LSTM 单元)的 LSTM 层的示例:
** 一个模型可以有多个 LSTM 层。
现在我再次使用 Daniel Möller 的示例以便更好地理解: 我们有10个油罐。对于它们中的每一个,我们测量 2 个特征:温度、压力,每隔一小时测量一次,共测量 5 次。 现在参数是:
- batch_size = 一次 forward/backward 遍中使用的样本数(默认值=32)--> 例如,如果您有 1000 个样本并且您将 batch_size 设置为 100,然后模型将进行 10 次迭代以通过网络(1 个纪元)传递所有样本一次。批次大小越大,您需要的内存越多 space。由于本例中的样本数量较少,我们认为batch_size等于所有样本=10
- 时间步长 = 5
- 特征 = 2
- units = 它是一个正整数,决定了隐藏状态和单元状态的维度,或者换句话说,传递给下一个 LSTM 单元的参数数量。它可以根据特征和时间步长任意或凭经验选择。使用更多的单位将导致更高的准确性和更多的计算时间。但是可能会造成过拟合。
- input_shape = (batch_size, 时间步长, 特征) = (10,5,2)
- output_shape:
- (batch_size, 时间步长, 单位) if return_sequences=True
- (batch_size, 单位) if return_sequences=False