【问题标题】:Loss decreases but weights don't appear to change during tensorflow gradient descent损失减少,但在张量流梯度下降期间权重似乎没有变化
【发布时间】:2018-09-17 05:41:57
【问题描述】:

我已经使用 sigmoid 传递函数建立了一个非常简单的多层感知器,它具有一个隐藏层,并使用 2 个输入模拟数据。

我尝试使用 Github 上的 Simple Feedforward Neural Network using TensorFlow 示例进行设置。我不会在这里发布整个内容,但我的成本函数是这样设置的:

# Backward propagation
loss = tensorflow.losses.mean_squared_error(labels=y, predictions=yhat)
cost = tensorflow.reduce_mean(loss, name='cost')
updates = tensorflow.train.GradientDescentOptimizer(0.01).minimize(cost)

然后我简单地遍历一堆时期,目的是通过updates 操作在每一步优化我的权重:

with tensorflow.Session() as sess:
    init = tensorflow.global_variables_initializer()
    sess.run(init)

    for epoch in range(10):

        # Train with each example
        for i in range(len(train_X)):
            feed_dict = {X: train_X[i: i + 1], y: train_y[i: i + 1]}

            res = sess.run([updates, loss], feed_dict)

            print "epoch {}, step {}. w_1: {}, loss: {}".format(epoch, i, w_1.eval(), res[1])

        train_result = sess.run(predict, feed_dict={X: train_X, y: train_y})
        train_errors = abs((train_y - train_result) / train_y)
        train_mean_error = numpy.mean(train_errors, axis=1)

        test_result = sess.run(predict, feed_dict={X: test_X, y: test_y})
        test_errors = abs((test_y - test_result) / test_y)
        test_mean_error = numpy.mean(test_errors, axis=1)

        print("Epoch = %d, train error = %.5f%%, test error = %.5f%%"
              % (epoch, 100. * train_mean_error[0], 100. * test_mean_error[0]))

    sess.close()

我希望这个程序的输出显示,在每个时期和每一步,权重都会更新,loss 的值会随着时间的推移而大幅下降。

但是,虽然我看到损失值和错误在减少,但权重仅在第一步之后发生变化,然后在程序的其余部分保持不变。

这是怎么回事?

这是前 2 个时期打印到屏幕上的内容:

epoch 0, step 0. w_1: [[0. 0.]
 [0. 0.]], loss: 492.525634766
epoch 0, step 1. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 482.724365234
epoch 0, step 2. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 454.100799561
epoch 0, step 3. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 418.499267578
epoch 0, step 4. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 387.509033203
Epoch = 0, train error = 84.78731%, test error = 88.31780%
epoch 1, step 0. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 355.381134033
epoch 1, step 1. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 327.519226074
epoch 1, step 2. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 301.841705322
epoch 1, step 3. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 278.177368164
epoch 1, step 4. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 257.852508545
Epoch = 1, train error = 69.24779%, test error = 76.38461%

除了不变之外,每行的权重具有相同的值也很有趣。损失本身不断减少。这是最后一个纪元的样子:

epoch 9, step 0. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 13.5048065186
epoch 9, step 1. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 12.4460296631
epoch 9, step 2. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 11.4702644348
epoch 9, step 3. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 10.5709943771
epoch 9, step 4. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], loss: 10.0332946777
Epoch = 9, train error = 13.49328%, test error = 33.56935%

我在这里做错了什么?我知道权重正在某处更新,因为我可以看到训练和测试错误发生变化,但为什么我看不到呢?

编辑:根据squadrick的要求,这里是w_1y_hat的代码:

# Layer's sizes
x_size = train_X.shape[1] # Number of input nodes
y_size = train_y.shape[1] # Number of outcomes

# Symbols
X = tensorflow.placeholder("float", shape=[None, x_size], name='X')
y = tensorflow.placeholder("float", shape=[None, y_size], name='y')

# Weight initializations
w_1 = tensorflow.Variable(tensorflow.zeros((x_size, x_size)))
w_2 = tensorflow.Variable(tensorflow.zeros((x_size, y_size)))

# Forward propagation
h = tensorflow.nn.sigmoid(tensorflow.matmul(X, w_1))
yhat = tensorflow.matmul(h, w_2)

EDIT2: squadrick 的建议看w_2 很有趣;当我使用以下内容将w_2 添加到打印语句时;

print "epoch {}, step {}. w_1: {}, w_2: {}, loss: {}".format(epoch, i, w_1.eval(), w_2.eval(), res[1])

我看到它确实更新了;

epoch 0, step 0. w_1: [[0. 0.]
 [0. 0.]], w_2: [[0.22192918]
 [0.22192918]], loss: 492.525634766
epoch 0, step 1. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], w_2: [[0.44163907]
 [0.44163907]], loss: 482.724365234
epoch 0, step 2. w_1: [[0.5410637 0.5410637]
 [0.5803371 0.5803371]], w_2: [[0.8678319]
 [0.8678319]], loss: 454.100799561

所以现在看来​​问题是只有 w_2 正在更新,而不是 w_1。我仍然不确定为什么会发生这种情况。

