Динамическое переключение dropout в Keras / Tensorflow

Я создаю алгоритм обучения с подкреплением в 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)

person nicolas    schedule 14.12.2018    source источник


Ответы (3)


Почему бы не сделать единую непрерывную модель?

#layers
inputs = Input(shape(1,))
dense1 = Dense(1)
dense2 = Dense(1)

#no drop pass:
h = dense1(inputs)
o = dense2(h)
#optionally:
o = Lambda(lambda x: K.stop_gradient(x))(o)

#drop pass:
h = dense1(o)
h = Dropout(.5)(h)
h = dense2(h)

modelOnlyFinalOutput = Model(inputs,h)
modelOnlyNonDrop = Model(inputs,o)
modelBothOutputs = Model(inputs, [o,h])

Выберите один для обучения:

model.fit(x_train,y_train) #where y_train = [targets1, targets2] if using both outputs
person Daniel Möller    schedule 14.12.2018
comment
Это не вариант. Как я объясняю, мне это нужно для алгоритма обучения с подкреплением. В коде показан игрушечный пример, воспроизводящий проблему. Я отредактировал свой вопрос, чтобы прояснить это. Прости - person nicolas; 14.12.2018
comment
Я не понимаю, почему это не вариант. Это именно то. Два прохода вперед в одной модели. Второй с отсечкой. Если хотите, можете добавить o = Lambda(lambda x: K.stop_gradient(x))(o), чтобы не распространять обратный проход полностью. - person Daniel Möller; 14.12.2018
comment
Прости еще раз. Это правда, что это может сработать, если использовать слой Dropout, как в моем примере с игрушкой. Однако я использую выпадение внутри LSTM (LSTM(int(lstm_size, dropout=0.5, recurrent_dropout=0.5))). В данном случае я думаю, что предложенная вами стратегия не может быть использована, верно? Или я не прав? - person nicolas; 14.12.2018
comment
Ухм, тогда все усложняется ... но вы все равно можете попытаться создать единую модель из m2 и m3. Но это потребует тестирования. Я сейчас очень тороплюсь ... - person Daniel Möller; 15.12.2018
comment
Оказывается, Keras "из коробки" поддерживает то, что я хочу делать. Использование аргумента training в вызове уровня Dropout / LSTM в сочетании с подходом @danielm к построению модели (спасибо!) Делает свое дело. Я добавил ответ - person nicolas; 16.12.2018

Оказывается, Keras "из коробки" поддерживает то, что я хочу делать. Использование аргумента training в вызове уровня Dropout / LSTM в сочетании с подходом Даниэля Мёллера к построению модели (спасибо!) Делает свое дело.

В приведенном ниже коде (просто игрушечный пример) o1 и o3 должны быть равны и отличаться от o2.

from tensorflow.keras.layers import Dropout, Dense, Input, Lambda, Layer, Add, RepeatVector, LSTM
from tensorflow.python.keras import backend as K
from tensorflow.keras import Model
import tensorflow as tf
import numpy as np

repeat = RepeatVector(2)
lstm = LSTM(1, dropout=0.5, recurrent_dropout=0.5)

#Forward pass with dropout disabled
next_state = tf.placeholder(tf.float32, shape=(None, 1), name='next_state')
h = repeat(next_state)
# Use training to disable dropout
o1 = lstm(h, training=False)
target1 = tf.stop_gradient(o1)

#Forward pass with dropout enabled
state = tf.placeholder(tf.float32, shape=(None, 1), name='state')
h = repeat(state)
o2 = lstm(h, training=True)
target2 = tf.stop_gradient(o2)

#Forward pass with dropout disabled
ph3 = tf.placeholder(tf.float32, shape=(None, 1), name='ph3')
h = repeat(ph3)
o3 = lstm(h, training=False)

loss = target1 + target2 - o3
opt = tf.train.GradientDescentOptimizer(0.1)
train = opt.minimize(loss)

sess = tf.Session()
K.set_session(sess)
sess.run(tf.global_variables_initializer())

data = np.ones((1,1))
sess.run([o1, o2, o3], feed_dict={next_state:data, state:data, ph3:data})
person nicolas    schedule 16.12.2018

Как насчет этого :

class CustomDropout(tf.keras.layers.Layer):
    def __init__(self):
        super(CustomDropout, self).__init__()
        self.dropout1= Dropout(0.5)
        self.dropout2= Dropout(0.1)

    def call(self, inputs):
       if xxx:
           return self.dropout1(inputs)
       else:
           return self.dropout2(inputs)
person DachuanZhao    schedule 24.05.2021