web-dev-qa-db-ja.com

Tensorflow 2.0の転移学習用のガイド付きバックプロップ付きGradcam

TF 2.0の転移学習で勾配視覚化を使用するとエラーが発生します。勾配の視覚化は、転移学習を使用しないモデルで機能します。

コードを実行すると、エラーが発生します。

    assert str(id(x)) in tensor_dict, 'Could not compute output ' + str(x)
AssertionError: Could not compute output Tensor("block5_conv3/Identity:0", shape=(None, 14, 14, 512), dtype=float32)

以下のコードを実行するとエラーになります。名前付け規則、またはベースモデルvgg16からの入力と出力を、追加するレイヤーに接続することに問題があると思います。本当にあなたの助けに感謝します!

"""
Broken example when grad_model is created. 
"""
!pip uninstall tensorflow
!pip install tensorflow==2.0.0
import cv2
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
import matplotlib.pyplot as plt

IMAGE_PATH = '/content/cat.3.jpg'
LAYER_NAME = 'block5_conv3'
model_layer = 'vgg16'
CAT_CLASS_INDEX = 281

imsize = (224,224,3)

img = tf.keras.preprocessing.image.load_img(IMAGE_PATH, target_size=(224, 224))
plt.figure()
plt.imshow(img)
img = tf.io.read_file(IMAGE_PATH)
img = tf.image.decode_jpeg(img)
img = tf.cast(img, dtype=tf.float32)
# img = tf.keras.preprocessing.image.img_to_array(img)
img = tf.image.resize(img, (224,224))
img = tf.reshape(img, (1, 224,224,3))

input = layers.Input(shape=(imsize[0], imsize[1], imsize[2]))
base_model = tf.keras.applications.VGG16(include_top=False, weights='imagenet',
                                          input_shape=(imsize[0], imsize[1], imsize[2]))
# base_model.trainable = False
flat = layers.Flatten()
dropped = layers.Dropout(0.5)
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()

fc1 = layers.Dense(16, activation='relu', name='dense_1')
fc2 = layers.Dense(16, activation='relu', name='dense_2')
fc3 = layers.Dense(128, activation='relu', name='dense_3')
prediction = layers.Dense(2, activation='softmax', name='output')
for layr in base_model.layers:
    if ('block5' in layr.name):

        layr.trainable = True
    else:
        layr.trainable = False

x = base_model(input)
x = global_average_layer(x)
x = fc1(x)
x = fc2(x)
x = prediction(x)

model = tf.keras.models.Model(inputs = input, outputs = x)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
                  loss='binary_crossentropy',
                  metrics=['accuracy'])

コードのこの部分がエラーの原因です。入力と出力にラベルを付けるための正しい方法がわからない。

# Create a graph that outputs target convolution and output
grad_model = tf.keras.models.Model(inputs = [model.input, model.get_layer(model_layer).input], 
                                   outputs=[model.get_layer(model_layer).get_layer(LAYER_NAME).output,
                                            model.output])

print(model.get_layer(model_layer).get_layer(LAYER_NAME).output)
# Get the score for target class

# Get the score for target class
with tf.GradientTape() as tape:
    conv_outputs, predictions = grad_model(img)
    loss = predictions[:, 1]

以下のセクションは、gradcamのヒートマップをプロットするためのものです。

print('Prediction shape:', predictions.get_shape())
# Extract filters and gradients
output = conv_outputs[0]
grads = tape.gradient(loss, conv_outputs)[0]

# Apply guided backpropagation
gate_f = tf.cast(output > 0, 'float32')
gate_r = tf.cast(grads > 0, 'float32')
guided_grads = gate_f * gate_r * grads

# Average gradients spatially
weights = tf.reduce_mean(guided_grads, axis=(0, 1))

# Build a ponderated map of filters according to gradients importance
cam = np.ones(output.shape[0:2], dtype=np.float32)

for index, w in enumerate(weights):
    cam += w * output[:, :, index]

# Heatmap visualization
cam = cv2.resize(cam.numpy(), (224, 224))
cam = np.maximum(cam, 0)
heatmap = (cam - cam.min()) / (cam.max() - cam.min())

