使 LSTM 单元可训练
make LSTM cell trainable
我正在使用 tf.contrib.rnn.MultiRNNCell
模块制作多层 RNN。我使用以下行来定义 3 层 RNN-LSTM 网络:
n_hidden = 2
num_layers = 3
lstm_cell = tf.contrib.rnn.BasicLSTMCell(n_hidden)
stacked_lstm_cell = tf.contrib.rnn.MultiRNNCell([lstm_cell] * num_layers)
然而,对于 tensorflow 中实际发生的事情,我心中存在一些不确定性。据我所知,这段代码给了我一个计算图,其中有 3 层 LSTM 单元,每层有 2 个 LSTM 单元。我有以下疑惑:
- 这 3 个 LSTM 层之间的权重是否被视为变量?
- 如果这些权重被视为变量,它们是否在训练期间被修改?
- LSTM 单元具有诸如 forget 等运算符。这些运算符是否也被视为变量,因此在训练时进行了调整?
关于语法的一个注意事项:从 TF ~1.0 开始,您需要在循环中定义多个层,而不是使用 [cell] * num_layers
语法,因此类似于:
lstm_cells = []
for _ in range(num_layers):
cell = tf.contrib.rnn.BasicLSTMCell(n_hidden)
lstm_cells.append(cell)
stacked_lstm_cell = tf.contrib.rnn.MultiRNNCell(lstm_cells)
关于您的主要问题:
- 您的代码为您提供了一个 3 层网络 (
num_layers
),其中每一层都包含一个隐藏状态长度为 2 (n_hidden
) 的 LSTM。稍后会详细介绍。
- 三个 LSTM 层之间没有权重:每个 LSTM 将其输出提供给下一个 LSTM 的输入。
- 除非您告诉 TF 不要训练某些东西,否则您网络中的所有权重和偏差都将被视为可训练变量并通过反向传播进行训练。
- LSTM 中的遗忘和更新等操作对网络输入和网络先前隐藏状态的线性组合执行某些功能。 "linear combination" 这部分涉及由您的网络训练的权重和偏差。
看一下 LSTM
我们来看看LSTM网络架构。 This is a pretty great overview that I recommend reading。基本上,单个 LSTM 单元维护一个隐藏状态,代表它到目前为止所见的 "memory",并且在每个更新步骤中,它决定要将多少新信息与该隐藏状态中的现有信息混合使用 "gates"。它还使用一个门来确定它将输出什么。查看单个单元格的更新过程:
我们首先确定要遗忘多少旧信息(我们的遗忘门):f_k = sigmoid(W_f * [h_k-1, x_k] + b_f)
在这里,我们对网络的先前历史记录 h_k-1
与当前观察结果 x_k
进行操作。您的历史向量 h
的大小由 n_hidden
定义。权重 W_f
和偏差 b_f
将通过训练过程学习。
我们确定要合并多少新信息(我们的输入门,i_k
),并创建一些新的候选细胞状态(c'_k
):
i_k = sigmoid(W_i * [h_k-1, x_k] + b_i)
c`_k = tanh(W_c * [h_k-1, x_k] + b_c)
同样,我们正在对旧的内部状态 h_k-1
和新的观察 x_k
进行操作,以确定下一步该做什么。细胞状态c
和候选细胞状态c'
的大小是由n_hidden
决定的也。 W_*
和 b_*
是我们将学习的更多参数。
将旧信息与新的候选状态结合起来得出一个新的细胞状态:c_k = f_k * c_k-1 + i_k * c'_k
这里我们做的是逐元素乘法,而不是点积或其他任何东西。基本上我们选择保留多少旧信息 (f_k * c_k-1
),以及合并多少新信息 (i_k * c'_k
)。
最后,我们确定要使用输出门输出多少细胞状态:
o_k = sigmoid(W_o * [h_k-1, x_k] + b_o)
h_k = o_k * tanh(c_k)
所以基本上我们将新旧信息混合到内部 "cell state" c_k
,然后在 h_k
中输出一些信息。我还建议查看 gated recurrent unit (GRU) network,它的性能与 LSTM 类似,但结构更容易理解。
现在介绍多层网络的堆叠方式。基本上,你有这样的东西:
x_k ---> (network 0) --h0_k--> (network_1) --h1_k--> (network_2) --h2_k-->
因此您的观察结果进入第一个网络,然后该网络的输出作为输入馈送到下一个网络,下一个网络将其与自己的内部状态混合以产生输出,然后成为第三个网络的输入网络,依此类推,直到结束。这应该有助于学习数据中的时间结构。我对此没有很好的引用。
通常情况下,如果您正在进行分类(例如),您会在最后一个网络的输出上放置一个最终的全连接层,以确保您观察到的过程位于您正在分类的每个类别中。
可训练变量
您可以打印出您的网络将要学习的所有可训练变量,方法如下:
for var in tf.trainable_variables():
print('{}\nShape: {}'.format(var.name, var.get_shape()))
Tensorflow 通过组合不同的操作做了一些奇特的事情,因此您可能会看到一些奇怪的形状并且显然缺少权重矩阵和偏差,但它就在那里。基本上,您正在学习每个门中使用的权重和偏差。在上面,那将是:
- 权重:
W_f
、W_i
、W_c
和 W_o
每层
- 偏差:
b_f
、b_i
、b_c
和 b_o
每层
- 和附加输出层 weights/biases 添加到最后一个 LSTM 层
我更熟悉 TF 如何处理 GRU 架构,它基本上将所有门组合到一个大矩阵运算中,因此所有门都有一个组合权重矩阵和一个组合偏置向量。然后它将结果拆分到每个单独的门中,以将它们应用到正确的位置。仅供参考,以防万一您似乎没有每个单元格的每个单独步骤的权重和偏差。
我正在使用 tf.contrib.rnn.MultiRNNCell
模块制作多层 RNN。我使用以下行来定义 3 层 RNN-LSTM 网络:
n_hidden = 2
num_layers = 3
lstm_cell = tf.contrib.rnn.BasicLSTMCell(n_hidden)
stacked_lstm_cell = tf.contrib.rnn.MultiRNNCell([lstm_cell] * num_layers)
然而,对于 tensorflow 中实际发生的事情,我心中存在一些不确定性。据我所知,这段代码给了我一个计算图,其中有 3 层 LSTM 单元,每层有 2 个 LSTM 单元。我有以下疑惑:
- 这 3 个 LSTM 层之间的权重是否被视为变量?
- 如果这些权重被视为变量,它们是否在训练期间被修改?
- LSTM 单元具有诸如 forget 等运算符。这些运算符是否也被视为变量,因此在训练时进行了调整?
关于语法的一个注意事项:从 TF ~1.0 开始,您需要在循环中定义多个层,而不是使用 [cell] * num_layers
语法,因此类似于:
lstm_cells = []
for _ in range(num_layers):
cell = tf.contrib.rnn.BasicLSTMCell(n_hidden)
lstm_cells.append(cell)
stacked_lstm_cell = tf.contrib.rnn.MultiRNNCell(lstm_cells)
关于您的主要问题:
- 您的代码为您提供了一个 3 层网络 (
num_layers
),其中每一层都包含一个隐藏状态长度为 2 (n_hidden
) 的 LSTM。稍后会详细介绍。 - 三个 LSTM 层之间没有权重:每个 LSTM 将其输出提供给下一个 LSTM 的输入。
- 除非您告诉 TF 不要训练某些东西,否则您网络中的所有权重和偏差都将被视为可训练变量并通过反向传播进行训练。
- LSTM 中的遗忘和更新等操作对网络输入和网络先前隐藏状态的线性组合执行某些功能。 "linear combination" 这部分涉及由您的网络训练的权重和偏差。
看一下 LSTM
我们来看看LSTM网络架构。 This is a pretty great overview that I recommend reading。基本上,单个 LSTM 单元维护一个隐藏状态,代表它到目前为止所见的 "memory",并且在每个更新步骤中,它决定要将多少新信息与该隐藏状态中的现有信息混合使用 "gates"。它还使用一个门来确定它将输出什么。查看单个单元格的更新过程:
我们首先确定要遗忘多少旧信息(我们的遗忘门):
f_k = sigmoid(W_f * [h_k-1, x_k] + b_f)
在这里,我们对网络的先前历史记录
h_k-1
与当前观察结果x_k
进行操作。您的历史向量h
的大小由n_hidden
定义。权重W_f
和偏差b_f
将通过训练过程学习。我们确定要合并多少新信息(我们的输入门,
i_k
),并创建一些新的候选细胞状态(c'_k
):i_k = sigmoid(W_i * [h_k-1, x_k] + b_i) c`_k = tanh(W_c * [h_k-1, x_k] + b_c)
同样,我们正在对旧的内部状态
h_k-1
和新的观察x_k
进行操作,以确定下一步该做什么。细胞状态c
和候选细胞状态c'
的大小是由n_hidden
决定的也。W_*
和b_*
是我们将学习的更多参数。将旧信息与新的候选状态结合起来得出一个新的细胞状态:
c_k = f_k * c_k-1 + i_k * c'_k
这里我们做的是逐元素乘法,而不是点积或其他任何东西。基本上我们选择保留多少旧信息 (
f_k * c_k-1
),以及合并多少新信息 (i_k * c'_k
)。最后,我们确定要使用输出门输出多少细胞状态:
o_k = sigmoid(W_o * [h_k-1, x_k] + b_o) h_k = o_k * tanh(c_k)
所以基本上我们将新旧信息混合到内部 "cell state" c_k
,然后在 h_k
中输出一些信息。我还建议查看 gated recurrent unit (GRU) network,它的性能与 LSTM 类似,但结构更容易理解。
现在介绍多层网络的堆叠方式。基本上,你有这样的东西:
x_k ---> (network 0) --h0_k--> (network_1) --h1_k--> (network_2) --h2_k-->
因此您的观察结果进入第一个网络,然后该网络的输出作为输入馈送到下一个网络,下一个网络将其与自己的内部状态混合以产生输出,然后成为第三个网络的输入网络,依此类推,直到结束。这应该有助于学习数据中的时间结构。我对此没有很好的引用。
通常情况下,如果您正在进行分类(例如),您会在最后一个网络的输出上放置一个最终的全连接层,以确保您观察到的过程位于您正在分类的每个类别中。
可训练变量
您可以打印出您的网络将要学习的所有可训练变量,方法如下:
for var in tf.trainable_variables():
print('{}\nShape: {}'.format(var.name, var.get_shape()))
Tensorflow 通过组合不同的操作做了一些奇特的事情,因此您可能会看到一些奇怪的形状并且显然缺少权重矩阵和偏差,但它就在那里。基本上,您正在学习每个门中使用的权重和偏差。在上面,那将是:
- 权重:
W_f
、W_i
、W_c
和W_o
每层 - 偏差:
b_f
、b_i
、b_c
和b_o
每层 - 和附加输出层 weights/biases 添加到最后一个 LSTM 层
我更熟悉 TF 如何处理 GRU 架构,它基本上将所有门组合到一个大矩阵运算中,因此所有门都有一个组合权重矩阵和一个组合偏置向量。然后它将结果拆分到每个单独的门中,以将它们应用到正确的位置。仅供参考,以防万一您似乎没有每个单元格的每个单独步骤的权重和偏差。