[ML Study]ch08-3 합성곱 신경망의 시각화
혼자 공부하는 머신러닝+딥러닝 [8-3]
본 포스팅 한빛미디어의 <혼자공부하는 머신러닝+딥러닝(박해선 저)>를 요약 정리했습니다.
1. 가중치 시각화
각 필터는 커널이라 부르는 가중치와 절편을 가지고 있는데 일반적으로 절편은 시각적으로 의미가 있지 않다.
가중치는 입력 이미지의 2차원 영역에 적용되어 어떤 특징을 크게 두드러지게 표현하는 역할을 한다.
2절에서 best-cnn-model.h5 파일을 생성하고 이어서 학습해야 하고, model.layers를 통해 층을 확인한다.
Conv2D, MaxPooling2D, Conv2D, MaxPooling2D, Flatten, Dense, Dropout, Dense 층으로 이루어진 것을 확인 할 수 있다.
layers 속성의 첫 번째 원소를 선택해 weigths의 첫 번째 원소(가중치)와 두 번째 원소(절편)의 크기를 출력해보면
conv = model.layers[0]
print(conv.weights[0].shape, conv.weights[1].shape)
(3, 3, 1, 32) (32, )와 같이 나타난다.
그리고 가중치 배열의 평균과 표준편차를 넘파이 mean()과 std() 메서드로 계산해보면
conv_weights = conv.weights[0].numpy()
print(conv_weights.mean(), conv_weights.std())
가중치의 평균값은 0에 가깝고 표준편차는 0.27 정도이다.
이 값을 히스토그램으로 확인해보면 0을 중심으로 종 모양 분포를 띠고 있는 것을 알 수 있다.
이번에는 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()
훈련하지 않은 빈 합성곱 신경망을 만들기 위해서 먼저 Sequential 클래스로 모델을 생성하고 Conv2D 층을 하나 추가한다.
다음 이 층의 가중치를 no_training_conv 변수에 저장해 이번처럼 평균과 표준편차를 확인해본다.
no_training_weights = no_training_conv.weights[0].numpy()
print(no_training_weights.mean(), no_training_weights.std())
-0.010310263과 0.0773888으로 표준편차가 매우 작게 나타난다.
히스토그램으로 확인해 보면 이전과 다르게 비교적 고른 분포를 볼 수 있다.
그 이유는 텐서플로가 신경망의 가중치를 처음 초기화할 때 균등 분포에서 랜덤하게 값을 선택하기 때문이다.
2. 함수형 API
함수형 API는 케라스의 Model 클래스를 사용해서 모델을 만든다.
마치 체인처럼 입력에서 출력까지 연결하고 마지막에 Model 클래스에 입력과 출력을 지정하여 모델을 만든다.
model 객체의 층에서 Conv2D의 출력이 필요한데,
model 객체의 입력과 Conv2D의 출력을 알 수 있다면 이 둘을 연결해 새로운 모델을 만들 수 있다.
첫 번째 Conv2D 층이 출력한 특성 맵이 필요하므로 Conv2D 객체의 output 속성에서 얻으면 된다.
model.layers[0].output처럼 참조 가능하고 model 객체의 입력은 model.input으로 알 수 있다.
또한 이 둘을 연결하는 새로운 conv_acti 모델을 만들 수 있다.
conv_acti = keras.Model(model.input, model.layers[0].output)
model 객체의 predict() 메서드를 호출하면 최종 출력층의 확률을 반환하지만,
conv_acti 객체의 predict() 메서드를 호출하면 첫 번째 Conv2D의 출력을 반환할 것이다.
3. 특성 맵 시각화
훈련 세트에 있는 첫 번째 샘플을 보면 앵클 부츠가 나온다.
이 샘플을 conv_acti 모델에 주입해 Conv2D 층이 만드는 특성 맵을 출력하는 것을 해보자.
첫 번째 차원을 유지해야 하므로 슬라이싱 연산자를 통해 첫 번째 샘플을 선택한다.
feature_maps.shape의 크기를 확인해보면 (1, 28, 28, 32)가 나오는데
세임 패딩과 32개의 필터를 사용한 합성곱의 출력이므로 (28, 28, 32)로 출력되고, 첫 번째 차원은 배치 차원이라 1로 출력된다.
imshow 함수로 특성 맵을 그려보면 32개의 필터로 인해 입력 이미지에서 강하게 활성화된 부분을 보여준다.
출력한 그림을 보고 어떤 부분이 강하게 활성화되어 보여주는지 일 수 있다.
이와 같은 방법으로 두 번째 합성곱 층의 특성 맵도 확인 가능하다.
첫 번째 풀링 층에서 가로세로 크기가 절반으로 줄고, 필터 개수가 64개이므로 (1, 14, 14, 64)일 것이다.
하지만 inshow 함수로 확인 했을 때 시각적으로 확인하기 어렵다.
두 번째 합성곱의 필터 크기는 (3, 3, 32)인데,
두 번째 합성곱 층의 첫 번째 필터가 앞서 출력된 32개의 특성 맵과 곱해져 두 번째 합성곱 층의 첫 번째 특성 맵이 된다.
따라서 (14, 14, 32) 특성 맵에서 어떤 부위를 감지하는지 직관적으로 알기 어렵다.
합성곱 층을 많이 쌓을 수록 심해지는데 뒤로 갈수록 앞쪽에서 감지한 시각적인 정보를 바탕으로 추상적인 정보를 학습한다고 할 수 있다.