我无法理解张量流 1 中的 LSTM 实现



我一直在研究神经网络架构中LSTM层的实现。LSTM 层在其中定义如下。我无法理解此代码。我在代码片段之后列出了我的疑问。

代码来源:https://gist.github.com/awjuliani/66e8f477fc1ad000b1314809d8523455#file-a3c-py

lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(RNN_SIZE,state_is_tuple=True)
c_init = np.zeros((1, lstm_cell.state_size.c), np.float32)
h_init = np.zeros((1, lstm_cell.state_size.h), np.float32)
state_init = [c_init, h_init]
c_in = tf.placeholder(tf.float32, [1, lstm_cell.state_size.c])
h_in = tf.placeholder(tf.float32, [1, lstm_cell.state_size.h])
state_in = (c_in, h_in)
rnn_in = tf.expand_dims(self.h3, [0])
step_size = tf.shape(inputs)[:1]
state_in = tf.nn.rnn_cell.LSTMStateTuple(c_in, h_in)
lstm_outputs, lstm_state = tf.nn.dynamic_rnn(
lstm_cell, rnn_in, initial_state=state_in, sequence_length=step_size,
time_major=False)
lstm_c, lstm_h = lstm_state
state_out = (lstm_c[:1, :], lstm_h[:1, :])
self.rnn_out = tf.reshape(lstm_outputs, [-1, RNN_SIZE])

这是我的疑问:

  1. 我知道我们需要初始化一个随机上下文并隐藏 载体传递到我们的第一个 LSTM 单元。但是为什么要同时初始化c_init,h_init然后c_in,h_in。它们的目的是什么? 它们彼此之间有何不同?(state_in和state_init一样?
  2. 为什么我们使用 LSTMStateTuple?
def work(self, max_episode_length, gamma, sess, coord, saver, dep):
........
rnn_state = self.local_AC.state_init
def train(self, rollout, sess, gamma, bootstrap_value):
......
rnn_state = self.local_AC.state_init
feed_dict = {self.local_AC.target_v: discounted_rewards,
self.local_AC.inputs: np.vstack(observations),
self.local_AC.actions: actions,
self.local_AC.advantages: advantages,
self.local_AC.state_in[0]: rnn_state[0],
self.local_AC.state_in[1]: rnn_state[1]}

在工作开始时,然后 在训练新批次之前,网络状态用零填充

  1. 我知道我们需要初始化一个随机上下文和隐藏向量以传递给我们的第一个 LSTM 单元。但是为什么要同时初始化c_init,h_init,然后c_in,h_in。它们的目的是什么?它们彼此之间有何不同?(state_in和state_init一样?

要开始使用 LSTM,应该初始化其单元和状态状态 - 分别命名为ch。对于每个输入,这些状态都被视为"空",应用零初始化。所以,我们有这里

c_in = tf.placeholder(tf.float32, [1, lstm_cell.state_size.c])
h_in = tf.placeholder(tf.float32, [1, lstm_cell.state_size.h])
state_in = (c_in, h_in)
state_in = tf.nn.rnn_cell.LSTMStateTuple(c_in, h_in)

为什么有两个变量,state_instate_init?第一个只是占位符,将在评估状态(即 session.run(使用第二个进行初始化。因为state_in不包含任何实际值,换句话说,numpy 数组在训练阶段使用,tf.placeholders在定义网络架构的阶段使用。

TL;DR
为什么会这样?好吧,tf1.x(曾经?(是一个相当低级的系统。它具有以下实体:

  • tf.Session又名计算会话 - 包含计算图并允许用户通过session.run向图提供输入的东西。
  • tf.Graph,这是计算图的表示。通常工程师使用tf.placeholders 和tf.Variabless 来定义图形。人们可以将它们"就像"数学运算一样连接起来:
with tf.Session() as sess:
a = tf.placeholder(tf.float32, (1,))
b = tf.Variable(1.0, dtype=tf.float32)
tf.global_variables_initializer()
c = a * b
# ...and so on
占位符
  • 占位符,但不是实际值,旨在session.run阶段用实际值填充。tf.Variables,好吧,为了优化神经网络的实际权重。为什么不是普通的 NumPy 数组,而是其他东西?这是因为 TensorFlow 会自动将每个张量和占位符作为边添加到默认计算图中(不可能对 NumPy 数组做同样的事情(;此外,它允许定义一个架构,然后用不同的输入初始化/训练它,这很好。

因此,要进行计算(向前/向后传播等(,必须将占位符和变量设置为某些值。为此,在一个简单的示例中,我们可以执行以下操作:

import tensorflow as tf
with tf.compat.v1.Session() as sess:
a = tf.compat.v1.placeholder(tf.float32, shape=())
b = tf.compat.v1.Variable(1.0, dtype=tf.float32)
init = tf.compat.v1.global_variables_initializer()
c = a + b
sess.run(init)

a_value = 2.0
result = sess.run([c], feed_dict={a: a_value})
print("value of [c]:", result)

(我在这里使用tf.compat.v1而不仅仅是tf,因为我在 tf2 环境中工作;你可以省略它( 注意两件事:首先,我创建init操作。因为在 tf1.x 中,像tf.Variable(1.0)这样的初始化变量是不够的,但用户必须有点"通知"框架有关创建和运行init操作的信息。 然后我做一个计算:我初始化一个a_value变量并将其映射到占位符a' in thesess.runmethod.Session.run' 需要将张量列表计算为第一个参数,以及从计算目标张量到其实际值所需的占位符的映射。

回到您的示例:state_in是一个占位符,state_init包含要馈送到代码中某个位置的此占位符的值。

它看起来像这样:less.run(..., feed_dict={state_in: state_init, ...}).

  1. 为什么我们使用 LSTMStateTuple?

解决问题的第二部分:看起来TensorFlow开发人员实现它是为了进行一些性能优化。从源代码:

logging.warning(
"%s: Using a concatenated state is slower and will soon be"
"deprecated. Use state_is_tuple=True.", self)

如果state_is_tuple=True,状态应该是状态元组。但我不是 100% 确定它 - 我不记得我是如何使用它的。毕竟,StateTuple只是一个具有两个命名属性的collections.namedtuplech.

最新更新