Как в TensorFlow 2.0 с нетерпеливым исполнением вычислить градиенты выходных сетевых данных по определенному слою?

У меня есть сеть, созданная с помощью InceptionNet, и для входного образца bx я хочу вычислить градиенты выходных данных модели w.r.t. скрытый слой. У меня такой код:

bx = tf.reshape(x_batch[0, :, :, :], (1, 299, 299, 3))


with tf.GradientTape() as gtape:
    #gtape.watch(x)
    preds = model(bx)
    print(preds.shape, end='  ')

    class_idx = np.argmax(preds[0])
    print(class_idx, end='   ')

    class_output = model.output[:, class_idx]
    print(class_output, end='   ')

    last_conv_layer = model.get_layer('inception_v3').get_layer('mixed10')
    #gtape.watch(last_conv_layer)
    print(last_conv_layer)


grads = gtape.gradient(class_output, last_conv_layer.output)#[0]
print(grads)

Но это даст None. Я тоже пробовал gtape.watch(bx), но он все равно дает None.

Перед тем, как попробовать GradientTape, я попытался использовать tf.keras.backend.gradient, но это дало следующую ошибку:

RuntimeError: tf.gradients is not supported when eager execution is enabled. Use tf.GradientTape instead.

Моя модель выглядит следующим образом:

model.summary()

Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
inception_v3 (Model)         (None, 1000)              23851784  
_________________________________________________________________
dense_5 (Dense)              (None, 2)                 2002      
=================================================================
Total params: 23,853,786
Trainable params: 23,819,354
Non-trainable params: 34,432
_________________________________________________________________

Любое решение приветствуется. Это не обязательно должна быть GradientTape, если есть другой способ вычислить эти градиенты.


person Vahid Mirjalili    schedule 06.06.2019    source источник
comment
Возможный дубликат stackoverflow.com/questions/52340645/   -  person giser_yugang    schedule 06.06.2019
comment
Спасибо, но проблема не решается. Как вы можете видеть в приведенном выше коде, я также пробовал gtape.watch(bx), но в конце он не стал None. Я поставлю свой вопрос и тоже упомяну об этом.   -  person Vahid Mirjalili    schedule 06.06.2019


Ответы (4)


У меня была такая же проблема, как и у вас. Я не уверен, что это самый чистый способ решить проблему, но вот мое решение.

Я думаю, проблема в том, что вам нужно передать фактическое возвращаемое значение last_conv_layer.call(...) в качестве аргумента tape.watch(). Поскольку все слои вызываются последовательно в рамках вызова model(bx), вам придется каким-то образом ввести код во внутреннюю область видимости. Я сделал это с помощью следующего декоратора:

def watch_layer(layer, tape):
    """
    Make an intermediate hidden `layer` watchable by the `tape`.
    After calling this function, you can obtain the gradient with
    respect to the output of the `layer` by calling:

        grads = tape.gradient(..., layer.result)

    """
    def decorator(func):
        def wrapper(*args, **kwargs):
            # Store the result of `layer.call` internally.
            layer.result = func(*args, **kwargs)
            # From this point onwards, watch this tensor.
            tape.watch(layer.result)
            # Return the result to continue with the forward pass.
            return layer.result
        return wrapper
    layer.call = decorator(layer.call)
    return layer

В вашем примере я считаю, что для вас должно работать следующее:

bx = tf.reshape(x_batch[0, :, :, :], (1, 299, 299, 3))
last_conv_layer = model.get_layer('inception_v3').get_layer('mixed10')
with tf.GradientTape() as gtape:
    # Make the `last_conv_layer` watchable
    watch_layer(last_conv_layer, gtape)  
    preds = model(bx)
    class_idx = np.argmax(preds[0])
    class_output = model.output[:, class_idx]
# Get the gradient w.r.t. the output of `last_conv_layer`
grads = gtape.gradient(class_output, last_conv_layer.result)  
print(grads)
person Fantasty    schedule 12.06.2019
comment
Я попробовал ваше решение, однако, когда я вызываю model.predict() в блоке with tf.GradientTape() as gtape, я получаю следующую ошибку: LookupError: для операции «IteratorGetNext» не определен градиент (тип операции: IteratorGetNext). Есть идеи, что может вызвать это? - person Matthias; 09.10.2019
comment
@Matthias Эй, ты нашел решение для этого? Я получаю ту же ошибку - person Riya208; 28.11.2019

