TensorFlow: использование Keras с обучаемыми tfp.bijectors / tfp.distributions - PullRequest
0 голосов
/ 29 октября 2018

Я пытаюсь реорганизовать некоторые из наших методов обучения с подкреплением, используя tf.keras. Модели keras, кажется, довольно хорошо работают для сетей с прямой связью, которые я использую для функций-значений и препроцессоров, но я пытаюсь реализовать некоторые вероятностные модели (например, политики) с помощью keras. В частности, объединение tfp.distributions и tfp.bijectors с tf.keras.Model кажется мне очень не интуитивным, и все примеры, которые я видел (например, [1, 2, 3]), либо слишком упрощены, либо полагаются на «хаки», которые, на мой взгляд, подрывают многие преимущества использования моделей keras (например, возможность скрыть обработку ввода, сеансы и числовую оценку от самой модели).

Предположим, я хочу реализовать политику скрытого пространства, как описано в [4], которая использует поток RealNVP для преобразования гауссовых выборок, обусловленных состояниями, в действия. Политика должна поддерживать как минимум две операции: 1. Выборка действий Y такая, что Y = g (X | S) X ~ Normal (0, 1), где g - преобразование RealNVP, описанное в [4, 5], а S - переменные состояния (например, наблюдения за состоянием в случае RL). 2. Вычисление логарифмических вероятностей выбранных Y.

Простая реализация может выглядеть примерно так:

class LearnableConditionalRealNVP(object):
    def __init__(self, input_shape, output_shape):
        self._input_shape = input_shape
        self._output_size = np.prod(output_shape)

        conditions = tf.keras.layers.Input(shape=input_shape)

        batch_size = tf.keras.layers.Lambda(
            lambda x: tf.shape(x)[0])(conditions)

        def samples_and_log_probs_fn(inputs):
            conditions, batch_size = inputs

            base_distribution = tfp.distributions.MultivariateNormalDiag(
                loc=tf.zeros(output_shape),
                scale_diag=tf.ones(output_shape))

            real_nvp_bijector = tfp.bijectors.RealNVP(
                num_masked=self._output_size // 2,
                shift_and_log_scale_fn=conditioned_real_nvp_template(
                    hidden_layers=(128, 128),
                    activation=tf.nn.relu),
                name='real_nvp')

            distribution = (
                tfp.distributions.ConditionalTransformedDistribution(
                    distribution=base_distribution,
                    bijector=real_nvp_bijector))

            samples = distribution.sample(batch_size)
            log_probs = distribution.log_prob(samples)

            return [samples, tf.reshape(log_probs, (-1, 1))]

        samples, log_probs = tf.keras.layers.Lambda(
            samples_and_log_probs_fn)([conditions, batch_size])

        self.samples_and_log_probs_model = tf.keras.Model(
            conditions, [samples, log_probs])

    def samples_and_log_probs(self, conditions):
        return self.samples_and_log_probs_model(conditions)

    def samples_and_log_probs_np(self, conditions):
        return self.samples_and_log_probs_model.predict(conditions)

где conditioned_real_nvp_template создает сеть с прямой связью, которая объединяет скрытые выборки и значения условий вдоль последней оси и использует их в качестве входных данных. Полный пример можно найти здесь: https://gist.github.com/hartikainen/17ac2ec102032e986cb4d31e225f592a

Этот способ обработки дистрибутива принес бы мне два основных преимущества. Во-первых, мне не нужно обрабатывать повторное использование параметров вручную. Я могу вызывать samples_and_log_probs в своем коде несколько раз, и он автоматически использует параметры модели. Во-вторых, мне не нужно ничего знать о сессиях, если я хочу получить числовые результаты. Заключение промежуточных слоев в их собственные модели позволяет мне вызывать методы прогнозирования, которые обрабатывают сеансы.

Проблемы возникают при расширении примера еще немного. Предположим, что я хотел изменить LearnableConditionalRealNVP так, чтобы я мог предоставить скрытые выборки x в качестве входных данных для него, и вместо вызова distribution.sample() в samples_and_log_probs_fn он вернул бы distribution.forward(x). Или, может быть, я хочу получить образцы и log_probs отдельно от модели. Это потребовало бы, чтобы я разделил samples_and_log_probs_fn на две отдельные лямбда-функции, но сделать это не тривиально, если я хочу поделиться параметрами бижектора RealNVP (потому что я не могу передать биектор как вход / выход в / из слой керас).

Я пытался решить эти проблемы путем подкласса LearnableConditionalRealNVP из tf.keras.Model, но все мои попытки приводили к грязным реализациям, в основном из-за изменения входов и выходов. В частности, я не смог создать call -метод для модели так, чтобы модель сохранила возможность использования с predict, и мне пришлось бы делать некоторые трюки с __call__ -методом. Ничто из этого не кажется ужасным, но они все же увеличивают накладные расходы на использование моделей keras настолько, что мне легче реализовать эти типы вещей в простом тензорном потоке и обрабатывать сеансы, пустые выводы и т. Д. Вручную.

Мои вопросы:

  1. Предполагается, что биекторы / распределения тензорного потока совместимы с кератами (моделями)? Если да, кто-нибудь знает, есть ли какие-нибудь нетривиальные примеры, на которые я мог бы взглянуть? Если нет, планируется ли сделать их совместимыми?
  2. Как мне использовать модели keras в тех случаях, когда то, что я считаю одной моделью, имеет несколько разных выходов? Например, в моем примере выше, обучаемый дистрибутив RealNVP интуитивно чувствует, что он должен быть единой моделью, но в то же время он имеет несколько, возможно, независимых входов / выходов, что затрудняет встраивание в каркас модели keras. Я готов признать, что моя интуиция здесь неправильна, и в этом случае было бы неплохо услышать, что является лучшей практикой для построения таких моделей.
  3. Существует ли способ передачи нетензорных данных в качестве входных / выходных данных в / из модели keras, как это сделано в [1], при сохранении каким-либо образом связанных моделей. Если вместо tfe.Variable s вы используете входные данные в этом примере, он ломается, потому что график не связан.

Редактировать: после публикации этого и выполнения дополнительного тестирования с вышеупомянутой реализацией, я заметил, что эта модель, в конце концов, не обучаема, так как переменные для биектора RealNVP создаются в слое keras lambda. Это наводит меня на мысль о том, что функциональный способ построения этих моделей вообще нельзя использовать для моделей такого типа.

[1] https://github.com/tensorflow/probability/blob/5f5510201865350b6cce2a0f18fbe0cdf4f15eee/tensorflow_probability/examples/disentangled_vae.py#L186

[2] https://blog.keras.io/building-autoencoders-in-keras.html

[3] http://louistiao.me/posts/implementing-variational-autoencoders-in-keras-beyond-the-quickstart-tutorial/

[4] https://arxiv.org/pdf/1804.02808.pdf

[5] https://arxiv.org/abs/1605.08803

...