为啥 LayerNormBasicLSTMCell 比 LSTMCell 慢得多且精度低得多?

Posted

技术标签:

【中文标题】为啥 LayerNormBasicLSTMCell 比 LSTMCell 慢得多且精度低得多?【英文标题】:Why is LayerNormBasicLSTMCell much slower and less accurate than LSTMCell?为什么 LayerNormBasicLSTMCell 比 LSTMCell 慢得多且精度低得多? 【发布时间】:2017-12-22 08:05:23 【问题描述】:

我最近发现 LayerNormBasicLSTMCell 是 LSTM 的一个版本,实现了 Layer Normalization 和 dropout。因此,我将使用 LSTMCell 的原始代码替换为 LayerNormBasicLSTMCell。这种变化不仅将测试准确率从 ~96% 降低到 ~92%,而且训练时间更长(~33 小时)(原始训练时间约为 6 小时)。所有参数都相同:epochs 数(10),堆叠层数(3),隐藏向量大小数(250),dropout keep prob(0.5),...硬件也是一样的。

我的问题是:我在这里做错了什么?

我的原始模型(使用 LSTMCell):

# Batch normalization of the raw input
tf_b_VCCs_AMs_BN1 = tf.layers.batch_normalization(
    tf_b_VCCs_AMs, # the input vector, size [#batches, #time_steps, 2]
    axis=-1, # axis that should be normalized 
    training=Flg_training, # Flg_training = True during training, and False during test
    trainable=True,
    name="Inputs_BN"
    )

# Bidirectional dynamic stacked LSTM

##### The part I changed in the new model (start) #####
dropcells = []
for iiLyr in range(3):
    cell_iiLyr = tf.nn.rnn_cell.LSTMCell(num_units=250, state_is_tuple=True)
    dropcells.append(tf.nn.rnn_cell.DropoutWrapper(cell=cell_iiLyr, output_keep_prob=0.5))
##### The part I changed in the new model (end) #####

MultiLyr_cell = tf.nn.rnn_cell.MultiRNNCell(cells=dropcells, state_is_tuple=True)

outputs, states  = tf.nn.bidirectional_dynamic_rnn(
    cell_fw=MultiLyr_cell, 
    cell_bw=MultiLyr_cell,
    dtype=tf.float32,
    sequence_length=tf_b_lens, # the actual lengths of the input sequences (tf_b_VCCs_AMs_BN1)
    inputs=tf_b_VCCs_AMs_BN1,
    scope = "BiLSTM"
    )

我的新模型(使用 LayerNormBasicLSTMCell):

...
dropcells = []
for iiLyr in range(3):
    cell_iiLyr = tf.contrib.rnn.LayerNormBasicLSTMCell(
        num_units=250,
        forget_bias=1.0,
        activation=tf.tanh,
        layer_norm=True,
        norm_gain=1.0,
        norm_shift=0.0,
        dropout_keep_prob=0.5
        )
    dropcells.append(cell_iiLyr)
...

【问题讨论】:

一个想法:[***.com/questions/43234667/… 可能是问题吗? tf.layers.batch_normalization 中的均值和方差似乎没有自动更新。我想知道tf.contrib.rnn.LayerNormBasicLSTMCell 是否有同样的问题。 @FariborzGhavamian,我对两个归一化函数都使用了第二种方法(即update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)with tf.control_dependencies(update_ops): ...)。 关于训练时间:我在tensorflow网站上找到的:tensorflow.org/performance/performance_guide#common_fused_ops。您可以打开一个名为 fused 的参数并获得 12%-30% 的加速。 @FariborzGhavamian,感谢您提供的指南链接。我将通读整个性能指南。它们看起来都非常有用。我会试试看(包括参数fused)。 @FariborzGhavamian,fused参数与batch_normalization有关,与layer_normalization无关。 【参考方案1】:

也许应该为dropout_keep_prob 分配一个占位符而不是一个常量值。尝试在训练时分配0.5,在推理时分配1.0。只是猜测。

【讨论】:

这绝对是必需的。 Dropout 显然需要在评估期间关闭,并且单元格无法知道它处于哪种模式,因此您需要在评估期间通过将dropout_keep_prob 设置为 1.0 来告诉它。 感谢您提到dropout_keep_prob 的值对于训练和测试应该不同。实际代码由特定于模式的值分配。【参考方案2】:

关于培训时间:我偶然发现了这篇博文:http://olavnymoen.com/2016/07/07/rnn-batch-normalization。见最后一张图。批量标准化 lstm 比普通 lstm 慢 3 倍以上。作者认为原因是批量统计计算。

关于准确性:我不知道。

【讨论】:

我没有仔细阅读帖子,我认为帖子在谈论Batch Normalization而不是Layer Normalization(问题中的那个)? 是的,这篇文章是关于批量标准化的。但它和层归一化之间的共同点是统计数据(均值和方差)的计算。尽管统计数据是基于不同的数据(BN:批量,LN:层)计算的,但它似乎是额外的计算成本。 我不明白为什么简单的统计数据(均值或方差)计算要花费这么多时间(尤其是使用 GPU)。【参考方案3】:

批量归一化和层归一化不只是对小批量的输入分布进行归一化,它们还添加了新的可学习变量 beta 和 gamma。这是每个输入的每个维度的标量,因此四个输入乘以输入维度的两倍。

我认为是大量新的可训练变量导致了减速。

顺便说一句,如果您关闭 layer_norm,它会大大加快速度,因此这将支持这样的假设,即问题在于层标准化。

【讨论】:

在文献中,层标准化 (arxiv.org/pdf/1607.06450.pdf) 确实加快了训练时间。所以说关闭 layer_norm 会大大加快速度是完全错误的。 我的意思是它加快了单个批次的时间。当然,打开它可能意味着损失减少得更快,从而使达到一定损失的总时间更短。

以上是关于为啥 LayerNormBasicLSTMCell 比 LSTMCell 慢得多且精度低得多?的主要内容,如果未能解决你的问题,请参考以下文章

你应该同步运行方法吗?为啥或者为啥不?

为啥使用 glTranslatef?为啥不直接更改渲染坐标?

为啥 DataGridView 上的 DoubleBuffered 属性默认为 false,为啥它受到保护?

为啥需要softmax函数?为啥不简单归一化?

为啥 g++ 需要 libstdc++.a?为啥不是默认值?

为啥或为啥不在 C++ 中使用 memset? [关闭]