Вы можете использовать ленту для вычисления градиента выходного узла относительно набора наблюдаемых объектов. По умолчанию обучаемые переменные доступны для просмотра с ленты, и вы можете получить доступ к обучаемым переменным определенного уровня, получив его по имени и открыв свойство trainable_variables.

Например. в приведенном ниже коде я вычисляю градиенты предсказания только по отношению к переменным первого уровня FC (имя «fc1»), считая любую другую переменную константой.

import tensorflow as tf

model = tf.keras.models.Sequential(
    [
        tf.keras.layers.Dense(10, input_shape=(3,), name="fc1", activation="relu"),
        tf.keras.layers.Dense(3, input_shape=(3,), name="fc2"),
    ]
)

inputs = tf.ones((1, 299, 299, 3))

with tf.GradientTape() as tape:
    preds = model(inputs)

grads = tape.gradient(preds, model.get_layer("fc1").trainable_variables)
print(grads)
person nessuno    schedule 07.06.2019
comment
Спасибо за ваш ответ. Однако я хочу, чтобы градиенты относились к самому скрытому слою, а не к обучающим переменным на этом слое. Как бы вы изменили свой код для расчета градиентов вывода относительно слоя fc1? - person Vahid Mirjalili; 07.06.2019
comment
Я не понимаю ваших требований. Вы хотите вычислить градиент по отношению к какой части слоев fc1? - person nessuno; 07.06.2019
comment
Выход слоя fc1. В более старой версии TF я мог сделать это следующим образом: layer = model.get_layer (fc1) grads = K.gradients (class_output, layer.output) [0] - person Vahid Mirjalili; 07.06.2019

Если вам нужны градиенты прогнозов по выходным данным всех слоев, вы можете:

(На основе ответа @nessuno)

import tensorflow as tf

model = tf.keras.models.Sequential(
    [
        tf.keras.layers.Dense(10, input_shape=(3,), name="fc1", activation="relu"),
        tf.keras.layers.Dense(3, input_shape=(3,), name="fc2"),
    ]
)

# build a new model
output_layer = model.outputs
all_layers = [layer.output for layer in model.layers]
grad_model = tf.keras.model(inputs=model.inputs, outputs=all_layers)

inputs = tf.ones((1, 299, 299, 3))
with tf.GradientTape() as tape:
    output_of_all_layers = grad_model(inputs)
    preds = output_layer[-1]  # last layer is output layer
    # take gradients of last layer with respect to all layers in the model
    grads = tape.gradient(preds, output_of_all_layers)
    # note: grads[-1] should be all 1, since it it d(output)/d(output)
print(grads)
person Ali Salehi    schedule 21.02.2020

Пример вычисления градиента сети вывода относительно определенного слоя.

def example():

def grad_cam(input_model, image, category_index, layer_name):

    gradModel = Model(
        inputs=[model.inputs],
        outputs=[model.get_layer(layer_name).output,
                 model.output])

    with tf.GradientTape() as tape:

        inputs = tf.cast(image, tf.float32)
        (convOutputs, predictions) = gradModel(inputs)
        loss = predictions[:, category_index]

    grads = tape.gradient(loss, convOutputs)


    castConvOutputs = tf.cast(convOutputs > 0, "float32")
    castGrads = tf.cast(grads > 0, "float32")
    guidedGrads = castConvOutputs * castGrads * grads


    convOutputs = convOutputs[0]
    guidedGrads = guidedGrads[0]

    weights = tf.reduce_mean(guidedGrads, axis=(0, 1))
    cam = tf.reduce_sum(tf.multiply(weights, convOutputs), axis=-1)


    H, W = image.shape[1], image.shape[2]
    cam = np.maximum(cam, 0)  # ReLU so we only get positive importance
    cam = cv2.resize(cam, (W, H), cv2.INTER_NEAREST)
    cam = cam / cam.max()

    return cam



im = load_image_normalize(im_path, mean, std)

print(im.shape)
cam = grad_cam(model, im, 5, 'conv5_block16_concat') # Mass is class 5

# Loads reference CAM to compare our implementation with.
reference = np.load("reference_cam.npy")
error = np.mean((cam-reference)**2)

print(f"Error from reference: {error:.4f}, should be less than 0.05")




plt.imshow(load_image(im_path, df, preprocess=False), cmap='gray')
plt.title("Original")
plt.axis('off')

plt.show()

plt.imshow(load_image(im_path, df, preprocess=False), cmap='gray')
plt.imshow(cam, cmap='magma', alpha=0.5)
plt.title("GradCAM")
plt.axis('off')
plt.show()
person Arnab Das    schedule 09.08.2020