두번째로는 필터가 반응하는 시각적 패턴을 그리는 것인데요.

경사 상승법을 적용하여 이미지의 필터가 무엇을 인식하고 있는지 보는 것 입니다.

 

- 필터 시각화를 위한 손실 텐서 정의

from keras.applications import VGG16
from keras import backend as K

model = VGG16(weights = 'imagenet', 
             include_top = False)

layer_name = 'block3_conv1'
filter_index = 0

layer_output = model.get_layer(layer_name).output
loss = K.mean(layer_output[:, :, :, filter_index])

# 입력에 대한 손실의 그래디언트 구하기
grads = K.gradients(loss, model.input)[0]

# 그래디언트 정규화 ( 경사 상승법 과정을 부드럽게 하기 위하여 사용 )
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5) # 1e-5는 0 나눗셈 방지, 이를 gradient-clipping 이라고 함.

# 손실값과 그래디언트 값을 얻기 위함.
# 주어진 입력 이미지에 대해 손실 텐서와 그래디언트 텐서를 계산한다. 
iterate = K.function([model.input], [loss, grads])

import numpy as np
loss_value, grads_value = iterate([np.zeros((1, 150, 150, 3))])

# 확률적 경사 상승법을 사용한 손실 최대화하기
input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.

step = 1.
for i in range(40):
    loss_value, grads_value = iterate([input_img_data])
    input_img_data += grads_value * step 

 

지금 계속 신경망의 필터에 경사상승법을 적용하여 어떤 패턴에 반응을 하는지 보기위한 작업을 하고 있습니다. 

다음은 텐서를 이미지 형태로 변환하기 위한 util function입니다.

def deprocess_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

다음 함수는 필터 활성화를 최대화하는 패턴을 이미지 텐서로 출력합니다.

또한 위의 모든 함수를 사용하여 패턴을 출력해봅시다.

def generate_pattern(layer_name, filter_index, size = 150):
    # 주어진 층과 필터의 활성화를 최대화 하기위한 loss 정의
    layer_output = model.get_layer(layer_name).output
    loss = K.mean(layer_output[:, :, :, filter_index])
    
    grads = K.gradients(loss, model.input)[0]
    
    # grad clipping
    grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)
    
    # 입력 이미지에 대한 손실과 그래디언트 반환
    iterate = K.function([model.input], [loss, grads])
    # 회색이미지로 시작
    input_img_data = np.random.random((1, size, size, 3)) * 20 + 128.
    
    step = 1.
    for i in range(40):
        loss_value, grads_value = iterate([input_img_data])
        input_img_data += grads_value * step
        
    img = input_img_data[0]
    return deprocess_image(img)

위 함수들을 이용하여 다른 층도 살펴봅시다.

layer_name = 'block1_conv1'
size = 64
margin = 5

# 결과를 담은 빈 검은 이미지
results = np.zeros((8 * size + 7 * margin , 8 * size + 7 * margin, 3), dtype='uint8')

for i in range(8):
    for j in range(8):
        # layer_name에 있는 i + (j*8) 번째 필터에 대한 패턴 생성
        filter_img = generate_pattern(layer_name, i + (j * 8), size = size)
        
        horizontal_start = i * size + i * margin
        horizontal_end = horizontal_start + size
        vertical_start = j * size + j * margin
        vertical_end = vertical_start + size
        results[horizontal_start:horizontal_end, vertical_start: vertical_end, :] = filter_img
        
plt.figure(figsize=(20, 20))
plt.imshow(results)

다음과 같은 패턴을 통해 여러분의 모델이 어떤 패턴을 인식하는지 알아 볼 수 있겠네요. (이미지는 일부분만 캡쳐)

 

 

Reference

본 코드의 내용은 '케라스 창시자에게 배우는 딥러닝' 교재의 github를 참조 하였습니다. -- 

https://github.com/fchollet/deep-learning-with-python-notebooks