Machine Learning/Basic

[8-3] 합성곱 신경망의 시각화(1)

jwjwvison 2021. 4. 15. 00:44

 이 포스팅은 혼자 공부하는 머신러닝 + 딥러닝을 공부하고 정리한것 입니다.


 이번 시간에는 합성곱 층이 이미지에서 어떤 것을 학습했는지 알아보기 위해 합성곱 층의 가중치와 특성 맵을 그림으로 시각화해 보겠다. 이를 통해 합성곱 신경망의 동작 원리에 대한 통찰을 키울 수 있다.

 

 

가중치 시각화

 합성곱 층은 여러 개의 필터를 사용해 이미지에서 특징을 학습한다. 각 필터는 커널이라 부르는 가중치와 절편을 가지고 있다. 일반적으로 절편은 시각적으로 의미가 있지 않다. 가중치는 입력 이미지의 2차원 영역에 적용되어 어떤 특징을 크게 두드러지게 표현하는 역할을 한다. 예를 들어 다음과 같은 가중치는 둥근 모서리가 있는 영역에서 크게 활성화되고 그렇지 않은 영역에서는 낮은 값을 만들 것이다.

 이 필터의 가운데 곡선 부분의 가중치 값은 높고 그 외 부분의 가중치 값은 낮을 것이다. 이렇게 해야 둥근 모서리가 있는 입력과 곱해져서 큰 출력을 만들기 때문이다.

 

from tensorflow import keras

model=keras.models.load_model('/content/drive/MyDrive/혼자공부하는 머신러닝 + 딥러닝/best-cnn-model.h5')
model.layers

 

 그럼 첫 번째 합성곱 층의 가중치를 조사해 보겠다. 층의 가중치와 절편은 층의 weights 속성에 저장되어 있다. weights도 파이썬 리스트이다. 다음 코드에서처럼 layers속성의 첫 번째 원소를 선택해 weights의 첫 번째 원소(가중치)와 두 번째 원소(절편)의 크기를 출력해 보자.

conv=model.layers[0]
print(conv.weights[0].shape,conv.weights[1].shape)

 이전에 커널 크기를 (3,3)으로 지정했고 깊이가 1이였다. 그리고 필터 개수가 32개였으므로 weights의 첫 번째 원소인 가중치의 크기는 (3,3,1,32)가 되었다. weights의 두 번째 원소는 절편의 개수를 나타낸다. 필터마다 1개의 절편이 있으므로 (32,) 크기가 된다.

 weights 속성은 Tensor클래스의 객체이다.

conv_weights=conv.weights[0].numpy()
print(conv_weights.mean(),conv_weights.std())   #평균, 표준편차

 이 가중치의 평균은 0에 가깝고, 표준편차는 0.21 정도이다. 나중에 이 값을 훈련하기 전의 가중치와 비교해 보겠다.

 

import matplotlib.pyplot as plt

plt.hist(conv_weights.reshape(-1,1))  #hist 함수는 1차원 배열을 기대한다
plt.xlabel('weight')
plt.ylabel('count')
plt.show()

 이 가중치가 무엇인가 의미를 학습한 것일까? 잠시 후에 훈련하기 전의 가중치와 비교해 보겠다.

 

 이번에는 32개의 커널을 16개씩 두 줄에 출력해 보겠다.

fig,axs=plt.subplots(2,16,figsize=(15,2))
for i in range(2):
  for j in range(16):
    axs[i,j].imshow(conv_weights[:,:,0,i*16+j],vmin=-0.5,vmax=0.5)
    axs[i,j].axis('off')
plt.show()

 앞에서 conv_weights에 32개의 가중치를 저장했다. 결과 그래프를 보면 이 가중치 값이 무작위로 나열된 것이 아닌 어떤 패턴을 볼 수 있다.

 

 이번에는 훈련하지 않은 빈 합성곱 신경망을 만들어 보겠다.

no_training_model=keras.Sequential()
no_training_model.add(keras.layers.Conv2D(32,kernel_size=3,activation='relu',padding='same',input_shape=(28,28,1)))

no_training_conv=no_training_model.layers[0]
print(no_training_conv.weights[0].shape)

no_training_weights=no_training_conv.weights[0].numpy()
print(no_training_weights.mean(),no_training_weights.std())

plt.hist(no_training_weights.reshape(-1,1))
plt.xlabel('weight')
plt.ylabel('count')
plt.show()

 대부분의 가중치가 -0.15~0.15 사이에 있고 비교적 고른 분포를 보인다. 이런 이유는 텐서플로가 신경망의 가중치를 처음 초기화할 때 균등 분포에서 랜덤하게 값을 선택하기 때문이다.

fig,axs=plt.subplots(2,16,figsize=(15,2))
for i in range(2):
  for j in range(16):
    axs[i,j].imshow(no_training_weights[:,:,0,i*16+j],vmin=-0.5,vmax=0.5)
    axs[i,j].axis('off')
plt.show()

 전체적으로 밋밋하게 초기화되었다. 이 그림을 훈련이 끝난 이전 가중치와 비교해보면 합성곱 신경망이 MNIST 데이터셋의 분류 정확도를 높이기 위해 유용한 패턴을 학습했다는 사실을 알 수 있다.

 

 

 함수형 API

 지금까지는 신경망 모델을 만들 때 케라스 Sequential 클래스를 사용했다. 이 클래스는 층을 차례대로 쌓은 모델을 만든다. 딥러닝에서는 좀 더 복잡한 모델이 많이 있다. 예를 들어 입력이 2개일 수도 있고 출력이 2개일 수도 있다. 이런 경우는 대신 함수형 API를 사용한다.

print(model.input)

'Machine Learning > Basic' 카테고리의 다른 글

[8-3] 합성곱 신경망의 시각화(2)  (0) 2021.04.15
[8-2] 합성곱 신경망을 사용한 이미지 분류  (0) 2021.04.14
[8-1] 합성곱 신경망  (0) 2021.04.14
[7-3] 신경망 모델 훈련  (0) 2021.04.13
[7-2] 심층 신경망  (0) 2021.04.13