长期短期记忆(LSTM)是由三个内部闸(internal gates)所构建成的循环神经网络(recurrent neuralnetwork)。
与基本RNN (vanilla RNN)不同的是,LSTM的这些内部闸的设计可以允许整个模型使用反向传播(backpropagation)来训练模型,并避免梯度消失(gradients vanishing)的问题。
在Keras深度学习库中,可以使用LSTM()类别来创建LSTM神经层。而且每一层LSTM单元都允许我们指定图层内存储单元的数量。层中的每个LSTM单元的内部状态,通常缩写为“c”,并输出隐藏状态,通常缩写为“h”。
Keras API允许我们访问这些"内部状态"数据,这些数据在开发复杂的循环神经网络架构(如encoder-decoder
模型)时可能有用,甚至是必需的。
每个LSTM单元将为每个输入来输出一个隐藏状态" h "。
h = LSTM(X)
我们可以在Keras中用一个非常小的模型来观察这一点,该模型具有单个LSTM层(其本身包含单个"LSTM"单元)。
在这个例子中,我们将有一个有三个时间步(每个时间歩只有一个特征)的输入样本:
timestep_1 = 0.1
timestep_2 = 0.2
timestep_3 = 0.3
from keras.models import Model
from keras.layers import Input
from keras.layers import LSTM
import numpy as np
# 定义模型架构
input_x = Input(shape=(, ))
lstm_1 = LSTM()(input_x)
model = Model(inputs = input_x, outputs = lstm_1)
# LSTM的模型需要的输入张量格式为
# (batch_size,timesteps,input_features)
data = np.array([0.1, 0.2, 0.3]).reshape((,,))
# 打印模型的输出
print(model.predict(data))
[[-0.07730947]]
运行以上以3个时间步长为输入序列来让LSTM输出LSTMcell的内部隐藏状态" h "。
由于LSTM权重和单元状态的随机初始化,你的具体输出值会有所不同。
如果有需要, 我们也可要求Keras来输出每个输入时间步的隐藏状态。这可以通过在定义LSTM层时将return_sequences
属性设置为True
,如下所示:
LSTM(, return_sequences=True)
我们可以修改更新前一个例子。
from keras.models import Model
from keras.layers import Input
from keras.layers import LSTM
import numpy as np
# 定义模型架构
input_x = Input(shape=(, ))
lstm_1 = LSTM(, return_sequences=True)(input_x)
model = Model(inputs = input_x, outputs = lstm_1)
# LSTM的模型需要的输入张量格式为:
# (batch_size,timesteps,input_features)
data = np.array([0.1, 0.2, 0.3]).reshape((,,))
# 打印模型的输出
print(model.predict(data))
[[[ 0.00558797]
[ 0.01459772]
[ 0.02498127]]]
运行该范例将返回包含了"3"个值的序列,每一个隐藏状态输出会对应到每个输入时间步。
LSTM单元或单元层的输出被称为隐藏状态。 这很令人困惑,因为每个LSTM单元保留一个不输出的内部状态,称为单元状态或"c"。通常,我们不需要访问单元状态,除非我们正在开发复杂的模型,其中后续神经层可能需要使用另一层的最终单元状态(例如encoder-decoder模型)来初始化其单元状态。 Keras为LSTM层提供了return_state参数,以提供对隐藏状态输出(state_h)和单元状态(state_c)的访问。 例如: lstm1, state_h, state_c = LSTM(1, return_state=True) 这可能看起来很混乱,因为lstm1和state_h都指向相同的隐藏状态输出。 这两个张量分开的原因将在其它的文章中会进行清楚的解释。我们可以通过下面列出的工作范例来演示如何访问LSTM层中单元格的隐藏和单元状态。
from keras.models import Model
from keras.layers import Input
from keras.layers import LSTM
import numpy as np
# 定义模型架构
input_x = Input(shape=(, ))
lstm_1, state_h, state_c = LSTM(, return_state=True)(input_x)
model = Model(inputs=input_x, outputs=[lstm_1, state_h, state_c])
# LSTM的模型需要的输入张量格式为:
# (batch_size,timesteps,input_features)
data = np.array([0.1, 0.2, 0.3]).reshape((,,))
# 打印模型的输出
print(model.predict(data))
[array([[ 0.03827229]], dtype=float32), array([[ 0.03827229]],
dtype=float32), array([[ 0.06963068]], dtype=float32)]
运行该范例将返回3个数组:
隐藏状态和单元状态可以用来初始化具有相同单元数量的另一个LSTM层的状态。
我们可以同时访问隐藏状态序列和单元状态。 这可以通过配置LSTM层来返回序列和返回状态来完成。
lstm_1, state_h, state_c = LSTM(, return_sequences=True, return_state=True)
完整代码
from keras.models import Model
from keras.layers import Input
from keras.layers import LSTM
import numpy as np
# 定义模型架构
input_x = Input(shape=(, ))
lstm_1, state_h, state_c = LSTM(, return_sequences=True, return_state=True)(input_x)
model = Model(inputs=input_x, outputs=[lstm_1, state_h, state_c])
# LSTM的模型需要的輸入张量格式为:
# (batch_size,timesteps,input_features)
data = np.array([0.1, 0.2, 0.3]).reshape((,,))
# 打印模型的输出
print(model.predict(data))
我们可以同时访问隐藏状态序列和单元状态。 这可以通过配置LSTM层来返回序列和返回状态来完成。
lstm_1, state_h, state_c = LSTM(, return_sequences=True, return_state=True)
完整代码
from keras.models import Model
from keras.layers import Input
from keras.layers import LSTM
import numpy as np
# 第一模型架构
input_x = Input(shape=(, ))
lstm_1, state_h, state_c = LSTM(, return_sequences=True, return_state=True)(input_x)
model = Model(inputs=input_x, outputs=[lstm_1, state_h, state_c])
# LSTM的模型需要的輸入张量格式为:
# (batch_size,timesteps,input_features)
data = np.array([0.1, 0.2, 0.3]).reshape((,,))
# 打印模型的输出
print(model.predict(data))
[array([[[ 0.02167951],[ 0.05465259],[ 0.09158381]]], dtype=float32),
array([[ 0.09158381]], dtype=float32),
array([[ 0.20488389]], dtype=float32)] 运行这个例子,我们现在可以看到为什么LSTM输出张量和隐藏状态输出张量被分开声明。这次的LSTM该层会返回每个输入时间步的隐藏状态,然后分别返回最后一个时间步的隐藏状态输出和最后输入时间步的单元状态。