cam = cv2.applyColorMap(np.uint8(255 * heatmap), cv2.COLORMAP_JET)

output_image = cv2.addWeighted(cv2.cvtColor(img.astype('uint8'), cv2.COLOR_RGB2BGR), 0.5, cam, 1, 0)

plt.figure()
plt.imshow(output_image)
plt.show()

https://github.com/tensorflow/tensorflow/issues/3768 のgithubのtensorflowチームにもこれを尋ねました。

3
aveevu

私はそれを考え出した。レイヤーのような新しいモデルにベースモデルを挿入するのではなく、独自のレイヤーでvgg16ベースモデルを拡張するモデルを設定すると、機能します。最初にモデルを設定し、必ずinput_tensorを宣言してください。

_inp = layers.Input(shape=(imsize[0], imsize[1], imsize[2]))
base_model = tf.keras.applications.VGG16(include_top=False, weights='imagenet', input_tensor=inp,
                                          input_shape=(imsize[0], imsize[1], imsize[2]))
_

このように、どの入力を入力するかを示すためにx=base_model(inp)のような行を含める必要はありません。これはtf.keras.applications.VGG16(...)にすでに含まれています。

このvgg16ベースモデルを別のモデル内に配置する代わりに、ベースモデル自体にレイヤーを追加することで、gradcamを実行する方が簡単です。 VGG16の最後のレイヤー(上部を削除したもの)の出力を取得します。これはプーリングレイヤーです。

_block5_pool = base_model.get_layer('block5_pool')
x = global_average_layer(block5_pool.output)
x = fc1(x)
x = prediction(x)

model = tf.keras.models.Model(inputs = inp, outputs = x)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
                  loss='binary_crossentropy',
                  metrics=['accuracy'])
_

ここで、可視化のためにレイヤーを取得します、_LAYER_NAME='block5_conv3'_。

_# Create a graph that outputs target convolution and output
grad_model = tf.keras.models.Model(inputs = [model.input], 
                                   outputs=[model.output, model.get_layer(LAYER_NAME).output])

print(model.get_layer(LAYER_NAME).output)
# Get the score for target class

# Get the score for target class
with tf.GradientTape() as tape:
    predictions, conv_outputs = grad_model(img)
    loss = predictions[:, 1]
print('Prediction shape:', predictions.get_shape())
# Extract filters and gradients
output = conv_outputs[0]
grads = tape.gradient(loss, conv_outputs)[0]
_
1
aveevu

私たちと私はプロジェクトを開発している多くのチームメンバーが、Grad-CAMを実装するコードに同様の問題を発見しました チュートリアル で見つかりました。

そのコードは、VGG19のベースモデルと、その上に追加されたいくつかの追加レイヤーで構成されるモデルでは機能しませんでした。問題は、VGG19基本モデルがモデル内の「レイヤー」として挿入され、GradCAMコードがそれを処理する方法を認識していないことでした-「グラフの切断...」エラーが発生していました。その後、デバッグを行った後(私ではなく別のチームメンバーが実行しました)、元のコードを変更して、内部に別のモデルを含むこの種のモデルで機能するようにしました。アイデアは、GradCAMクラスの追加の引数として内部モデルを追加することです。これは他の人に役立つかもしれないので、以下の変更されたコードを含めています(GradCAMクラスの名前をMy_GradCAMに変更しました)。

class My_GradCAM:
    def __init__(self, model, classIdx, inner_model=None, layerName=None):
        self.model = model
        self.classIdx = classIdx
        self.inner_model = inner_model
        if self.inner_model == None:
            self.inner_model = model
        self.layerName = layerName 

[...]

        gradModel = tensorflow.keras.models.Model(inputs=[self.inner_model.inputs],
                  outputs=[self.inner_model.get_layer(self.layerName).output,
                  self.inner_model.output])                                   

次に、内部モデルを追加の引数として追加することで、クラスをインスタンス化できます。例:

cam = My_GradCAM(model, None, inner_model=model.get_layer("vgg19"), layerName="block5_pool")

これがお役に立てば幸いです。

編集:Mirtha Lucas の功績により、デバッグを実行し、解決策を見つけました。

1
mlerma54