Градиенты тензорного потока в нетерпеливом режиме возвращают нули

Проблема: я загружаю простой VGG16 из сохраненной контрольной точки. Я хочу создать заметность изображения во время вывода. Когда я вычисляю градиенты (потери относительно входного изображения), необходимые для этого, я возвращаю все градиенты как ноль. Любые идеи относительно того, что мне здесь не хватает, очень ценятся!

Версия tf: tensorflow-2.0alpha-gpu

Модель:

import tensorflow as tf
from tensorflow.keras.applications.vgg16 import VGG16 as KerasVGG16
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Flatten, Dense

class VGG16(Model):

    def __init__(self, num_classes, use_pretrained=True):

        super(VGG16, self).__init__()
        self.num_classes = num_classes
        self.use_pretrained = use_pretrained

        if use_pretrained:
            self.base_model = KerasVGG16(weights='imagenet', include_top=False)
            for layer in self.base_model.layers:
                layer.trainable = False
        else:
            self.base_model = KerasVGG16(include_top=False)

        self.flatten1 = Flatten(name='flatten')
        self.dense1 = Dense(4096, activation='relu', name='fc1')
        self.dense2 = Dense(100, activation='relu', name='fc2')
        self.dense3 = Dense(self.num_classes, activation='softmax', name='predictions')

    def call(self, inputs):

        x = self.base_model(tf.cast(inputs, tf.float32))
        x = self.flatten1(x)
        x = self.dense1(x)
        x = self.dense2(x)
        x = self.dense3(x)
        return x

Я обучаю эту модель, сохраняю ее на контрольной точке и загружаю обратно через:

model = VGG16(num_classes=2, use_pretrained=False)
checkpoint = tf.train.Checkpoint(net=model)
        status = checkpoint.restore(tf.train.latest_checkpoint('./my_checkpoint'))
status.assert_consumed()

Я проверяю, правильно ли загружены грузы.

Получите тестовое изображение

# load my image and make sure its float
img = tf.convert_to_tensor(image, dtype=tf.float64)
support_class = tf.convert_to_tensor(support_class, dtype=tf.float64)

Получите градиенты:

with tf.GradientTape(persistent=True) as g_tape:
    g_tape.watch(img)
    #g_tape.watch(model.base_model.trainable_variables)
    #g_tape.watch(model.trainable_variables)
    loss = tf.losses.CategoricalCrossentropy()(support_class, model(img))    
    gradients_wrt_image = g_tape.gradient(loss,
                                    img, unconnected_gradients=tf.UnconnectedGradients.NONE)

Когда я проверяю свои градиенты, они все равны нулю! Есть идеи, что мне не хватает? Заранее спасибо!


person borarak    schedule 08.04.2019    source источник
comment
Как вы определяете support_class?   -  person Vlad    schedule 08.04.2019
comment
Это всего лишь один горячий тензор, например [[0.0, 1.0]]   -  person borarak    schedule 08.04.2019
comment
Как вы думаете, почему у вас нулевые градиенты? grads = g_tape.gradient(loss, img, unconnected_gradients=tf.UnconnectedGradients.NONE); print(tf.reduce_sum(grads, axis=None)) не выводит ноль. Может быть близко к нулю, но не к нулю   -  person Vlad    schedule 08.04.2019
comment
@Vlad извини в моем фине. Да, я бы также ожидал, что градиенты будут отличными от нуля (и близкими к нулю). Я провожу тщательную проверку всех градиентов, чтобы убедиться, что какой-либо из них не равен нулю.   -  person borarak    schedule 08.04.2019
comment
Я не получаю нулевых оценок за ваш код с image = [np.random.normal(size=(32, 32, 3))].   -  person Vlad    schedule 08.04.2019


Ответы (2)


Градиенты не равны нулю, хотя они очень маленькие:

def almost_equals(a, b, decimal=6):
    try:
        np.testing.assert_almost_equal(a, b, decimal=decimal)
    except AssertionError:
        return False
    return True

image = [abs(np.random.normal(size=(32, 32, 3))) for _ in range(20)]
label = [[0, 1] if i % 3 == 0 else [1, 0] for i in range(20)]
img = tf.convert_to_tensor(image, dtype=tf.float64)
support_class = tf.convert_to_tensor(label, dtype=tf.float64)
loss_fn = tf.losses.CategoricalCrossentropy()

with tf.GradientTape(persistent=True) as tape:
    tape.watch(img)
    softmaxed = model(img)
    loss = loss_fn(support_class, softmaxed)
    grads = tape.gradient(loss, img, unconnected_gradients=tf.UnconnectedGradients.NONE)
    # summing up all gradients with reduction over all dimension:
    print(tf.reduce_sum(grads, axis=None).numpy()) # 0.07137820225818814
    # comparing to zeros:
    zeros_like_grads = np.zeros_like(grads.numpy())  
    for decimal in range(10, 0, -1):
        print('decimal: {0}: {1}'.format(decimal,
                                         almost_equals(zeros_like_grads,
                                                       grads.numpy(),
                                                       decimal=decimal)))
# decimal: 10: False
# decimal: 9: False
# decimal: 8: False
# decimal: 7: False
# decimal: 6: False
# decimal: 5: False
# decimal: 4: False
# decimal: 3: True
# decimal: 2: True
# decimal: 1: True

Как видите, только начиная с decimal=3 он начинает возвращать True.

person Vlad    schedule 08.04.2019
comment
спасибо @Vlad за помощь в отладке сети. Ваш чек подтвердил тот факт, что сеть работает правильно. Я опубликовал ответ, который поможет мне вернуть мои градиенты :) - person borarak; 10.04.2019
comment
@borarak Рад слышать. - person Vlad; 10.04.2019

Итак, с сетью все в порядке. Проблема связана с поведением активации softmax, которую я использую в моем последнем Dense слое. Я не учел тот факт, что очень надежные прогнозы от softmax (например, одно из моих прогнозов [[1.0000000e + 00 1.9507678e-25]]) сделают градиенты нулевыми (теоретически очень близкими к нулю, но практически нулевыми). Полезная ветка, в которой обсуждают это и как с этим бороться: https://github.com/keras-team/keras/issues/5881

Мое решение: отключите активацию softmax, когда я хочу вычислять градиенты по входному изображению

person borarak    schedule 10.04.2019