Keras 2 : examples : 畳み込みの学習結果を可視化する (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 12/27/2021 (keras 2.7.0)
* 本ページは、Keras の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
- Code examples : Computer Vision : Visualizing what convnets learn (Author: fchollet)
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- sales-info@classcat.com ; Web: www.classcat.com ; ClassCatJP
Keras 2 : examples : 畳み込みの学習結果を可視化する
Description: 畳み込みフィルタが反応する視覚パターンを表示する。
イントロダクション
このサンプルでは、画像分類モデルがどのような視覚パターンを学習するのか調べます。ImageNet データセットで訓練された、ResNet50V2 モデルを使用していきます。
私達のプロセスは単純です : (モデルの中央辺りで選択された層 : conv3_block4_out) ターゲット層の特定のフィルタの活性を最大化する入力画像を作成します。そのような画像はフィルタが反応するパターンの可視化を表しています。
セットアップ
import numpy as np
import tensorflow as tf
from tensorflow import keras
# The dimensions of our input image
img_width = 180
img_height = 180
# Our target layer: we will visualize the filters from this layer.
# See `model.summary()` for list of layer names, if you want to change this.
layer_name = "conv3_block4_out"
特徴抽出モデルの構築
# Build a ResNet50V2 model loaded with pre-trained ImageNet weights
model = keras.applications.ResNet50V2(weights="imagenet", include_top=False)
# Set up a model that returns the activation values for our target layer
layer = model.get_layer(name=layer_name)
feature_extractor = keras.Model(inputs=model.inputs, outputs=layer.output)
勾配上昇プロセスのセットアップ
最大化する「損失」は単純にターゲット層の特定のフィルタの活性の平均です。境界効果を避けるため、境界ピクセルは除外します。
def compute_loss(input_image, filter_index):
activation = feature_extractor(input_image)
# We avoid border artifacts by only involving non-border pixels in the loss.
filter_activation = activation[:, 2:-2, 2:-2, filter_index]
return tf.reduce_mean(filter_activation)
勾配上昇関数は入力画像に関する上の損失の勾配を単純に計算し、ターゲットフィルタをより強く活性化する状態に向かうようにアップデート画像を更新します。
@tf.function
def gradient_ascent_step(img, filter_index, learning_rate):
with tf.GradientTape() as tape:
tape.watch(img)
loss = compute_loss(img, filter_index)
# Compute gradients.
grads = tape.gradient(loss, img)
# Normalize gradients.
grads = tf.math.l2_normalize(grads)
img += learning_rate * grads
return loss, img
end-to-end フィルタ可視化ループのセットアップ
プロセスは以下のようなものです :
- 「総てグレー」(i.e. 視覚的にニュートラル (無彩色) ) に近いランダム画像から開始します。
- 上で定義された勾配上昇ステップ関数を繰り返し適用します。
- 結果としての入力画像を正規化し、中央で切り抜き、そして [0, 255] 範囲に制限することで表示可能な形式に変換し戻します。
def initialize_image():
# We start from a gray image with some random noise
img = tf.random.uniform((1, img_width, img_height, 3))
# ResNet50V2 expects inputs in the range [-1, +1].
# Here we scale our random inputs to [-0.125, +0.125]
return (img - 0.5) * 0.25
def visualize_filter(filter_index):
# We run gradient ascent for 20 steps
iterations = 30
learning_rate = 10.0
img = initialize_image()
for iteration in range(iterations):
loss, img = gradient_ascent_step(img, filter_index, learning_rate)
# Decode the resulting input image
img = deprocess_image(img[0].numpy())
return loss, img
def deprocess_image(img):
# Normalize array: center on 0., ensure variance is 0.15
img -= img.mean()
img /= img.std() + 1e-5
img *= 0.15
# Center crop
img = img[25:-25, 25:-25, :]
# Clip to [0, 1]
img += 0.5
img = np.clip(img, 0, 1)
# Convert to RGB array
img *= 255
img = np.clip(img, 0, 255).astype("uint8")
return img
ターゲット層のフィルタ 0 でそれを試してみましょう :
from IPython.display import Image, display
loss, img = visualize_filter(0)
keras.preprocessing.image.save_img("0.png", img)
これが、ターゲット層のフィルタ 0 のレスポンスを最大化する入力がどのように見えるかです :
display(Image("0.png"))
ターゲット層の最初の 64 フィルタの可視化
そして、モデルが学習した様々な視覚パターンの範囲に対する感覚を得るために、ターゲット層の最初の 64 フィルタの 8×8 グリッドを作成しましょう。
# Compute image inputs that maximize per-filter activations
# for the first 64 filters of our target layer
all_imgs = []
for filter_index in range(64):
print("Processing filter %d" % (filter_index,))
loss, img = visualize_filter(filter_index)
all_imgs.append(img)
# Build a black picture with enough space for
# our 8 x 8 filters of size 128 x 128, with a 5px margin in between
margin = 5
n = 8
cropped_width = img_width - 25 * 2
cropped_height = img_height - 25 * 2
width = n * cropped_width + (n - 1) * margin
height = n * cropped_height + (n - 1) * margin
stitched_filters = np.zeros((width, height, 3))
# Fill the picture with our saved filters
for i in range(n):
for j in range(n):
img = all_imgs[i * n + j]
stitched_filters[
(cropped_width + margin) * i : (cropped_width + margin) * i + cropped_width,
(cropped_height + margin) * j : (cropped_height + margin) * j
+ cropped_height,
:,
] = img
keras.preprocessing.image.save_img("stiched_filters.png", stitched_filters)
from IPython.display import Image, display
display(Image("stiched_filters.png"))
Image classification models see the world by decomposing their inputs over a “vector basis” of texture filters such as these.
See also this old blog post for analysis and interpretation.
以上