如何在 MNIST 数据上使用 TensorFlow 和 python 创建 2 层神经网络

How to create 2-layers neural network using TensorFlow and python on MNIST data

我是机器学习的新手,我正在按照 tensorflow 的教程创建一些学习 MNIST 数据的简单神经网络。

我已经构建了一个单层网络(按照教程),准确度约为 0.92,这对我来说还可以。但是后来我又加了一层,准确率降到0.113,很糟糕。

下面是2层之间的关系:

import tensorflow as tf
x = tf.placeholder(tf.float32, [None, 784])

#layer 1
W1 = tf.Variable(tf.zeros([784, 100]))
b1 = tf.Variable(tf.zeros([100]))
y1 = tf.nn.softmax(tf.matmul(x, W1) + b1)

#layer 2
W2 = tf.Variable(tf.zeros([100, 10]))
b2 = tf.Variable(tf.zeros([10]))
y2 = tf.nn.softmax(tf.matmul(y1, W2) + b2)

#output
y = y2
y_ = tf.placeholder(tf.float32, [None, 10])

我的结构好吗?是什么原因让它表现如此糟糕?我应该如何修改我的网络?

第二层的输入是第一层输出的softmax。你不想那样做。

您强制这些值的总和为 1。如果 tf.matmul(x, W1) + b1 的某些值大约为 0(并且肯定是),则 softmax 操作会将此值降低为 0。结果:您正在扼杀梯度,没有任何东西可以流过这些神经元。

如果您删除层之间的 softmax(但如果您想将这些值视为概率,则将其放在输出层上的 softmax)您的网络将正常工作。

Tl;博士:

import tensorflow as tf
x = tf.placeholder(tf.float32, [None, 784])

#layer 1
W1 = tf.Variable(tf.zeros([784, 100]))
b1 = tf.Variable(tf.zeros([100]))
y1 = tf.matmul(x, W1) + b1 #remove softmax

#layer 2
W2 = tf.Variable(tf.zeros([100, 10]))
b2 = tf.Variable(tf.zeros([10]))
y2 = tf.nn.softmax(tf.matmul(y1, W2) + b2)

#output
y = y2
y_ = tf.placeholder(tf.float32, [None, 10])

遇到完全相同的问题,梯度发散并得到一堆 nan 用于预测 y。实施了 nessuno 的建议,不幸的是,发散梯度仍未修复。

相反,我尝试 sigmoid 作为第 1 层的激活函数,它起作用了!但是对于 relu 如果将 W1W2 初始化为零矩阵则不起作用,精度仅为 0.1135 。为了使 relusigmoid 都起作用,最好将 W1W2 的初始化随机化。这是修改后的代码

import tensorflow as tf

x = tf.placeholder(tf.float32, [None, 784])

# layer 1
with tf.variable_scope('layer1'):
    W1 = tf.get_variable('w1',[784,200],
                         initializer=tf.random_normal_initializer())
    b1 = tf.get_variable('b1',[1,],
                         initializer=tf.constant_initializer(0.0))
    y1 = tf.nn.sigmoid(tf.matmul(x, W1) + b1)
#   y1 = tf.nn.relu(tf.matmul(x, W1) + b1) # alternative choice for activation

# layer 2
with tf.variable_scope('layer2'):
    W2 = tf.get_variable('w2',[200,10],
                     initializer= tf.random_normal_nitializer())
    b2 = tf.get_variable('b2',[1,],
                         initializer=tf.constant_initializer(0.0))
    y2 = tf.nn.softmax(tf.matmul(y1, W2) + b2)

# output
y = y2
y_ = tf.placeholder(tf.float32, [None, 10])

我发现 this link 很有帮助,请参阅问题 2 (c) 部分,它给出了基本 2 层神经网络的反向传播导数。在我看来,当用户没有指定任何激活函数时,只在第 1 层应用线性流,最终会反向传播一个看起来像 (sth)*W2^T*W1^T 的梯度,并且当我们同时初始化 W1W2 为零,它们的乘积可能非常小接近于零,这导致梯度消失。

更新

这是来自 Quora 的回答 Ofir posted 关于神经网络中好的初始权重。

The most common initializations are random initialization and Xavier initialization. Random initialization just samples each weight from a standard distribution (often a normal distribution) with low deviation. The low deviation allows you to bias the network towards the 'simple' 0 solution, without the bad repercussions of actually initializing the weights to 0.

我尝试 运行 上面的代码片段。低于 90% 的结果被丢弃,我从来没有真正确定我做了上面的评论。这是我的完整代码。

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
x = tf.placeholder(tf.float32, [None, 784])

#layer 1
W1 = tf.get_variable('w1', [784, 100], initializer=tf.random_normal_initializer())
b1 = tf.get_variable('b1', [1,], initializer=tf.random_normal_initializer())
y1 = tf.nn.sigmoid(tf.matmul(x, W1) + b1) 

#layer 2
W2 = tf.get_variable('w2',[100,10], initializer= 
tf.random_normal_initializer())
b2 = tf.get_variable('b2',[1,], initializer=tf.random_normal_initializer())
y2 = tf.nn.softmax(tf.matmul(y1, W2) + b2)

#output
y = y2
y_ = tf.placeholder(tf.float32, [None, 10])

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), 
reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(0.2).minimize(cross_entropy)
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()

for _ in range(10000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: 
mnist.test.labels}))

通过改变 10000 -> 200000 我达到了 95.5%。