【问题讨论】:

  • 你能把你创建w1的代码和计算yhat的代码贴出来吗?
  • @squadrick 我已将这些附加到帖子的末尾。
  • 打印出w_2,看看是否会随着时间的推移而改变
  • @squadrick 你是对的 - w_2 正在更新,但 w_1 没有。你知道为什么会这样吗?我已经用新的打印语句更新了问题。

标签: python tensorflow


【解决方案1】:

您使用以下代码将所有权重初始化为 0:

# Weight initializations
w_1 = tensorflow.Variable(tensorflow.zeros((x_size, x_size)))
w_2 = tensorflow.Variable(tensorflow.zeros((x_size, y_size)))

这是有问题的,用小的随机数初始化所有权重更为常见(例如,在您的原始 github 链接中)。更好的是Xavier initalization


一般来说,将所有权重初始化为(接近)0 是有问题的,因为这会导致梯度为0 并更新幅度为0。例如,如果您的网络涉及 RELU 或 tanh 激活函数,情况尤其如此。

如需详细了解反向传播背后的数学原理,请参阅this page


如果我为您的具体情况计算出数学,那么这似乎不应该完全发生(除非我在某处犯了错误)。事实上,我们确实看到您的w_1 权重在远离0 时会更新一次。让我们试着算出 3 次前传 + 后传:

a^(l) = l 层中的激活级别,e^(l) = l 层中的错误。

第一次向前传球

  • a^(1) = X
  • a^(2) = h = sigmoid(matmul(X, w_1)) = sigmoid(matmul(X, 0)) = 0.5
  • a^(3) = yhat = matmul(h, w_2) = matmul(0.5, 0) = 0

第一次向后传球

  • e^(3) = cost = reduce_mean(loss) * 1(这里的* 1是输出层激活函数的导数)。
  • e^(2) = w_2 e^(3) * (a^(2) * (1 - a^(2))) = 0(a^(2) * (1 - a^(2))这里是sigmoid在隐藏层的导数)。
  • w_2 <-- w_2 + learning_rate * a^(2) * e^(3)(不乘以 0,权重非零变化)
  • w_1 <-- w_1 + learning_rate * a^(1) e^(2)(这里的e^(2)0,所以这一步权重没有变化)。

第二次前传

  • a^(1) = X
  • a^(2) = h = sigmoid(matmul(X, w_1)) = sigmoid(matmul(X, 0)) = 0.5
  • a^(3) = yhat = matmul(h, w_2) =/= 0(不再是 0,因为 w_2 已更新)

第二次向后传球

  • e^(3) = cost = reduce_mean(loss) * 1
  • e^(2) = w_2 e^(3) * (a^(2) * (1 - a^(2)))(不再是 0,因为 w_2 已更新)。
  • w_2 <-- w_2 + learning_rate * a^(2) * e^(3)(不乘以 0,权重非零变化)
  • w_1 <-- w_1 + learning_rate * a^(1) e^(2)(现在这里也有非零更新)。

第三次前传

  • a^(1) = X
  • a^(2) = h = sigmoid(matmul(X, w_1)) = ???
  • a^(3) = yhat = matmul(h, w_2) = ???

第三次向后传球

  • e^(3) = cost = reduce_mean(loss)
  • e^(2) = w_2 e^(3) * (a^(2) * (1 - a^(2)))
  • w_2 <-- w_2 - learning_rate * a^(2) * e^(3)
  • w_1 <-- w_1 - learning_rate * a^(1) e^(2)

现在,如果事情继续这样下去,w_1 应该继续学习。也就是说,除非出现以下情况之一:

  1. 上面的数学在某处不正确,或者
  2. w_1 更新一次后,a^(2) 变为(非常接近)全零或全一

如果您查看a plot of the sigmoid curve,您会发现a^(2)(隐藏层中的激活级别)可能确实都接近0,如果matmul(X, w_1) 的结果很小(例如, < -6),或者如果matmul(X, w_1) 的结果很高,则都接近1。由于您的初始损失确实看起来相当高(大约490),我可以想象对w_1 的第一次更新幅度太高,导致隐藏层几乎全零或全一随后的迭代。

通过尝试打印h 中的值来尝试验证此假设可能很有用。最好的解决方案实际上是随机初始化所有权重,您还需要它来解决另一个问题(见答案底部)。如果这里的假设是正确的,那么看看标准化输入和/或输出(您目前是否有绝对值非常高的输入和/或输出?)和/或降低学习可能也是一个好主意GradientDescentOptimizer 的速率。


请注意,w_2 权重的更新也存在问题。它们确实会更新,但所有权重始终具有相同的值。即使您设法获得非零梯度,从而获得有意义的更新,由于将所有这些权重初始化为完全相同的值,它们将始终获得完全相同的梯度,完全相同的更新,因此始终保持完全相同相同的。这就是为什么将所有权重初始化为 0.01 而不是 0.0 是不够的;它们都应该以不同的方式(随机)初始化。

【讨论】:

  • 谢谢丹尼斯,这是一个很好的答案,不仅因为它是正确的,而且因为你解释了这个过程。我相信其他人也会从中受益。干杯!
猜你喜欢
  • 2021-03-20
  • 2021-05-12
  • 2012-05-15
  • 1970-01-01
  • 1970-01-01
  • 2020-10-02
  • 2020-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多