합성곱 신경망의 시각화
합성곱 신경망의 시각화
가중치 시각화
그림에서는 둥근 모서리가 있는 부분이 출력이 가중치가 곱해졌을 때 출력이 더 높게 나와야 하기 때문에
둥근 모서리가 있는 부분의 가중치값은 높아야 할 것이다.
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()
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()
함수형 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)
여러개의 입력/출력을 가진 다양한 모델을 만들 수 있게 해 준다.
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()
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()
어떤 것을 학습했는지 알아보기 힘들다
합성곱 신경망은 저수준 특성 학습에서 층이 깊어질수록 고수준 특성을 학습을 한다.
위의 예시에서는 conv1에서는 이미지의 시각적인 정보를 감지했다고 한다면 conv2에서는 conv1의 정보를 바탕으로 추상적인 정보를 학습한다고 볼 수 있다.
합성곱층의 활성화출력을 확인하는 것은 저수준일때는 유용하지만 고수준의 특성에서는 의미가 거의 없다.
참고
박해선,혼자 공부하는 머신러닝, 한빛미디어, 2021,464~483p