Пусть активные читатели сами обоснуют это утверждение. В целом локальные минимумы из-за неопределимости глубоких нейросетей по природе своей не создают проблем. Ведь все неопределимые конфигурации ведут себя примерно одинаково независимо от того, какие входные значения в них поступают. Они дадут одну ошибку на обучающем, проверочном и тестовом наборах данных. Все они достигнут одинаковых успехов на обучающих данных и будут вести себя идентично при обобщении до неизвестных примеров.
Локальные минимумы становятся проблемой, только если они сомнительные . Тогда они соответствуют конфигурации весов в нейросети, которая вызывает ошибку больше, чем конфигурация в глобальном минимуме. Если локальные минимумы встречаются часто, мы вскоре столкнемся с серьезными проблемами при использовании градиентных методов оптимизации, поскольку учитывать можем только локальную структуру.
Насколько неприятны сомнительные локальные минимумы в нейросетях?
Много лет специалисты во всех проблемах при обучении глубоких сетей винили сомнительные локальные минимумы, даже не имея достаточных доказательств. Сейчас открытым остается вопрос, действительно ли такие минимумы с высокой частотой ошибок по сравнению с глобальными часто встречаются в реальных глубоких сетях. Однако, судя по последним исследованиям, у большинства локальных минимумов частота ошибок и характеристики обобщения не очень отличаются от глобальных минимумов.
Можно попробовать решить эту проблему наивным путем: построить график функции потерь во время обучения глубокой нейросети. Но эта стратегия не даст достаточно информации о поверхности ошибок, ведь трудно судить о том, действительно ли она так «ухабиста» или мы никак не можем понять, куда двигаться.
Для более эффективного анализа проблемы И. Гудфеллоу и его коллеги (команда исследователей, объединившая специалистов из Google и Стэнфордского университета) опубликовали в 2014 году статью [42], в которой попытались разделить два этих фактора, способных вызвать затруднение. Вместо анализа функции ошибок во времени они исследовали, что происходит на поверхности ошибок между случайным образом инициализированным вектором параметров и успешным конечным решением, с помощью линейной интерполяции.
Итак, при случайным образом инициализированном векторе параметров θ i и решении стохастического градиентного спуска θ f мы намерены вычислить функцию ошибок в каждой точке методом линейной интерполяции θ α = α θ f + (1 − α ) θ i .
Ученые хотели понять, будут ли локальные минимумы помехой градиентному методу поиска даже в случае, если мы будем знать, куда двигаться. Оказалось, что для большого количества реальных сетей с разными типами нейронов прямой путь между случайным образом инициализированной точкой в пространстве параметров и решением стохастического градиентного спуска не осложняется сколь-нибудь проблемными локальными минимумами.
Мы можем даже показать это сами на примере сети с прямым распространением сигнала из нейронов ReLU, созданной в главе 3. Запустив контрольный файл, который мы сохранили при обучении исходной сети, мы можем пересоздать компоненты inference и loss, сохраняя список указателей на переменные в оригинальном графе для дальнейшего использования в var_list_opt (где opt — оптимальные настройки параметров):
# mnist data image of shape 28*28=784
x = tf.placeholder("float", [None, 784])
# 0–9 digits recognition => 10 classes
y = tf.placeholder("float", [None, 10])
sess = tf.Session()
with tf.variable_scope("mlp_model") as scope:
output_opt = inference(x)
cost_opt = loss(output_opt, y)
saver = tf.train.Saver()
scope.reuse_variables()
var_list_opt = [
"hidden_1/W",
"hidden_1/b",
"hidden_2/W",
"hidden_2/b",
"output/W",
"output/b"
]
var_list_opt = [tf.get_variable(v) for v in var_list_opt]
saver.restore(sess, "mlp_logs/model-checkpoint-file")
Так же мы можем повторно использовать конструкторы компонентов для создания случайным образом инициализированной сети. Вот как мы сохраняем переменные в var_list_rand для следующего шага программы:
with tf.variable_scope("mlp_init") as scope:
output_rand = inference(x)
cost_rand = loss(output_rand, y)
scope.reuse_variables()
var_list_rand = [
"hidden_1/W",
"hidden_1/b",
"hidden_2/W",
"hidden_2/b",
"output/W",
"output/b"
]
var_list_rand = [tf.get_variable(v) for v in var_list_rand]
init_op = tf.initialize_variables(var_list_rand)
sess.run(init_op)
Инициализировав две эти сети, мы можем вывести линейную интерполяцию при помощи параметров alpha и beta:
with tf.variable_scope("mlp_inter") as scope:
alpha = tf.placeholder("float", [1, 1])
beta = 1 — alpha
h1_W_inter = var_list_opt[0] * beta + var_list_rand[0] * alpha
h1_b_inter = var_list_opt[1] * beta + var_list_rand[1] * alpha
Читать дальше
Конец ознакомительного отрывка
Купить книгу