CNN Visualization

변현섭·2024년 8월 16일
0

지금은? AI 전성시대!

목록 보기
21/21
post-thumbnail

1. Weight Visualization

필터가 학습한 가중치를 시각화함으로써, CNN이 어떤 특징을 학습했는지 확인할 수 있다.

① 지난 포스팅에서 작성한 코드를 먼저 실행시켜 모델을 저장한 후, 아래의 코드를 이용해 해당 모델을 불러온다.

from tensorflow import keras

model = keras.models.load_model('best-cnn-model.keras')

model.layers를 이용해 모델의 계층 정보를 출력한다.

  • Conv2D, MaxPooling2D, Flatten, Dense, Dropout, 출력층이 차례로 등장한다.
model.layers

③ Conv2D에서 사용된 커널의 가중치를 확인해보자. 각 층에서 사용된 가중치와 절편은 weights 속성으로 확인할 수 있다.

  • (3, 3, 1) 크기의 커널을 32개 사용했으므로, 가중치의 크기는 (3, 3, 1, 32)가 되고, 절편의 크기는 (32,)가 된다.
conv = model.layers[0]
print(conv.weights[0].shape, conv.weights[1].shape)

④ weights 배열을 Numpy 배열로 변환하여 평균과 표준편차를 구할 수도 있다.

  • 가중치의 평균은 0에 가까우며, 표준편차는 0.26 정도이다.
conv_weights = conv.weights[0].numpy()
print(conv_weights.mean(), conv_weights.std())

⑤ 가중치의 분포를 한 눈에 확인해보기 위해 히스토그램을 그릴 수 있다.

  • hist() 메서드에는 1차원 배열을 전달해야 하므로, conv_weights의 사이즈를 변환해주어야 한다.
import matplotlib.pyplot as plt

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

⑥ 이번에는 훈련되지 않은 CNN의 가중치로 히스토그램을 그려보겠다.

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]
no_training_weights = no_training_conv.weights[0].numpy()

plt.hist(no_training_weights.reshape(-1, 1))
plt.xlabel('Weight')
plt.ylabel('Count')
plt.show()

훈련되지 않은 모델의 가중치로 그린 히스토그램은 모든 가중치에 대해 거의 균등한 분포를 보이게 된다. 이는 Tensorflow에서 처음 CNN의 가중치를 초기화할 때, 균등 분포에서 랜덤하게 값을 선택하기 때문이다. 이 히스토그램을 훈련된 모델의 가중치로 그린 히스토그램과 비교해보면, 확연히 다른 모습임을 알 수 있다. 이는 모델이 무언가 유의미한 패턴을 학습했다는 사실을 의미한다.

2. Functional API

지금까지는 신경망 모델을 만들 때, Keras의 Sequential 클래스를 사용해왔다. 그러나, Sequential 클래스는 복잡한 모델을 구성해야 할 때에는 사용이 어려운데, 이 때 사용할 수 있는 방법이 바로 Functional API(함수형 API)이다.

Functional API는 각 Layer 객체를 마치 함수처럼 호출하는 방식을 사용하여 매우 직관적으로 Layer를 추가할 수 있다. 다만 한 가지 주의할 것은, Sequential 클래스는 InputLayer 클래스를 자동으로 추가해주지만, Functional API를 사용할 때에는 InputLayer를 직접 추가해주어야 한다는 것이다.

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)

3. Feature Map Visualization

함수형 API를 사용하여, 특성 맵을 시각화할 수도 있다. Model 객체의 input과 Conv2D Layer의 output을 연결한 후, predict() 메서드를 사용하면 Conv2D Layer에서 출력한 특성 맵을 얻을 수 있다.

① 입력 데이터와 Conv2D Layer의 출력을 연결한다.

conv_acti = keras.Model(model.input, model.layers[0].output)

② Fashion MNIST 데이터를 불러온 후, 훈련 Set의 첫번째 Sample을 그려보자.

  • 첫번째 Sample은 앵클 부츠이다.
model = keras.models.load_model('best-cnn-model.keras')
conv_acti = keras.Model(model.inputs, model.layers[0].output)

(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
plt.imshow(train_input[0], cmap='gray_r')
plt.show()

③ 입력 데이터에 대해 전처리를 수행하고, 특성 맵 배열을 만든다.

inputs = train_input[0:1].reshape(-1, 28, 28, 1) / 255
feature_maps = conv_acti.predict(inputs)

④ feature_maps의 크기를 확인해보자.

  • 이 특성 맵은 32개의 필터로 인해 입력 이미지에서 상하게 활성화된 부분을 보여준다.
print(feature_maps.shape)

⑤ 32개의 특성 맵을 이미지로 그려보자.

fig, axs = plt.subplots(4, 8, figsize=(15, 8))
for i in range(4):
for j in range*8(:

⑥ 계속해서 두번째 Convolution Layer에서의 출력도 확인해보자.

  • 두번째 Convolution Layer는 Pooling에 의해 크기가 절반으로 줄어들고, 64개의 필터를 사용하므로, (1, 14, 14, 64)의 크기를 갖는다.
conv2_acti = keras.Model(model.inputs, model.layers[2].output)
feature_maps = conv2_acti.predict(train_input[0:1].reshape(-1, 28, 28, 1) / 255)
print(feature_maps.shape)

⑦ 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()

첫번째 Convolution Layer에서와 달리, 거의 형태를 알아볼 수 없는 이미지가 출력되었다. 이는 Layer가 깊어질수록 더욱 복잡하고 추상적인 패턴을 학습하기 때문이다.

profile
LG전자 Connected Service 1 Unit 연구원 변현섭입니다.

0개의 댓글