ディープラーニングの畳み込みニューラルネットワーク(CNN)のフィルタを可視化してみました。可視化に使用したのは、5種類の花の分類に使用したVGG16を転移学習したモデルです。VGG16は、ディープラーニングによる画像応用の代表的なモデルの一つです。

 

VGG16(転移学習)モデルのフィルタを可視化してみる。

In [1]:
import os

import keras
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential, Model
from keras.layers import Input, Dense, Dropout, Activation, Flatten
from keras import optimizers
from keras.applications.vgg16 import VGG16
Using TensorFlow backend.
In [2]:
keras.__version__
Out[2]:
'2.2.4'

訓練画像、検証画像、テスト画像のディレクトリ

In [3]:
# 分類クラス
classes = ['daisy', 'dandelion','rose','sunflower','tulip']
nb_classes = len(classes)
batch_size_for_data_generator = 20

base_dir = "."

test_dir = os.path.join(base_dir, 'test_images')

test_daisy_dir = os.path.join(test_dir, 'daisy')
test_dandelion_dir = os.path.join(test_dir, 'dandelion')
test_rose_dir = os.path.join(test_dir, 'rose')
test_sunflower_dir = os.path.join(test_dir, 'sunflower')
test_tulip_dir = os.path.join(test_dir, 'tulip')

# 画像サイズ
img_rows, img_cols = 200, 200

VGG16モデル

In [4]:
input_tensor = Input(shape=(img_rows, img_cols, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
#vgg16.summary()

VGG16モデルに全結合分類器を追加する

In [5]:
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(nb_classes, activation='softmax'))
model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))
model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 200, 200, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 200, 200, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 200, 200, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 100, 100, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 100, 100, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 100, 100, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 50, 50, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 50, 50, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 50, 50, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 50, 50, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 25, 25, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 25, 25, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 25, 25, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 25, 25, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 12, 12, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 12, 12, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 12, 12, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 12, 12, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 6, 6, 512)         0         
_________________________________________________________________
sequential_1 (Sequential)    (None, 5)                 4720133   
=================================================================
Total params: 19,434,821
Trainable params: 19,434,821
Non-trainable params: 0
_________________________________________________________________
In [6]:
model.compile(loss='categorical_crossentropy',optimizer=optimizers.RMSprop(lr=1e-5), metrics=['acc'])

学習結果を読み出す

In [7]:
hdf5_file = os.path.join(base_dir, 'flower-model.hdf5')
model.load_weights(hdf5_file)
In [8]:
import matplotlib.pyplot as plt
In [9]:
%matplotlib inline

フィルタを出力する中間層を選択する

In [10]:
import numpy as np
from keras import backend as K
In [11]:
layer_output = model.get_layer('block1_conv1').output
layer_output
Out[11]:
<tf.Tensor 'block1_conv1/Relu:0' shape=(?, 200, 200, 64) dtype=float32>

損失関数

In [12]:
filter_index = 0
loss = K.mean(layer_output[:,:,:,filter_index])

損失関数の勾配

In [13]:
grads = K.gradients(loss,model.input)[0]
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)

入力画像から損失と勾配を計算する関数

In [14]:
iterate = K.function([model.input],[loss,grads])

勾配上昇法

In [15]:
input_img_data = np.random.random((1,img_rows,img_cols,3))*20+128.
In [16]:
step = 1.
for i in range(40):
    loss_value, grads_value = iterate([input_img_data])
    input_img_data += grads_value * step

テンソルを画像に変換する関数定義

In [17]:
def depross_image(x):
    x -= x.mean()
    x /= (x.std() + 1e-5)
    x *= 0.1
    x += 0.5
    x = np.clip(x, 0, 1)
    x *= 255
    x = np.clip(x, 0, 255).astype('uint8')
    return x

フィルタを可視化する

In [18]:
print(input_img_data.shape)
(1, 200, 200, 3)
In [19]:
plt.imshow(depross_image(input_img_data[0]))
Out[19]:
<matplotlib.image.AxesImage at 0x2a23dd726d8>
最初の畳み込み層のフィルタ(0番目)の画像

最初の畳み込み層のフィルタ(0番目)

別の中間層のフィルタを表示する

In [20]:
layer_output = model.get_layer('block5_conv3').output
layer_output
Out[20]:
<tf.Tensor 'block5_conv3/Relu:0' shape=(?, 12, 12, 512) dtype=float32>
In [21]:
filter_index = 0
loss = K.mean(layer_output[:,:,:,filter_index])
In [22]:
grads = K.gradients(loss,model.input)[0]
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)
In [23]:
iterate = K.function([model.input],[loss,grads])
In [24]:
step = 1.
for i in range(40):
    loss_value, grads_value = iterate([input_img_data])
    input_img_data += grads_value * step
In [25]:
plt.imshow(depross_image(input_img_data[0]))
Out[25]:
<matplotlib.image.AxesImage at 0x2a23ed0af98>
最後の畳み込み層のフィルタ(0番目)の画像

最後の畳み込み層のフィルタ(0番目)

 


<参考>
Francois Chollet 著、株式会社クイープ 翻訳、巣籠悠輔 監訳、「PythonとKerasによるディープラーニング」、マイナビ出版