딥러닝_CNN_합성곱연산(Convnet)_6. 컨브넷 학습 해석

주지윤·2022년 12월 13일
0

딥러닝

목록 보기
19/21

컨브넷 해석가능성

  • 컴퓨터 비전에서의 문제점: 해석가능성
    • 모델이 학습한 표현을 사람이 이해하기 쉬운 형태로 뽑아내거나 제시하기 어려움
  • 컨브넷의 표현: 시각화 가능
    • 중간층 출력: 입력을 어떻게 변형시키는가 파악, 필터의 의미 파악
    • 필터 시각화: 필터가 찾으려는 시각적 패턴과 개념 이해
    • 클래스 활성화에 대한 히트맵을 이미지에 시각화: 어떤 클래스에 속하는 데 어떤 부분이 기여했는지 이해, 객체 위치 추정

중간층 출력

  • 각 컨브층과 풀링층의 output을 그리는 것
  • 사용모델: cat vs dog 데이터 셋에서 데이터 증식한 모델
    • convnet_from_scratch_with_augmentation.keras

이미지 다운로드, 이미지(원본, 텐서) 출력

🔸 활용할 이미지(1장) 다운로드

# 이미지 다운로드
img = keras.utils.get_file(fname = 'cat.jpg', origin = 'https://img-datasets.s3.amazonaws.com/cat.jpg')
img

/root/.keras/datasets/cat.jpg


🔸 이미지 출력 함수, 이미지 텐서 저장

# 함수

def get_img_array(img, target_size):
  # 이미지 로드
  img = keras.utils.load_img(img, target_size=target_size)
  # 이미지 배열화
  array = keras.utils.img_to_array(img)
  # 이미지 배치변환을 위한 차원 추가(samples,height,widht,channel)
  array = np.expand_dims(array,axis=0)
  return array
img_tensor = get_img_array(img, target_size=(180,180))

🔸 이미지 출력

  • 원본
keras.utils.load_img(img, target_size=(180,180))

  • 텐서 출력
# 이미지 출력
# img_tensor의 크기는(1,180,180,3)
# 1개의 sample만 가지고 있지만 항상 슬라이싱으로 한 개의 sample을 불러야 한다

import matplotlib.pyplot as plt
plt.axis('off')
plt.imshow(img_tensor[0].astype('uint8'))
plt.show()



이미지를 모델에 입력 → 각각의 컨브층,풀링층의 output을 전부 출력

🔸 각 층의 활성화(output)을 반환하는 모델 생성

# cat vs dog에 사용한 model 불러오기
model = keras.models.load_model('convnet_from_scratch_with_augmentation.keras')

# 층 활성화 반영하는 모델 생성
from keras import layers

layer_outputs = []
layer_names = []

for layer in model.layers:
  # layer가 컨브넷층이나 풀링층일 경우, 그 layer의 output 리스트에 추가
  if isinstance(layer, (layers.Conv2D, layers.MaxPool2D)):
    layer_outputs.append(layer.output)
    layer_names.append(layer.name)

activation_model = keras.Model(inputs = model.input, outputs=layer_outputs)

  • isinstance(인스턴스, 데이터나 클래스 타입)
    • 첫 번째 매개변수(인스턴스) : 확인하고자 하는 데이터의 값
    • 두 번째 매개변수 : 확인하고자 하는 데이터의 타입, 클래스
    • 반환 값 : 인스턴스와 타입이 같으면 True를 반환하고, 다르면 False를 반환

🔸 층 활성화 계산(적용,예측)

# 활성화 계산

# 층 활성화마다 배열 하나씩, 총 9개(컨브와 풀링층의 개수)의 배열을 가진 리스트 반환
activations= activation_model.predict(img_tensor)

🔸 모델의 첫번째 층 확인(5번째 필터)

# 첫번째 층의 5번째 피처 그리기

import matplotlib.pyplot as plt

print(activations[0].shape)

plt.matshow(activations[0][0,:,:,5],cmap='viridis')
plt.axis('off')
plt.show()

(1, 178, 178, 32)


🔸 모든 층의 활성화 확인

# 모든 층의 활성화에 있는 전체 채널 시각화

# 이미지그리드 행 개수
images_per_row = 16 

for layer_name, layer_activation in zip(layer_names, activations):
    # layer_activation(1,size,size,n_features)
    n_features = layer_activation.shape[-1]
    size = layer_activation.shape[1]

    # 모든 피처를 그릴 그리드 준비
    n_cols = n_features // images_per_row
    display_grid = np.zeros(((size + 1) * n_cols - 1,
                             images_per_row * (size + 1) - 1))
    

    for col in range(n_cols):
        for row in range(images_per_row):
            # 하나의 채널 이미지
            channel_index = col * images_per_row + row
            channel_image = layer_activation[0, :, :, channel_index].copy()

            # 채널값 정규화[0,255], 모두 0인 채널은 그대로
            if channel_image.sum() != 0:
                channel_image -= channel_image.mean()
                channel_image /= channel_image.std()
                channel_image *= 64
                channel_image += 128
            channel_image = np.clip(channel_image, 0, 255).astype("uint8")

            # 이미지그리드에 채널 행렬 밀어 넣기
            display_grid[
                col * (size + 1): (col + 1) * size + col,
                row * (size + 1) : (row + 1) * size + row] = channel_image

    # 이미지 그리드 출력            
    scale = 1. / size
    plt.figure(figsize=(scale * display_grid.shape[1],
                        scale * display_grid.shape[0]))
    plt.title(layer_name)
    plt.grid(False)
    plt.axis("off")
    plt.imshow(display_grid, aspect="auto", cmap="viridis")


출력된 이미지 해석

  • 첫번째 층: 에지 감지기의 역할로 보임
  • 깊은 층:
    • 추상적, 고수준의 개념을 인코딩
      • 콘텐츠 정보 ↓ 이미지 클래스(타깃) 정보 ↑
    • 비어있는 활성화
      • 활성화 되지 않은 필터: 인코딩된 패턴에 해당하는 사항이 없음

0개의 댓글