2 minute read

합성곱 신경망의 시각화

가중치 시각화

image-20220607202303567

그림에서는 둥근 모서리가 있는 부분이 출력이 가중치가 곱해졌을 때 출력이 더 높게 나와야 하기 때문에

둥근 모서리가 있는 부분의 가중치값은 높아야 할 것이다.

model.layers
# [<keras.layers.convolutional.conv2d.Conv2D at 0x7f1da1df4250>,
#  <keras.layers.pooling.max_pooling2d.MaxPooling2D at 0x7f1da1df4fa0>,
#  <keras.layers.convolutional.conv2d.Conv2D at 0x7f1da0533730>,
#  <keras.layers.pooling.max_pooling2d.MaxPooling2D at 0x7f1da04ec4f0>,
#  <keras.layers.reshaping.flatten.Flatten at 0x7f1da04fe9a0>,
#  <keras.layers.core.dense.Dense at 0x7f1da04fef40>,
#  <keras.layers.regularization.dropout.Dropout at 0x7f1da04feca0>,
#  <keras.layers.core.dense.Dense at 0x7f1da0506d30>]
# 합성곱-풀링-합성곱2-풀링-dense-dropout-dense 의 순서

conv = model.layers[0]

print(conv.weights[0].shape, conv.weights[1].shape)
#(3, 3, 1, 32) (32,)
# 3x3x1필터 32개 사용 절편도 32개의 1차원 벡터로 구성
# conv.weights에는 필터(커널)와 가중치 정보가 있다


가중치 시각화

conv_weights = conv.weights[0].numpy()
print(conv_weights.mean(), conv_weights.std())
# -0.021033935 0.23466988

plt.hist(conv_weights.reshape(-1, 1))
#conv_weights:(3, 3, 1, 32)를 1차원으로 펼치기 
plt.xlabel('weight')
plt.ylabel('count')
plt.show()

image-20220607203201081

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)
        #imshow는 '상대적으로' 최소값을 가장 어둡게 최대값을 가장 밝게 표현한다 각 이미지의 색이 같다고 해도 값이 같은 것은 아니다
        # vmin=-0.5, vmax=0.5를 통해서 가중치를 서로 비교할 수 있게 해준다.
        axs[i, j].axis('off')

plt.show()

image-20220607203948094

함수형 API

입력이 2개이거나 출력이 2개인 경우는 Sequential 클래스를 사용하기 어렵다.

# 함수처럼 출력값을 얻는다
inputs = keras.Input(shape=(784,))
#input값을 지정하기 위해 inputlayer클래스 객체를 다루는 Input()함수를 호출하여 사용한다.
#shape=(784,)를 통해서 입력의 크기를 지정해준다.
#Sequential 클래스에서는 자동으로 지정된다.

dense1 = keras.layers.Dense(100, activation = 'sigmoid')
dense2 = keras.layers.Dense(100, activation = 'softmax')

hidden = dense1(inputs)

outputs = dense2(hidden)

model = keras.Model(inputs, outputs)

image-20220610233210468

여러개의 입력/출력을 가진 다양한 모델을 만들 수 있게 해 준다.

image-20220610233427781

Conv2D가 출력한 특성맵은 Conv2D의 output속성에서 얻을 수 있다.

model객체의 입력은 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'")

conv_acti = keras.Model(model.input, model.layers[0].output)
#model.layers의 첫번째 출력을 참조
#model.layers[0].output에 활성화 출력이 담겨져 있기 때문에 입력과 출력으로 conv_acti라는 새로운 모델을 만들 수 있다.

model.layers
#  [<keras.layers.convolutional.Conv2D at 0x7fdba58d3810>, <-model.layers[0].output
#  <keras.layers.pooling.MaxPooling2D at 0x7fdba58d3dd0>,
#  <keras.layers.convolutional.Conv2D at 0x7fdb901998d0>,
#  <keras.layers.pooling.MaxPooling2D at 0x7fdb9013de50>,
#  <keras.layers.core.flatten.Flatten at 0x7fdb9014b650>,
#  <keras.layers.core.dense.Dense at 0x7fdb9014b9d0>,
#  <keras.layers.core.dropout.Dropout at 0x7fdb901519d0>,
#  <keras.layers.core.dense.Dense at 0x7fdb9013df90>]

첫 번째 특성 맵 시각화

conv_acti = keras.Model(model.input, model.layers[0].output)
#(모델의 input, 첫 번째 합성곱의 출력값)

(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.0 
#train_input[0:1]훈련데이터의 첫 번째 원소
#차원 28x28x1로 바꾸고 이미지이므로 /255
feature_maps = conv_acti.predict(inputs)
#특성맵 출력
print(feature_maps.shape)
#(1, 28, 28, 32)
# 세임 패딩 이후에 32개의 필터(커널)을 사용했으므로(28,28,32)이다

fig, axs = plt.subplots(4, 8, figsize=(15,8))

#32개의 특성맵을 8개씩 imshow로 출력
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()

image-20220610235205071

image-20220610235501580

image-20220607203948094

32개의 특성맵과 가중치를 시각화한 것을 비교해보면 그 특성이 반영되어있는 것을 볼 수 있다.

두 번째 특성 맵 시각화

conv2_acti = keras.Model(model.input, model.layers[2].output)

feature_maps = conv2_acti.predict(train_input[0:1].reshape(-1, 28, 28, 1)/255.0)
print(feature_maps.shape)
fig, axs = plt.subplots(8, 8, figsize=(12,12))


#64개의 특성 맵을 8개씩 나누어서 imshow
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()

image-20220611021424360

어떤 것을 학습했는지 알아보기 힘들다

image-20220611021934164

합성곱 신경망은 저수준 특성 학습에서 층이 깊어질수록 고수준 특성을 학습을 한다.

위의 예시에서는 conv1에서는 이미지의 시각적인 정보를 감지했다고 한다면 conv2에서는 conv1의 정보를 바탕으로 추상적인 정보를 학습한다고 볼 수 있다.

합성곱층의 활성화출력을 확인하는 것은 저수준일때는 유용하지만 고수준의 특성에서는 의미가 거의 없다.

참고

박해선,혼자 공부하는 머신러닝, 한빛미디어, 2021,464~483p