Использование tf.contrib.opt.ScipyOptimizerInterface с tf.keras.layers, потери не меняются
Я хочу использовать интерфейс внешнего оптимизатора в программе tenorflow, чтобы использовать оптимизаторы Ньютона, поскольку в tf.train есть только оптимизаторы градиентного спуска первого порядка. В то же время я хочу построить свою сеть с использованием tf.keras.layers, поскольку это намного проще, чем использование tf.Variables при создании больших и сложных сетей. Я покажу свою проблему на следующем простом примере 1D линейной регрессии:
import tensorflow as tf
from tensorflow.keras import backend as K
import numpy as np
#generate data
no = 100
data_x = np.linspace(0,1,no)
data_y = 2 * data_x + 2 + np.random.uniform(-0.5,0.5,no)
data_y = data_y.reshape(no,1)
data_x = data_x.reshape(no,1)
# Make model using keras layers and train
x = tf.placeholder(dtype=tf.float32, shape=[None,1])
y = tf.placeholder(dtype=tf.float32, shape=[None,1])
output = tf.keras.layers.Dense(1, activation=None)(x)
loss = tf.losses.mean_squared_error(data_y, output)
optimizer = tf.contrib.opt.ScipyOptimizerInterface(loss, method="L-BFGS-B")
sess = K.get_session()
sess.run(tf.global_variables_initializer())
tf_dict = {x : data_x, y : data_y}
optimizer.minimize(sess, feed_dict = tf_dict, fetches=[loss], loss_callback=lambda x: print("Loss:", x))
При запуске это просто не меняет потери. При использовании любого другого оптимизатора из tf.train он работает нормально. Кроме того, при использовании tf.Variables вместо tf.keras.layers он отлично работает с tf.contrib.opt.ScipyOptimizerInterface. Почему это не работает с keras.layers, хотя??
Спасибо
0 ответов
После долгих раскопок я смог найти возможное объяснение.
ScipyOptimizerInterface использует feed_dicts для имитации обновлений ваших переменных в процессе оптимизации. Он выполняет операцию присваивания только в самом конце. Напротив, оптимизаторы tf.train всегда выполняют операции назначения. Код ScipyOptimizerInterface не так уж сложен, так что вы можете легко это проверить.
Теперь проблема в том, что присвоение переменных с feed_dict работает в основном случайно. Вот ссылка, где я узнал об этом. Другими словами, присвоение переменных с помощью feed dict, что и делает ScipyOptimizerInterface, является хакерским способом обновления.
Теперь этот хак в основном работает, кроме случаев, когда это не так. tf.keras.layers.Dense использует ResourceVariables для моделирования весов модели. Это улучшенная версия простых переменных, которая имеет более чистую семантику чтения / записи. Проблема заключается в том, что в соответствии с новой семантикой обновление рассылки происходит после расчета потерь. Ссылка выше дает некоторые пояснения.
Теперь tf.layers в настоящее время является тонкой оболочкой для tf.keras.layer, поэтому я не уверен, почему это сработает. Может быть, где-то в коде есть проверка совместимости.
Решения для решения этой проблемы несколько просты.
- Либо избегайте использования компонентов, которые используют ResourceVariables. Это может быть довольно сложно.
- Патч ScipyOptimizerInterface, чтобы всегда делать назначения для переменных. Это относительно просто, поскольку весь необходимый код находится в одном файле.
Были некоторые попытки заставить интерфейс работать с нетерпением (который по умолчанию использует ResourceVariables). Проверьте эту ссылку
Я думаю, что проблема с линией
output = tf.keras.layers.Dense(1, activation=None)(x)
В этом формате вывод является не слоем, а выводом слоя, что может препятствовать тому, чтобы оболочка собирала веса и смещения слоя и передавала их оптимизатору. Попробуйте написать это в две строки, например
output = tf.keras.layers.Dense(1, activation=None)
res = output(x)
Если вы хотите сохранить исходный формат, вам, возможно, придется вручную собрать все обучаемые материалы и передать их оптимизатору с помощью опции var_list
optimizer = tf.contrib.opt.ScipyOptimizerInterface(loss, var_list = [Trainables], method="L-BFGS-B")
Надеюсь это поможет.