Я создаю алгоритм обучения с подкреплением в Tensorflow, и я хотел бы иметь возможность динамически отключать, а затем включать отключение за один вызов session.run()
.
Обоснование: мне нужно (1) выполнить прямой проход без исключения, чтобы вычислить цели; и (2) выполнить шаг обучения с сгенерированными целями. Если я выполню эти два шага в разных вызовах session.run()
, все будет в порядке. Но я хотел бы сделать это одним единственным вызовом session.run()
(используя tf.stop_gradients(targets)
).
Попробовав несколько безуспешных решений, я нашел решение, в котором я заменяю заполнитель learning_phase, используемый Keras, на переменную (поскольку заполнители являются тензорами и не допускают назначения) и использую настраиваемый слой чтобы установить для этой переменной значение True или False по желанию. Это решение показано в приведенном ниже коде. Получение значения m1
или m2
по отдельности (например, запуск sess.run(m1, feed_dict={ph:np.ones((1,1))})
works, как ожидалось, без ошибки. Однако получение значения m3
или одновременное получение значений m1
и m2
работает иногда, а иногда нет (и сообщение об ошибке малоинформативный).
Вы знаете, что я делаю неправильно или как лучше делать то, что я хочу?
РЕДАКТИРОВАТЬ: В коде показан игрушечный пример. На самом деле у меня одна модель, и мне нужно выполнить два прохода вперед (один с отключением, а другой с отключением) и один проход назад. И я хочу сделать все это без возврата к питону.
from tensorflow.keras.layers import Dropout, Dense, Input, Layer
from tensorflow.python.keras import backend as K
from tensorflow.keras import Model
import tensorflow as tf
import numpy as np
class DropoutSwitchLayer(Layer):
def __init__(self, stateful=True, **kwargs):
self.stateful = stateful
self.supports_masking = True
super(DropoutSwitchLayer, self).__init__(**kwargs)
def build(self, input_shape):
self.lph = tf.Variable(True, dtype=tf.bool, name="lph", trainable=False)
K._GRAPH_LEARNING_PHASES[tf.get_default_graph()] = self.lph
super(DropoutSwitchLayer, self).build(input_shape)
def call(self, inputs, mask=None):
data_input, training = inputs
op = self.lph.assign(training[0], use_locking=True)
# ugly trick here to make the layer work
data_input = data_input + tf.multiply(tf.cast(op, dtype=tf.float32), 0.0)
return data_input
def compute_output_shape(self, input_shape):
return input_shape[0]
dropout_on = np.array([True], dtype=np.bool)
dropout_off = np.array([False], dtype=np.bool)
input_ph = tf.placeholder(tf.float32, shape=(None, 1))
drop = Input(shape=(), dtype=tf.bool)
input = Input(shape=(1,))
h = DropoutSwitchLayer()([input, drop])
h = Dense(1)(h)
h = Dropout(0.5)(h)
o = Dense(1)(h)
m = Model(inputs=[input, drop], outputs=o)
m1 = m([input_ph, dropout_on])
m2 = m([input_ph, dropout_off])
m3 = m([m2, dropout_on])
sess = tf.Session()
K.set_session(sess)
sess.run(tf.global_variables_initializer())
РЕДАКТИРОВАТЬ 2: Решение Даниэля Мёллера, приведенное ниже, работает при использовании слоя Dropout
, но что, если использовать выпадение внутри слоя LSTM
?
input = Input(shape=(1,))
h = Dense(1)(input)
h = RepeatVector(2)(h)
h = LSTM(1, dropout=0.5, recurrent_dropout=0.5)(h)
o = Dense(1)(h)