Почему пример TensorFlow не работает при увеличении размера партии?
Я смотрел пример MNIST для новичков и обнаружил, что в этой части:
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
изменение размера партии от 100 до отметки 204 приводит к тому, что модель не сходится. Он работает до 204, но в 205 и любом более высоком количестве, которое я пробовал, точность будет заканчиваться < 10%. Это ошибка, что-то о алгоритме, что-то еще?
Выполняется их двоичная установка для OS X, похоже, версия 0.5.0.
Ответы
Ответ 1
Вы используете самую основную линейную модель в примере для начинающих?
Вот трюк для его отладки - наблюдайте за кросс-энтропией по мере увеличения размера партии (первая строка из примера, вторая, которую я только что добавил):
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
cross_entropy = tf.Print(cross_entropy, [cross_entropy], "CrossE")
При размере партии 204, вы увидите:
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[92.37558]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[90.107414]
Но в 205 вы увидите последовательность, подобную этой, с самого начала:
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[472.02966]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[475.11697]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1418.6655]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1546.3833]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1684.2932]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1420.02]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1796.0872]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[nan]
Ack - NaN. В основном, большой размер партии создает такой огромный градиент, что ваша модель выкручивается из-под контроля - обновления, которые она применяет, слишком велики и превышают направление, которое должно пройти с огромным отрывом.
На практике есть несколько способов исправить это. Вы можете снизить скорость обучения с 0,01 до, скажем, 0,005, что приводит к конечной точности 0,92.
train_step = tf.train.GradientDescentOptimizer(0.005).minimize(cross_entropy)
Или вы можете использовать более сложный алгоритм оптимизации (Adam, Momentum и т.д.), который пытается сделать больше, чтобы определить направление градиента. Или вы можете использовать более сложную модель с более свободными параметрами, по которым можно разделить этот большой градиент.
Ответ 2
@dga дал отличный ответ, но я хотел немного расширить.
Когда я написал учебник для начинающих, я внедрил такую функцию стоимости:
cross_entropy = -tf.reduce_sum (y_ * tf.log(y))
Я написал это так, потому что это больше похоже на математическое определение кросс-энтропии. Но лучше было бы сделать что-то вроде этого:
cross_entropy = -tf.reduce_mean (y_ * tf.log(y))
Почему лучше использовать среднее значение вместо суммы? Ну, если мы суммируем, то удвоение размера партии удваивает стоимость, а также удваивает величину градиента. Если мы не будем корректировать нашу скорость обучения (или использовать алгоритм, который его корректирует, например, @dga), наше обучение взорвется! Но если мы используем среднее значение, то наша скорость обучения становится не зависимой от размера партии, что приятно.
Я бы посоветовал вам проверить Адама (tf.train.AdamOptimizer()
). Это часто более терпимо к игре с вещами, чем SGD.
Ответ 3
Нан возникает, когда происходит 0 * log (0):
заменить:
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
с:
cross_entropy = -tf.reduce_sum(y_*tf.log(y + 1e-10))
Ответ 4
@dga хорошо объяснил вам причину такого поведения (cross_entropy становится слишком огромным), и, следовательно, алгоритм не сможет сходиться. Есть несколько способов исправить это. Он уже предложил снизить скорость обучения.
Градиентный спуск является самым основным алгоритмом. Почти все остальные optimizers будут работать должным образом:
train_step = tf.train.AdagradOptimizer(0.01).minimize(cross_entropy)
train_step = tf.train.AdamOptimizer().minimize(cross_entropy)
train_step = tf.train.FtrlOptimizer(0.01).minimize(cross_entropy)
train_step = tf.train.RMSPropOptimizer(0.01, 0.1).minimize(cross_entropy)
Другой подход заключается в использовании tf.nn.softmax_cross_entropy_with_logits, который обрабатывает числовые неустойчивости.