Почему добавление еще одного слоя в пример простой нейронной сети Tensorflow ломает его?

Вот основной пример сети Tensorflow (на основе MNIST), полный код, который дает точность примерно 0,92:

import numpy as np
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])

W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x, W) + b)

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.5).minimize(cross_entropy)
sess = tf.InteractiveSession()
tf.global_variables_initializer().run() # or 
tf.initialize_all_variables().run()

for _ in range(1000):
  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}))

Вопрос. Почему добавление дополнительного слоя, как в приведенном ниже коде, делает его настолько хуже, что его точность падает примерно до 0,11?

W = tf.Variable(tf.zeros([784, 100]))
b = tf.Variable(tf.zeros([100]))
h0 = tf.nn.relu(tf.matmul(x, W) + b)

W2 = tf.Variable(tf.zeros([100, 10]))
b2 = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(h0, W2) + b2)

person Massyanya    schedule 16.06.2017    source источник
comment
Да, при запуске он получает точность 0,92. Инициализация весов как tf.random_normal дает около 0,88, а tf.random_uniform около 0,91.   -  person Massyanya    schedule 16.06.2017


Ответы (2)


В примере неправильно инициализируются веса, но без скрытого слоя оказывается, что эффективная линейная регрессия softmax, которую делает демонстрация, не зависит от этого выбора. Установка их всех равными нулю безопасна, но только для одноуровневой сети.

Однако, когда вы создаете более глубокую сеть, это катастрофический выбор. Вы должны использовать неравную инициализацию весов нейронной сети, и обычно это делается случайным образом.

Попробуй это:

W = tf.Variable(tf.random_uniform([784, 100], -0.01, 0.01))
b = tf.Variable(tf.zeros([100]))
h0 = tf.nn.relu(tf.matmul(x, W) + b)

W2 = tf.Variable(tf.random_uniform([100, 10], -0.01, 0.01))
b2 = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(h0, W2) + b2)

Причина, по которой вам нужны эти неидентичные веса, связана с тем, как работает обратное распространение — значения весов в слое определяют, как этот слой будет вычислять градиенты. Если все веса одинаковы, то и все градиенты будут одинаковыми. Это, в свою очередь, означает, что все обновления весов одинаковы - все меняется синхронно, и поведение аналогично тому, как если бы у вас был один нейрон в скрытом слое (потому что у вас есть несколько нейронов с одинаковыми параметрами). ), который может эффективно выбрать только один класс.

person Neil Slater    schedule 16.06.2017
comment
Спасибо, это работает идеально! :) Это также дает лучшую (~ 0,95) точность, чем первоначальный пример с 1 слоем. - person Massyanya; 16.06.2017

Нил хорошо объяснил вам, как решить вашу проблему, я добавлю немного объяснений, почему это происходит.

Проблема не столько в том, что градиенты все одинаковые, сколько в том, что все они равны 0. Это происходит потому, что relu(Wx + b) = 0 когда W = 0 и b = 0. Для этого даже есть название — мертвый нейрон.

Сеть вообще не прогрессирует и не важно тренируете ли вы ее на 1 шаг или на 1млн. Результаты не будут отличаться от случайного выбора, и вы видите его с вашей точностью 0,11 (если вы выберете случайным образом, вы получите 0,10).

person Salvador Dali    schedule 17.06.2017