혼자 공부하는 머신러닝+딥러닝 책을 바탕으로 공부한 내용입니다.
CH8 이미지를 위한 인공 신경망 ③
시각화로 이해하는 합성곱 신경망
합성곱 층의 가중치와 특성 맵을 시각화하여 신경망이 이미지에서 어떤 것을 학습하는지 이해해보자
이 전 절의 코드에 이어서 실행했습니다.
▶ 가중치 시각화
08-2에서 만들었던 모델이 어떤 가중치를 학습했는지 확인하기 위해 체크포인트 파일을 읽고 확인해보자
from tensorflow import keras
model = keras.models.load_model('best-cnn-model.h5')
# 케라스에 추가한 층 확인
model.layers
- 케라스 모델에 추가한 층은 layers 속성에 저장되어 있다.
- Conv2D, MaxPooling2D 층이 번갈아 2번, Flatten, Dense, Dropout, Dense 층이 놓여있다.
첫 번째 합성곱 층의 가중치를 확인해보자 ( 층의 가중치와 절편은 층의 weights 속성에 저장)
# 첫번째 합성곱 층의 가중치, 절편 크기
conv = model.layers[0]
print(conv.weights[0].shape, conv.weights[1].shape)
- 커널 크기가 (3, 3)이고 입력의 깊이가 1이므로 실제 커널 크기는 (3, 3, 1)이다.
- 필터의 개수는 32이므로 weights의 첫번째 원소인 가중치의 크기는 (3, 3, 1, 32)
- 두 번째 원소는 절편의 개수를 나타내고 필터마다 1개의 절편이 있으므로 (32, )이다.
가중치 배열의 평균과 표준편차를 구한 뒤 어떤 분포를 가졌는지 확인해보자
# 가중치 배열의 평균, 표준편차
conv_weights = conv.weights[0].numpy()
print(conv_weights.mean(), conv_weights.std())
# 시각화
import matplotlib.pyplot as plt
plt.hist(conv_weights.reshape(-1, 1))
plt.xlabel('weight')
plt.ylabel('count')
plt.show()
- 가중치들은 0을 중심으로 종 모양 분포를 띠고 있다
32개의 커널을 16개씩 두 줄에 출력해보자.
# 32개의 커널 출력
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개의 가중치를 저장했었다.
- imshow() 함수는 배열에 있는 최댓값과 최솟값을 사용해 픽셀의 강도를 표현한다.
이번에는 훈련하지 않은 빈 합성곱 신경망을 만들어 이 합성곱 층의 가중치가 위의 훈련한 가중치와 어떻게 다른지 그림으로 비교해 볼 것이다.
# 빈 합성곱 신경망
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) #(3, 3, 1, 32)
# 평균, 표준편차 확인
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에 가깝지만 표준편차는 이전과 달리 매우 작은 것을 확인할 수 있다.
- 대부분의 가중치가 -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 클래스를 사용했지만, 복잡한 모델을 만들 때는 함수형 API를 주로 사용한다.
dense1 = keras.layers.Dense(100, activation='sigmoid')
dense2 = keras.layers.Dense(10, activation='softmax')
inputs = keras.Input(shape=(784,))
hidden = dense1(inputs)
outputs = dense2(hidden)
model = keras.Model(inputs, outputs)
- 다음과 같이 만들 수 있다.
특성 맵을 시각화하기 위해 우리가 필요한 것은 첫번째 Conv2D 층이 출력한 특성 맵이다.
print(model.input)
conv_acti = keras.Model(model.input, model.layers[0].output)
- model.input으로 model 객체의 입력을 얻을 수 있다.
- model.input과 model.layers[0].output을 연결하는 새로운 conv_acti 모델을 만들었다.
model 객체의 predict() 메서드를 호출하면 최종 출력층의 확률을 반환하지만, conv_acti의 predict() 메서드를 호출하면 첫 번째 Conv2D의 출력을 반환할 것이다.
▶ 특성 맵 시각화
케라스로 패션 MNIST 데이터셋을 읽은 후 훈련 세트에 있는 첫번째 샘플을 그려보자
(train_input, train_target), (test_input, test_target) =\
keras.datasets.fashion_mnist.load_data()
plt.imshow(train_input[0], cmap='gray_r')
plt.show()
이 샘플을 conv_acti 모델에 주입하여 Conv2D 층이 만드는 특성 맵을 출력해보자
# Conv2D 층이 만드는 특성 맵 출력
inputs = train_input[0:1].reshape(-1, 28, 28, 1) / 255.0
feature_maps = conv_acti.predict(inputs)
print(feature_maps.shape) #(1, 28, 28, 32)
- predict() 메서드는 항상 입력의 첫 번째 차원이 배치 차원일 것으로 기대하므로 하나의 샘플을 전달하더라고 꼭 첫번째 차원을 유지해야 한다. -> 슬라이싱 연산자를 사용해 첫 번째 샘플 선택
- 세임 패딩과 32개의 필터를 사용한 합성곱 층의 출력이므로 (28, 28, 32)이다.
이제 이 특성 맵을 그려보자
# 특성 맵 그리기
fig, axs = plt.subplots(4, 8, figsize=(15, 8))
for i in range(4):
for j in range(8):
axs[i, j].imshow(feature_maps[0,:,:,i*8 + j])
axs[i, j].axis('off')
plt.show()
- 이 특성 맵은 32개의 필터로 인해 입력 이미지에서 강하게 활성화된 부분을 보여준다.
- 필터의 가중치 그림과 비교해보면 7번째 필터는 전체적으로 밝은 색이므로 전면이 모두 칠해진 영역을 감지한다.
두 번째 합성곱 층이 만든 특성 맵도 같은 방식으로 확인할 수 있다.
# 두 번째 합성곱 층이 만든 특성 맵 확인
conv2_acti = keras.Model(model.input, model.layers[2].output)
inputs = train_input[0:1].reshape(-1, 28, 28, 1) / 255.0
feature_maps = conv2_acti.predict(inputs)
print(feature_maps.shape) #(1, 14, 14, 64)
fig, axs = plt.subplots(8, 8, figsize=(12,12))
for i in range(8):
for j in range(8):
axs[i, j].imshow(feature_maps[0,:,:,i*8 + j])
axs[i, j].axis('off')
plt.show()
- 합성곱 신경망의 앞부분에 있는 합성곱 층은 이미지의 시각적인 정보를 감지한다.
- 뒤쪽에 있는 합성곱 층은 앞쪽에서 감지한 시각적인 정보를 바탕으로 추상적인 정보를 학습하므로 우리가 이 그림을 보고 어떤 부위를 감지하는지 직관적으로 이해하기는 어렵다.
'Python > ML & DL 공부' 카테고리의 다른 글
[DL] 09-3 LSTM과 GRU 셀 (0) | 2022.04.07 |
---|---|
[DL] 09-2 순환 신경망으로 IMDB 리뷰 분류하기 (+ 09-1) (0) | 2022.04.07 |
[DL] 08-2 합성곱 신경망을 사용한 이미지 분류 (+ 08-1) (0) | 2022.04.06 |
[DL] 07-3 신경망 모델 훈련 (0) | 2022.03.27 |
[DL] 07-2 심층 신경망 (0) | 2022.03.23 |