はじめに
畳み込みニューラルネットワーク(CNN)は、画像認識の分野で非常に高い精度を発揮する人気のあるモデルです。しかし、精度が高い一方で「どのように判断しているのか」が
分かりにくく、いわゆるブラックボックスとされがちです。
そこで本記事では、CNNの中間層(Conv層)から出力される「特徴マップ(Feature Map)」を可視化することで、モデルがどのような特徴を抽出しているのかを
視覚的に理解してみます。
使用環境
今回の可視化実験には以下のツール・ライブラリを使用します。
- TensorFlow / Keras: モデルの構築と訓練に使用
- MNIST: 手書き数字の画像データセット
- Matplotlib: 特徴マップの描画に使用
データ準備とモデル構築
まずは、MNISTデータセットの読み込みと前処理を行い、シンプルなCNNモデルを構築します。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
# データ読み込みと前処理
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)
# CNNモデル構築
inputs = keras.Input(shape=(28, 28, 1))
x = layers.Conv2D(16, 3, activation='relu', name='conv1')(inputs)
x = layers.MaxPooling2D()(x)
x = layers.Conv2D(32, 3, activation='relu', name='conv2')(x)
x = layers.MaxPooling2D()(x)
x = layers.Flatten()(x)
outputs = layers.Dense(10, activation='softmax')(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=3, validation_split=0.1)
このモデルには2つの畳み込み層(conv1, conv2)があり、それぞれが画像から異なるレベルの特徴を抽出する役割を果たします。
中間層の出力を取得する
モデルが学習済みの状態になったら、テスト画像1枚に対して中間層(Conv層)の出力を取得してみましょう。これにより、「各層がどのような特徴を捉えているのか」を 視覚的に確認できます。
# 可視化対象の画像(例:3)
idx = 0
img = x_test[idx:idx+1]
# 中間層出力モデルを作成
layer_outputs = [layer.output for layer in model.layers if 'conv' in layer.name]
activation_model = keras.Model(inputs=model.input, outputs=layer_outputs)
# 特徴マップを取得
activations = activation_model.predict(img)
このコードでは、元のモデルから畳み込み層だけを取り出し、それぞれの出力を取得できるように設定しています。
特徴マップの描画
続いて、取得した中間層の特徴マップを可視化します。畳み込み層ごとに複数のフィルターがあり、それぞれ異なる特徴を抽出しています。
import matplotlib.pyplot as plt
# 特徴マップを表示する関数
def display_feature_maps(activations, layer_names, cols=8):
for layer_name, feature_map in zip(layer_names, activations):
num_filters = feature_map.shape[-1]
rows = (num_filters + cols - 1) // cols # 必要な行数を自動計算
plt.figure(figsize=(cols * 1.5, rows * 1.5)) # サイズ調整
for i in range(num_filters):
plt.subplot(rows, cols, i + 1)
plt.imshow(feature_map[0, :, :, i], cmap='viridis')
plt.axis('off')
plt.suptitle(f"Layer: {layer_name}", fontsize=14)
plt.tight_layout()
plt.subplots_adjust(top=0.9) # タイトルのための余白
plt.show()
# レイヤー名を取得
layer_names = [layer.name for layer in model.layers if 'conv' in layer.name]
# 実行
display_feature_maps(activations, layer_names)
この関数では、各Conv層のすべてのフィルター出力を、行列状に並べて表示します。色は特徴量の強さを表しており、明るいほど強く反応しています。
特徴マップの描画結果
以下はConv1層とConv2層の特徴マップです。それぞれの層でどんな情報を抽出しているか、比較して見てみましょう。
Conv1層目の特徴マップ: エッジや輪郭など、比較的シンプルな形状に反応していることが分かります。
Conv2層目の特徴マップ: より抽象的で複雑なパターンに反応している様子が見られます。ここでは数字全体の構造に注目していることが分かります。
結果と考察
Conv1では主に「エッジ・輪郭」などの低レベルな特徴を抽出し、Conv2ではそれらを組み合わせて「数字の形状」などの高次特徴へと抽象化していることがわかります。
このように、CNNは層が深くなるにつれてより抽象的な情報を捉える構造になっており、階層的な学習が行われていることが視覚的に確認できました。
まとめ
中間層の特徴マップを可視化することで、CNNが「どのように画像を理解しているのか」を把握できます。
普段は見えにくいネットワーク内部の挙動を、視覚的に知ることができるこの手法は、モデルの解釈性の向上や研究・教育の現場でも
非常に有用です。ぜひ皆さんも、自分のモデルで試してみてください。
0 件のコメント:
コメントを投稿