Репараметризация по тензорной вероятности: tf.GradientTape () не вычисляет градиент относительно среднего значения распределения - PullRequest
2 голосов
/ 08 июля 2019

В tensorflow версии 2.0.0-beta1 я пытаюсь реализовать слой keras с весами, выбранными из нормального случайного распределения.Я хотел бы иметь среднее значение распределения в качестве обучаемого параметра.

Благодаря «уловке репараметризации», уже реализованной в tensorflow-probability, вычисление градиента относительно среднего значения распределения должно быть возможнымв принципе, если я не ошибаюсь.

Однако, когда я пытаюсь вычислить градиент выходной мощности сети относительно переменной среднего значения, используя tf.GradientTape(), возвращаемый градиент будет None.

Я создал два минимальных примера: один слой с детерминированными весами и один слой со случайными весами.Градиенты градиентов детерминированного слоя рассчитываются, как и ожидалось, но градиенты равны None в случае случайного слоя.Нет сообщения об ошибке, в котором сообщается, почему градиент равен None, и я застрял.

Минимальный пример кода:

A: Вотминимальный пример для детерминированной сети:

import tensorflow as tf; print(tf.__version__)

from tensorflow.keras import backend as K
from tensorflow.keras.layers import Layer,Input
from tensorflow.keras.models import Model
from tensorflow.keras.initializers import RandomNormal
import tensorflow_probability as tfp

import numpy as np

# example data
x_data = np.random.rand(99,3).astype(np.float32)

# # A: DETERMINISTIC MODEL

# 1 Define Layer

class deterministic_test_layer(Layer):

    def __init__(self, output_dim, **kwargs):
        self.output_dim = output_dim
        super(deterministic_test_layer, self).__init__(**kwargs)

    def build(self, input_shape):
        self.kernel = self.add_weight(name='kernel', 
                                      shape=(input_shape[1], self.output_dim),
                                      initializer='uniform',
                                      trainable=True)
        super(deterministic_test_layer, self).build(input_shape)

    def call(self, x):
        return K.dot(x, self.kernel)

    def compute_output_shape(self, input_shape):
        return (input_shape[0], self.output_dim)

# 2 Create model and calculate gradient

x = Input(shape=(3,))
fx = deterministic_test_layer(1)(x)
deterministic_test_model = Model(name='test_deterministic',inputs=[x], outputs=[fx])

print('\n\n\nCalculating gradients for deterministic model: ')

for x_now in np.split(x_data,3):
#     print(x_now.shape)
    with tf.GradientTape() as tape:
        fx_now = deterministic_test_model(x_now)
        grads = tape.gradient(
            fx_now,
            deterministic_test_model.trainable_variables,
        )
        print('\n',grads,'\n')

print(deterministic_test_model.summary())

B: следующий пример очень похож, но вместо детерминированных весов я попытался использовать случайные выборочные веса (случайные выборки в call() раз!) для тестаслой:

# # B: RANDOM MODEL

# 1 Define Layer

class random_test_layer(Layer):

    def __init__(self, output_dim, **kwargs):
        self.output_dim = output_dim
        super(random_test_layer, self).__init__(**kwargs)

    def build(self, input_shape):
        self.mean_W = self.add_weight('mean_W',
                                      initializer=RandomNormal(mean=0.5,stddev=0.1),
                                      trainable=True)

        self.kernel_dist = tfp.distributions.MultivariateNormalDiag(loc=self.mean_W,scale_diag=(1.,))
        super(random_test_layer, self).build(input_shape)

    def call(self, x):
        sampled_kernel = self.kernel_dist.sample(sample_shape=x.shape[1])
        return K.dot(x, sampled_kernel)

    def compute_output_shape(self, input_shape):
        return (input_shape[0], self.output_dim)

# 2 Create model and calculate gradient

x = Input(shape=(3,))
fx = random_test_layer(1)(x)
random_test_model = Model(name='test_random',inputs=[x], outputs=[fx])

print('\n\n\nCalculating gradients for random model: ')

for x_now in np.split(x_data,3):
#     print(x_now.shape)
    with tf.GradientTape() as tape:
        fx_now = random_test_model(x_now)
        grads = tape.gradient(
            fx_now,
            random_test_model.trainable_variables,
        )
        print('\n',grads,'\n')

print(random_test_model.summary())

Ожидаемый / фактический результат:

A: Детерминированная сеть работает, как и ожидалось, и градиенты рассчитываются.Выходные данные:

2.0.0-beta1



Calculating gradients for deterministic model: 

 [<tf.Tensor: id=26, shape=(3, 1), dtype=float32, numpy=
array([[17.79845  ],
       [15.764006 ],
       [14.4183035]], dtype=float32)>] 


 [<tf.Tensor: id=34, shape=(3, 1), dtype=float32, numpy=
array([[16.22232 ],
       [17.09122 ],
       [16.195663]], dtype=float32)>] 


 [<tf.Tensor: id=42, shape=(3, 1), dtype=float32, numpy=
array([[16.382954],
       [16.074356],
       [17.718027]], dtype=float32)>] 

Model: "test_deterministic"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 3)]               0         
_________________________________________________________________
deterministic_test_layer (de (None, 1)                 3         
=================================================================
Total params: 3
Trainable params: 3
Non-trainable params: 0
_________________________________________________________________
None

B: Однако в случае аналогичной случайной сети градиенты рассчитываются не так, как ожидалось (с использованием метода репараметизации).Вместо этого они None.Полный вывод

Calculating gradients for random model: 

 [None] 


 [None] 


 [None] 

Model: "test_random"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 3)]               0         
_________________________________________________________________
random_test_layer (random_te (None, 1)                 1         
=================================================================
Total params: 1
Trainable params: 1
Non-trainable params: 0
_________________________________________________________________
None

Кто-нибудь может указать мне на проблему здесь?

1 Ответ

1 голос
/ 09 июля 2019

Кажется, что tfp.distributions.MultivariateNormalDiag нельзя дифференцировать по отношению к его входным параметрам (например, loc). В данном конкретном случае следующее будет эквивалентно:

class random_test_layer(Layer):
    ...

    def build(self, input_shape):
        ...
        self.kernel_dist = tfp.distributions.MultivariateNormalDiag(loc=0, scale_diag=(1.,))
        super(random_test_layer, self).build(input_shape)

    def call(self, x):
        sampled_kernel = self.kernel_dist.sample(sample_shape=x.shape[1]) + self.mean_W
        return K.dot(x, sampled_kernel)

В этом случае, однако, потери дифференцируются по отношению к self.mean_W.

Будьте осторожны: Хотя этот подход может работать для ваших целей, обратите внимание, что вызов функции плотности self.kernel_dist.prob даст другие результаты, поскольку мы вывели loc снаружи.

...