[혼자 공부하는 머신러닝+딥러닝] 22강. 합성곱 신경망의 시각화 - 가중치, 특성치 시각화
https://www.youtube.com/watch?v=_lDVf8jDKHg&list=PLJN246lAkhQjoU0C4v8FgtbjOIXxSs_4Q&index=25
- 가중치 시각화
- 합성곱 신경망은 이미지 처리를 잘하기 때문에 무엇으로 학습했는지 시각적으로 보기 좋다.
- 입력값에 필터를 이용해 특성맵을 만들 경우 가주치가 높은 영역은 높은 출력 값을 가지게 되고 가중치가 낮은 영역은 낮은 출력값을 가지게 된다.
- 층의 가중치 분포
- 데이터 준비
model = keras.models.load_model('best-cnn-model.h5')
model.layers
[<keras.layers.convolutional.conv2d.Conv2D at 0x7a0182544820>,
<keras.layers.pooling.max_pooling2d.MaxPooling2D at 0x7a020cc2d840>,
<keras.layers.convolutional.conv2d.Conv2D at 0x7a0179a19060>,
<keras.layers.pooling.max_pooling2d.MaxPooling2D at 0x7a0179a189a0>,
<keras.layers.reshaping.flatten.Flatten at 0x7a01790ad450>,
<keras.layers.core.dense.Dense at 0x7a01790ad390>,
<keras.layers.regularization.dropout.Dropout at 0x7a01790ae2f0>,
<keras.layers.core.dense.Dense at 0x7a01790aeb90>]
- 첫 번째 합성곱 층의 가중치를 확인해 본다.
- 층의 가중치와 절편은 층의 weights 속성에 저장되어 있다.
conv = model.layers[0]
print(conv.weights[0].shape, conv.weights[1].shape)
(3, 3, 1, 32) (32,)
1. 가중치 시각화 하기
- 가중치 분포 히스토그램으로 나타내보기
- 0을 중심으로 종 모양으로 분포되는 것을 확인할 수 있다.
- 입력값이 0인 입력 데이터(의미 없는 값)는 출력값 또한 0이 된다.
import matplotlib.pyplot as plt
plt.hist(conv_weights.reshape(-1,1))
plt.xlabel('weight')
plt.ylabel('count')
plt.show()
- 커널 그림으로 나타내기
- 이번에는 32개의 커널을 16개씩 두 줄에 출력해 보기로 한다.
- vmin과 vmax를 이용해 픽셀의 최댓값과 최솟값을 지정하여 컬러맵으로 표현할 범위를 정한다.
- 픽셀의 특정 부분이 밝고 어두운 것을 토대로 가중치 값이 무작위가 아닌 특정 패턴을 가지고 있다는 걸 예상할 수 있다.
fig, axs = plt.subplots(2, 16, figsize=(15,2))
for i in range(2) :
for j in range(16) :
# 32개의 커널을 16개씩 두 줄에 출력
axs[i, j].imshow(conv_weights[:,:,0,i*16+j], vmin=-0.5, vmax=0.5)
axs[i, j].axis('off')
plt.show()
- 훈련되지 않은 합성곱 신경망의 가중치
- 훈련되지 않은 합성곱 신경망의 가중치를 확인해 보자
- 입력 데이터 없이 층만 만들어서 가중치를 확인한다.
- 3 x 3 커널을 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)
(3, 3, 1, 32)
- 가중치의 평균은 0에 근접하고 표준편차는 0.08에 근접하다.
- 위에서 훈련된 합성곱 신경망의 결과와 비교하면 평균은 비슷하지만 표준편차는 매우 작다는 것을 알 수 있다.
no_training_weights = no_training_conv.weights[0].numpy()
print(no_training_weights.mean(), no_training_weights.std())
-0.010310263 0.0773888
- 그래프로 확인하자
- 0에 가까운 균등분포가 되어 있다.
- 텐서플로우가 신경망의 가중치를 처음 초기화 할때 균등 분포에서 갠덤하게 값을 선택하기 때문에 이런 분포가 만들어 지는 것이고 이후 훈련을 하고 나면 입력 데이터를 기반으로 위에서 확인한 정규분포 그래프를 확인할 수 있다.
plt.hist(no_training_weights.reshape(-1, 1))
plt.xlabel('weight')
plt.ylabel('count')
plt.show()
- 그림으로 가중치를 시각화 해보자
- 초기화된 가중치는 특성 데이터가 없기 때문에 밋밋한 이미지인 것을 확인할 수 있다.
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()
2. 함수형 API
- 케라스의 Sequential 클래스를 사용하면 InputLayer 클래스를 자동으로 추가해 주기 때문에 이전 강좌들에서는 신경을 쓰지 않았었다.
- 함수형 API에서는 InputLayer를 명시적으로 만들어줘야 한다.
- 케라스는 InputLayer 클래스 객체들을 사용자가 쉽게 다룰 수 있도록 Input() 메서드를 제공한다.
- 신경망 모델을 만들 때 케라스의 Sequential 클래스를 사용해왔다. 하지만 딥러닝에서는 좀 더 복잡한 모델이 많이 있으며 이런 경우 함수형 API를 사용한다.
- 함수형 API는 케라스의 Model 클래스를 사용하여 모델을 만든다.
- 사용 방법으로 Dense를 이용해 생성한 클래스를 함수처럼 인자를 정의하고 호출하면 된다.
- 여러개의 입력 혹은 출력을 가진 다양한 구조를 만들 수 있다.
- Model()을 이용해 생성된 결과물은 Sequential 클래스를 이용해 만든 케라스 모델과 동일하다.
- 예시로 Dense층 2개로 만들어진 완전 연결 신경망을 함수형 API를 이용해 구현해 보도록 한다.
inputs = keras.Input(shape=(784,))
dense1 = keras.layers.Dense(100, activation='signoid')
dense2 = keras.layers.Dense(10, activation='softmax')
hidden = dense1(inputs)
outputs = dense2(hidden)
model = keras.Model(inputs, outputs)
- 모델 객체의 층
- 모델의 중간에서 출력을 별도로 반환받아 확인해보는 과정을 수행한다.
- 합성곱층의 활성화출력을 시각화 하는 방법으로 함수형 API를 사용하기로 한다.
- 특성 맵을 시각화하기 위해서는 첫 번째 층 Conv2D의 출력이 필요하고 이 정보는 Conv2D 객체의 output 속성에서 얻을 수 있다. 그리고 input속성으로 모델의 입력을 얻을 수 있다.
- input 확인
print(model.input)
KerasTensor(type_spec=TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='conv2d_input'), name='conv2d_input', description="created by layer 'conv2d_input'")
- 이러한 방법들을 이용해 model.input과 model.layers[0].output 을 연결하는 새로운 conv_acti 모델을 만든다.
conv_acti = keras.Model(model.input, model.layers[0].output)
- model 객체의 predict() 메서드를 호출하면 입력부터 마지막 층까지의 계산을 수행한 최종 출력을 반환받을 수 있다.
- conv_acti의 predict() 메서드를 호출하여 Conv2D의 출력을 받환 받고 이를 이용해 특성 맵을 시각화 한다.
3. 특성 맵 시각화
- 케라스 패션 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 층이 만드는 특성맵을 출력하자.
- 28 x 28 크기의 필터 32개로 구성되어 있다.
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)
- 필터 32개를 시각화 해보자
- 이 특성 맵은 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()
- 두 번째 합성곱 층이 만든 특성 맵도 시각화 해보자
- 두 번째 합성곱이 만든 특성 맵은 시각적으로 이해하기 어렵다.
- 뒤쪽에 있는 합성곱층은 앞쪽에서 감지한 시각적 정보를 바탕으로 추상적인 정보를 학습하기에 사람의 시각으로 판단하기 어려운 경우가 대부분이다.
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()
- 합성곱 신경망은 낮은층에서 저수준 특성을 학습한다.
- 단순 모양, 패턴, 색깔 등
- 층이 깊어질수록 고수준 특성을 학습힌다.
- 추상적인 특성을 학습한다.
- reference :
댓글