学習済みモデルの中間層の出力を可視化したい
Kerasの学習済みモデルの中間層の活性を取得したかったので,その方法を紹介してみます.
使っている tf.keras, Keras のバージョンは以下. ちょっと古めな環境です.
tensorflow : 2.0.0
Keras : 2.3.1
ここでは, tf.keras でも スタンドアローンでもどっちでも大丈夫です.
必要なライブラリ
必要最低限(のつもり)のライブラリです.
import matplotlib.pyplot as plt
import cv2
import numpy as np
from tensorflow.keras import models
#jupyter なら %matplotlib inline
今回は,Keras が学習済みのパラメータを提供しているモデルを使いました.
実際はMobileNetを使ってたんですけど,ここでは説明しやすいVGG16をインポートします.
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.applications.vgg16 import preprocess_input, decode_predictions
モデルの定義とか
モデルを定義していきます.「 weights = 'imagenet' 」を指定することで,ImageNetの学習済みパラメータを適用できます.
分類とかしたいわけでなく,中間層の出力だけみたいので 「 include_top = False 」とします.
「 include_top = False 」ならば,入力画像の縦横はなんでも良いのですが,分類時と同じ (224,224,3) にしています.
model = VGG16(input_shape=(224,224,3),
weights='imagenet',
include_top = False)
layer の名前取得と,見たいlayerの名前を指定します.pooling 層前の出力を見てみます.
#print([layer.name for layer in model.layers])
# ['input_1', 'block1_conv1', 'block1_conv2', 'block1_pool',
# 'block2_conv1', 'block2_conv2', 'block2_pool',
# 'block3_conv1', 'block3_conv2', 'block3_conv3', 'block3_pool',
# 'block4_conv1', 'block4_conv2', 'block4_conv3', 'block4_pool',
# 'block5_conv1', 'block5_conv2', 'block5_conv3', 'block5_pool']
layer_names = ['block1_conv2','block2_conv2','block3_conv3','block4_conv3','block5_conv3']
特定の層の出力を得るモデルを新しく定義
models.Model で新しいモデルを定義します.複数の入力・出力はリストで指定します.
actmodel = models.Model(inputs = model.input,
outputs=[model.get_layer(name).output for name in layer_names])
ここでは,先にターゲットの layer の name を指定した後,get_layerメソッドで layer の名前で呼び出しています.ちなみに get_layer では name ではなく,layer のインデックスの整数を指定しても呼び出せます.
print(model.get_layer(index=0).name)
#'input_1'
print(model.get_layer(index=-1).name)
#'block5_pool'
print(model.get_layer('block5_pool',index=0).name)
#'input_1'
# 異なるnameとindexを指定すると,indexが優先される.
出力の shape は以下のよう. batchsize x width x height x channel
actmodel.output_shape
# [(None, 224, 224, 64),
# (None, 112, 112, 128),
# (None, 56, 56, 256),
# (None, 28, 28, 512),
# (None, 14, 14, 512)]
画像を入力.出力の活性をみる
画像はなんでも良いですが,下の画像を入力しました
ex_img = plt.imread('./test.jpg')
plt.imshow(ex_img)
plt.show()
ex_img = cv2.resize(ex_img,(224,224)) # (224,224,3) にリサイズ
ex_img = ex_img[np.newaxis,:,:,:] # バッチサイズ次元を設ける.shapeは(1,224,224,3)になる
ex_img_input = preprocess_input(ex_img) # ネットワークに入力する前の前処理
あとは,ネットワークに通して出力を得るだけ.predict メソッドを用いる.
activations = actmodel.predict(ex_img_input)
activations はリストで出力される.
activations[0]ならば 'block1_conv2' layer の出力,activations[-1] ならば 'block5_conv3' layer の出力が得られる.
それぞれの layer の出力の最初の 14 枚の特徴量を描写してみる.ただし,それぞれの特徴量は,(224,224)サイズにリサイズしている.
activation1 = activations[0][0][:,:,:14]
num_of_rows = 2
num_of_culm = 7
fig, axes = plt.subplots(num_of_rows, ncols=num_of_culm,figsize=(24.0,7.0),dpi=200)
for i in range(14):
row = i%num_of_rows
culm = i//num_of_rows
axes[row][culm].imshow(activation1[:,:,i],cmap='jet')
axes[row][culm].grid(False)
plt.subplots_adjust(wspace=0.2, hspace=0.1)
plt.savefig('sample1.jpg')
plt.show
入力側では,入力したイメージが分かるような特徴量が得られている.
出力側に行くほど,より抽象化された特徴量が得られていることがわかる.
まとめ
models.Model を用いて,特定の層の出力だけを得るモデルを再定義することで,簡単に目的の特徴量のみが得られます.
おまけ〜出力を辞書型で管理したい〜
layer の名前を使って get_layer していたので,出力も layer name で呼べたら良いなと思う.
とりあえず出力を辞書型で指定してみる.
actmodel = models.Model(inputs = model.input,
outputs={name : model.get_layer(name).output for name in layer_names})
これ tf.keras (tf-ver 2.0.0) なら通るんだけど,スタンドアローンな Keras(2.3.1)だと
'tuple' object has no attribute 'layer'
ってエラーが出てしまう.
やっぱ tf.keras じゃないといけないんか.と思いつつ,tf.keras の方で predict すると...
actmodel = models.Model(inputs = model.input,
outputs={name : model.get_layer(name).output for name in layer_names})
print(type(actmodel.output))
# dict
activations = actmodel.predict(ex_img_input)
print(type(activations))
# list
結局出力はリストで帰ってくる.
新しい tf.keras だったら predict 後も辞書で返ってくるのだろうか.
それともなんかやり方があるのかな?ちょっと調査が必要.
(2020年12月21日追記) tensorflow ver 2.4.0 でやっても同じ挙動.predict 後も辞書型で返してしまうのは何か弊害があるのかな.それともやり方あるのかな.ちょっとワカンナイ.(まあ,これで特別困ることがあるわけでもないんだけどね)