python - 在 Keras 中为每个具有不同隐藏大小和多个 LSTM 层的小批量设置隐藏状态
问题描述
我使用 Keras 和 TensorFlow 作为后端创建了一个 LSTM。在将 num_step 为 96 的小批量提供给训练之前,LSTM 的隐藏状态设置为前一个时间步的真值。
首先是参数和数据:
batch_size = 10
num_steps = 96
num_input = num_output = 2
hidden_size = 8
X_train = np.array(X_train).reshape(-1, num_steps, num_input)
Y_train = np.array(Y_train).reshape(-1, num_steps, num_output)
X_test = np.array(X_test).reshape(-1, num_steps, num_input)
Y_test = np.array(Y_test).reshape(-1, num_steps, num_output)
Keras 模型由两个 LSTM 层和一个层组成,用于将输出修剪为 num_output,即 2:
model = Sequential()
model.add(LSTM(hidden_size, batch_input_shape=((batch_size, num_steps, num_input)),
return_sequences=True, stateful = True)))
model.add(LSTM(hidden_size, return_sequences=True)))
model.add(Dropout(0.2))
model.add(TimeDistributed(Dense(num_output, activation='softmax')))
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])
生成器以及训练(hidden_states[x] 的形状为 (2,)):
def gen_data():
x = np.zeros((batch_size, num_steps, num_input))
y = np.zeros((batch_size, num_steps, num_output))
while True:
for i in range(batch_size):
model.layers[0].states[0] = K.variable(value=hidden_states[gen_data.current_idx]) # hidden_states[x] has shape (2,)
x[i, :, :] = X_train[gen_data.current_idx]
y[i, :, :] = Y_train[gen_data.current_idx]
gen_data.current_idx += 1
yield x, y
gen_data.current_idx = 0
for epoch in range(100):
model.fit_generator(generate_data(), len(X_train)//batch_size, 1,
validation_data=None, max_queue_size=1, shuffle=False)
gen_data.current_idx = 0
这段代码没有给我错误,但我有两个问题:
1) 在生成器内部,我将 LSTM 的隐藏状态设置为形状为 (2,)model.layers[0].states[0]
的变量。hidden_states[gen_data.current_idx]
为什么这对于隐藏大小大于 2 的 LSTM 是可能的?
2) 中的值hidden_states[gen_data.current_idx]
也可以是 Keras 模型的输出。两层 LSTM 以这种方式设置隐藏状态有意义吗?
解决方案
LSTM 中的状态
LSTM 由计算cell state
和的门组成hidden state
。
在图中,从 LSTM 右侧出来的顶部箭头是单元状态 ( c_t
),底部箭头是隐藏状态 ( h_t
)。单元状态是门控操作的结果,状态的大小与 LSTM 的大小相同hidden_size
。每次展开(及其对应的输入 X)都会产生自己的单元格状态。在 LSTM 的情况下,单元状态由 (batch_size x hidden_size) 的两个值 hidden_state( h_t
) 和 (batch_size x hidden_size) 的 cell_state ( c_t
) 组成。
batch_size = 2
num_steps = 5
num_input = num_output = 1
hidden_size = 8
inputs = Input(batch_shape=(batch_size,num_steps, num_input))
lstm, state_h, state_c = LSTM(hidden_size, return_state=True, return_sequences=True)(inputs)
model = Model(inputs=inputs, outputs=[state_h, state_c])
print (model.predict(np.zeros((batch_size, num_steps, num_input))))
print (model.layers[1].cell.state_size)
注意:在 GRU/RNN 的情况下,没有单元状态,只有隐藏状态,因此单元状态h_t
的大小(batch_size,hidden_size)
参考:
LSTM的 Keras 实现
状态张量的数量为 1(对于 RNN 和 GRU)或 2(对于 LSTM)。
喂养状态
在您的示例中,layers[0]
指的是 1 LSTM 并layers[1]
指的是 2nd LSTM。如果您的意图是将c_t
第 n 个批次的单元状态 ( ) 初始化为 (n-1) 的单元状态,即前一批有两个选项
您在生成器中执行的方式,但
states[1]
如果需要c_t
, 请使用states[0]
forh_t
。同样layers[0]
用于第一个 LSTM 和layers[1]
第二个 LSTM。但改用set_value
方法。请参阅下面的编辑。使用 keras
Stateful=True
:将 stateful 设置为 true 时,LSTM 状态不会在每批后重置。因此,如果您有一个包含 5 个数据样本(每个都有一定序列长度)的批次,您将获得 5 个数据样本中每一个的单元状态。将 stateful 设置为 true 这些状态用于为下一批初始化下一批单元状态。
编辑:
该方法set_value
应该用于设置张量变量的值。该代码model.layers[0].states[0] = K.variable(value=hidden_states[gen_data.current_idx])
是有效的,因为它所做的是将指向大小变量 (batch_size X hidden_size) 的 state[0] 更改为大小变量 (batch_size x 2)。它不是改变张量变量的值,而是使其指向不同维度的新张量变量。
测试代码:
print (model.layers[0].states[0], hex(id(model.layers[0].states[0])))
model.layers[0].states[0]= K.variable(np.random.randn(10,2))
print (model.layers[0].states[0], hex(id(model.layers[0].states[0])))
输出
<tf.Variable 'lstm_18/Variable:0' shape=(10, 8) dtype=float32_ref> 0x7f8812e6ee10
<tf.Variable 'Variable_2:0' shape=(10, 2) dtype=float32_ref> 0x7f881269afd0
如您所见,它们是两个不同的变量。正确的方法是
print (model.layers[0].states[0], hex(id(model.layers[0].states[0])))
K.set_value(model.layers[0].states[0], np.random.randn(10,8))
print (model.layers[0].states[0], hex(id(model.layers[0].states[0])))
输出
<tf.Variable 'lstm_20/Variable:0' shape=(10, 8) dtype=float32_ref> 0x7f881138eb70
<tf.Variable 'lstm_20/Variable:0' shape=(10, 8) dtype=float32_ref> 0x7f881138eb70
如果您的代码是固定的,那么
K.set_value(model.layers[0].states[0], np.random.randn(10,2))
由于张量的大小与您设置的值的大小不匹配,将引发错误。
推荐阅读
- selenium - 基于兄弟文本值的 Xpath
- docker - Docker - 哪个版本支持 trust 命令?
- android - Android NDK 应用程序中的 android.app.NativeActivity.onCreate() 异常(仅限 Android 6.0)
- python - 为什么 AccessibleObjectFromWindow() 仅适用于 Microsoft Office 应用程序?
- c# - 如何设置非托管应用程序的首选 CLR (
) 在运行时? - reactjs - 如何在父组件中将道具从子组件传递给 map()?
- python - 计算python中数据框每一列的最后一个非零值的累积和
- python-3.x - 如何根据纬度、经度从 Netcdf 文件更新熊猫数据框
- swift - Swift:如何在 GET 请求中的 forHTTPHeaderField 中添加字典格式
- knockout.js - 如何在一个页面中多次组合相同的小部件