为什么抑制权重会提高 Tensorflow 神经网络性能?

Why does supressing weights improve Tensorflow neural net performance?

我在Tensorflow中有一个2层的非卷积网络,使用tanh作为激活函数。我知道权重应该用截断的正态分布除以 sqrt(nInputs) 来初始化,例如:

weightsLayer1 = tf.Variable(tf.div(tf.truncated_normal([nInputUnits, nUnitsHiddenLayer1),math.sqrt(nInputUnits))))

作为 NN 和 Tensorflow 的新手,我错误地将其实现为 2 行只是为了使其更具可读性:

weightsLayer1 = tf.Variable(tf.truncated_normal([nInputUnits, nUnitsHiddenLayer1])
weightsLayer1 = tf.div(weightsLayer1, math.sqrt(nInputUnits))

我现在知道这是错误的,第二行导致在每个学习步骤重新计算权重。然而,令我惊讶的是,"incorrect" 实现在训练和 test/evaluation 数据集中始终产生更好的性能。我认为不正确的 2 行实现应该是火车失事,因为它正在将权重重新计算(抑制)为优化器选择的值以外的值,我预计这会在优化过程中造成严重破坏,但它实际上改善了它.有人对此有任何解释吗?我正在使用 Tensorflow adam 优化器。

更新 2016.6.22 - 更新了上面的第二个代码块。

这就是单行代码的作用:

t = tf.truncated_normal( [ nInputUnits, nUnitsHiddenLayer1 ] )

创建一个 Tensor 形状为 [ nInputUnits, nUnitsHiddenLayer1 ],用 1.0 作为截断正态分布的标准差初始化。 ( 1.0 是标准的 stddev 值)

t1 = tf.div( t, math.sqrt( nInputUnits ) )

divide t 中的所有值 math.sqrt( nInputUnits )

你的两行代码做的事情完全一样。在第一行和第二行,所有值都除以 math.sqrt( nInputUnits ).

至于你的说法:

I now know that this is wrong and that the 2nd line causes the weights to be recomputed at each learning step.

编辑我的错误

确实你是对的,它们在每次执行时被math.sqrt( nInputUnits )除,但没有重新初始化!重点是你把 tf.variable()

放在哪里

此处两行只初始化一次:

weightsLayer1 = tf.truncated_normal( [ nInputUnits, nUnitsHiddenLayer1 ] )
weightsLayer1 = tf.Variable( tf.div( weightsLayer1, math.sqrt( nInputUnits ) ) )

这里第二行是在每一步执行的:

weightsLayer1 = tf.Variable( tf.truncated_normal( [ nInputUnits, nUnitsHiddenLayer1 ] )
weightsLayer1 = tf.div( weightsLayer1, math.sqrt( nInputUnits ) )

为什么第二个产生更好的结果?对我来说这看起来像是某种规范化,但更有知识的人应该验证这一点。

Ps。 你可以像这样写它更具可读性:

weightsLayer1 = tf.Variable( tf.truncated_normal( [ nInputUnits, nUnitsHiddenLayer1 ] , stddev = 1. / math.sqrt( nInputUnits ) ) 

你是对的,weightsLayer1 = tf.div(weightsLayer1, math.sqrt(nInputUnits))在每一步都被执行。但这并不意味着权重变量中的值在每一步中按比例缩小 sqrt(nInputUnits)。此行不是影响存储在变量中的值的就地操作。它计算一个新的张量,将变量中的值除以 sqrt(nInputUnits),然后我假设该张量进入计算图的其余部分。这不会干扰优化器。您仍在定义一个有效的计算图,只是对权重进行了某种程度的任意缩放。优化器仍然可以计算关于这个变量的梯度(它将通过你的除法操作反向传播)并创建相应的更新操作。

就您定义的模型而言,这两个版本是完全等同的。对于原始模型中 weightsLayer1 的任何一组值(您不进行除法),您只需按 sqrt(nInputUnits) 放大它们,您将获得与第二个模型相同的结果。这两个代表完全相同的模型class,如果你愿意的话。

为什么一个比另一个效果好?你的猜测和我的一样好。如果您对所有变量都进行了相同的除法,则您实际上已经将学习率除以 sqrt(nInputUnits)。这种较小的学习率可能对手头的问题有益。

编辑:我认为您为变量和新创建的张量赋予相同的名称这一事实会造成混淆。当你这样做时

A = tf.Variable(1.0)
A = tf.mul(A, 2.0)
# Do something with A

然后第二行创建一个新的张量(如上所述),然后将名称(它只是一个名称)A 重新绑定到该新张量。对于正在定义的图,命名是绝对无关紧要的。下面的代码定义了同一个图:

A = tf.Variable(1.0)
B = tf.mul(A, 2.0)
# Do something with B

如果您执行以下代码,也许这会变得清楚:

A = tf.Variable(1.0)
print A
B = A
A = tf.mul(A, 2.0)
print A
print B

输出为

<tensorflow.python.ops.variables.Variable object at 0x7ff025c02bd0>
Tensor("Mul:0", shape=(), dtype=float32)
<tensorflow.python.ops.variables.Variable object at 0x7ff025c02bd0>

你第一次print A它告诉你A是一个变量对象。在执行 A = tf.mul(A, 2.0) 并再次打印 A 之后,您可以看到名称 A 现在绑定到一个 tf.Tensor 对象。但是,变量仍然存在,查看名称B.

后面的对象可以看出