我已经设置了一个非常简单的多层感知器,其中一个隐藏层使用了sigmoid传递函数,模拟数据有两个输入.
我试图在Github上使用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)
Run Code Online (Sandbox Code Playgroud)
然后我简单地循环了一堆时代,意图是我的权重通过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()
Run Code Online (Sandbox Code Playgroud)
我希望这个程序的输出显示在每个时期和每个步骤都会更新权重,其loss值随着时间的推移会大致减少.
然而,当我看到损失值和错误减少时,权重仅在第一步之后改变,然后对于程序的其余部分保持固定.
这里发生了什么?
这是在前两个时期内打印到屏幕的内容:
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%
Run Code Online (Sandbox Code Playgroud)
除了不改变之外,权重对于每一行具有相同的值也是有趣的.损失本身不断减少.这是上一个时代的样子:
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%
Run Code Online (Sandbox Code Playgroud)
我在这里做错了什么?我知道权重正在某处更新,因为我可以看到训练和测试错误发生变化,但为什么我不能看到这个?
编辑:根据squadrick请求,这里的代码是:w_1和y_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)
Run Code Online (Sandbox Code Playgroud)
EDIT2: squadrick看到的建议w_2很有意思; 当我w_2用以下内容添加到print语句时;
print "epoch {}, step {}. w_1: {}, w_2: {}, loss: {}".format(epoch, i, w_1.eval(), w_2.eval(), res[1])
Run Code Online (Sandbox Code Playgroud)
我看到它确实更新了;
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
Run Code Online (Sandbox Code Playgroud)
所以现在看起来问题是只有w_2更新,而不是w_1.我仍然不确定为什么会发生这种情况.
您可以使用以下代码将所有权重初始化为 0:
# Weight initializations
w_1 = tensorflow.Variable(tensorflow.zeros((x_size, x_size)))
w_2 = tensorflow.Variable(tensorflow.zeros((x_size, y_size)))
Run Code Online (Sandbox Code Playgroud)
这是有问题的,用小随机数初始化所有权重更为常见(例如,在原始 github 链接中所做的)。更好的是Xavier 初始化。
一般来说,将所有权重初始化为(接近的值)0是有问题的,因为这可能导致 的梯度0和更新幅度0。例如,如果您的网络涉及 RELU 或 tanh 激活函数,情况尤其如此。
有关反向传播背后的数学原理的更多详细信息,请参阅本页。
如果我针对你的具体情况计算出数学,那么似乎这不应该完全发生(除非我在某个地方犯了错误)。事实上,我们确实看到您的w_1权重一旦远离 就会更新0。让我们尝试计算 3 次向前 + 向后传球:
a^(l)= 层中的激活级别l,e^(l)= 层中的错误l。
第一个前锋传球:
a^(1) = Xa^(2) = h = sigmoid(matmul(X, w_1)) = sigmoid(matmul(X, 0)) = 0.5a^(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) = Xa^(2) = h = sigmoid(matmul(X, w_1)) = sigmoid(matmul(X, 0)) = 0.5a^(3) = yhat = matmul(h, w_2) =/= 0(不再0是因为w_2更新了)第二次向后传递:
e^(3) = cost = reduce_mean(loss) * 1e^(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) = Xa^(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应该继续学习。也就是说,除非有下列情况之一:
a^(2)w_1更新一次后变为(非常接近)全零或全一如果您查看sigmoid 曲线图,您会发现a^(2)(隐藏层中的激活级别)可能确实全部接近0,如果 的结果matmul(X, w_1)很小(例如< -6),或者全部接近于1如果结果的matmul(X, w_1)高。由于您的初始损失确实看起来相当高(大约490),我可以想象第一次更新的w_1幅度太大,导致隐藏层在后续迭代中几乎全零或全一。
通过尝试打印 中的值来尝试验证这个假设可能会很有用h。最好的解决方案确实是随机初始化所有权重,您还需要它来解决另一个问题(请参见答案的底部)。如果这里的假设是正确的,那么考虑标准化输入和/或输出(您当前是否具有绝对值非常高的输入和/或输出?)和/或降低学习可能也是一个好主意的比率GradientDescentOptimizer。
w_2请注意,您的权重更新也存在问题。它们确实会更新,但所有权重始终具有相同的值。即使您设法获得非零梯度,从而获得有意义的更新,由于将所有这些权重初始化为完全相同的值,它们将始终获得完全相同的梯度、完全相同的更新,因此始终保持完全相同的值相同的。0.01这就是为什么将所有权重初始化为而不是0.0例如是不够的;它们都应该以不同的方式(随机)初始化。
| 归档时间: |
|
| 查看次数: |
326 次 |
| 最近记录: |