08-1 합성곱 신경망의 구성 요소¶
☆ 합성곱 : 밀집층과 비슷하게 입력과 가중치를 곱하고 절편을 더하는 선형 계산이지만 밀집층과 달리 합성곱은 입력 데이터 전체에 가중치를 적용하는 것이 아니라 일부에 가중치를 곱한다.
☆ 합성곱 층의 뉴런에 있는 가중치 개수는 하이퍼파라미터이다.
☆ 합성곱 신경망(CNN)에서는 뉴런을 필터(filter) 또는 커널(kernel)이라고 부른다.
☆ 합성곱 계산을 통해 얻은 출력을 특성 맵(feature map)이라고 부른다.
# 케라스 합성곱 층
from tensorflow import keras
keras.layers.Conv2D(10,kernel_size=(3,3),activation='relu')
# keras.layers.Conv2D(필터 개수, 커널 크기, 활성화 함수)
<keras.layers.convolutional.Conv2D at 0x25d2c63c9a0>
패딩과 스트라이드¶
☆ 입력 배열의 주위를 가상의 원소로 채우는 것을 패딩(padding)
☆ 입력과 특성 맵의 크기를 동일하게 만들기 위해 입력 주위에 0으로 패딩 하는 것을 세임 패팅(same padding)이라고 부른다.
☆ 패딩 없이 순수한 입력 배열에서만 합성곱을 하여 특성 맵을 만드는 경우를 밸리드 패딩(valid padding)이라고 한다.
- 적절한 패딩은 이미지의 주변에 있는 정보를 잃어버리지 않도록 도와준다.
# 패딩 설정 방법(기본값 : valid)
keras.layers.Conv2D(10,kernel_size=(3,3),activation='relu',padding='same') # 패딩 설정 가능
<keras.layers.convolutional.Conv2D at 0x25d54bdb940>
☆ 스트라이드(stride) : 합성곱 층에서 필터가 입력 위를 이동하는 크기로 기본으로 스트라이드는 1픽셀. 조절 가능
# 스트라이드 설정 방법(기본값 : 1)
keras.layers.Conv2D(10,kernel_size=(3,3),activation='relu',padding='same',strides=1) # 스트라이드 설정 가능
<keras.layers.convolutional.Conv2D at 0x25d54c75100>
☆ 풀링(pooling) : 합성곱 층에서 만든 특성 맵의 가로세로 크기를 줄이는 역할을 수행, 하지만 특성 맵의 개수는 줄이지 않는다. ex) (2,2,3) -> (1,1,3)
☆ 풀링에는 가중치가 없으며 가장 큰 값을 고르거나 평균값을 계산하는 최대 풀링과 평균 풀링이 있다.
☆ 풀링에서는 겹치지 않고 이동한다. 따라서 풀링은 풀링 크기와 스트라이드가 같다. 또한 패딩도 없다.
# 풀링 설정 방법
keras.layers.MaxPooling2D(2) # 풀링의 크기 2 , 평균 풀링 제공은 AveragePooling2D
# 풀링은 패딩하지 않으며 스트라이드의 값도 자동 지정된다.
<keras.layers.pooling.MaxPooling2D at 0x25d2c63c160>
- 평균 풀링보다 최대 풀링을 많이 사용한다. 그 이유는 평균 풀링은 특성 맵에 있는 중요한 정보를 (평균하여) 희석시킬 수 있기 때문
- 풀링을 사용하는 이유는 합성곱에서 스트라이드를 크게 하여 특성 맵을 줄이는 것보다 풀링 층에서 크기를 줄이는 것이 더 나은 성능을 내기 때문
컬러 이미지를 사용한 합성곱¶
☆ 입력이나 필터의 차원이 몇 개인지 상관없이 항상 출력은 하나의 값. 즉 특성 맵에 있는 한 원소가 채워진다.
08-2 합성곱 신경망을 사용한 이미지 분류¶
패션 MNIST 데이터 불러오기¶
from tensorflow import keras
from sklearn.model_selection import train_test_split
(train_input, train_target), (test_input, test_target) = \
keras.datasets.fashion_mnist.load_data()
train_scaled = train_input.reshape(-1, 28, 28, 1) / 255.0 # Conv2D 사용을 위해 채널 차원 추가
train_scaled, val_scaled, train_target, val_target = train_test_split(
train_scaled, train_target, test_size=0.2, random_state=42)
합성곱 신경망 만들기¶
# 첫 번째 합성곱-풀링 층 추가
model = keras.Sequential()
model.add(keras.layers.Conv2D(32, # 32개의 필터
kernel_size=3,
activation='relu',
padding='same',
input_shape=(28,28,1)))
model.add(keras.layers.MaxPooling2D(2)) # 풀링 추가
# 두 번째 합성곱-풀링 층 추가
model.add(keras.layers.Conv2D(64, kernel_size=(3,3), activation='relu',
padding='same'))
model.add(keras.layers.MaxPooling2D(2))
# 3차원 특성 맵을 일렬로 펼칠 코드
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4)) # 과대적합 방지
model.add(keras.layers.Dense(10, activation='softmax'))
# 모델 요약
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_3 (Conv2D) (None, 28, 28, 32) 320
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 32) 0
_________________________________________________________________
conv2d_4 (Conv2D) (None, 14, 14, 64) 18496
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 7, 7, 64) 0
_________________________________________________________________
flatten (Flatten) (None, 3136) 0
_________________________________________________________________
dense (Dense) (None, 100) 313700
_________________________________________________________________
dropout (Dropout) (None, 100) 0
_________________________________________________________________
dense_1 (Dense) (None, 10) 1010
=================================================================
Total params: 333,526
Trainable params: 333,526
Non-trainable params: 0
_________________________________________________________________
모델 컴파일과 훈련¶
☆ 케라스 API의 장점은 딥러닝 모델의 종류나 구성 방식에 상관없이 컴파일과 훈련 과정이 같다는 점이다.
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
metrics='accuracy')
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-cnn-model.h5',
save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2,
restore_best_weights=True) # 현재 model 객체가 최적의 모델 파라미터로 복원
history = model.fit(train_scaled, train_target, epochs=20,
validation_data=(val_scaled, val_target),
callbacks=[checkpoint_cb, early_stopping_cb])
Epoch 1/20
1500/1500 [==============================] - 68s 45ms/step - loss: 0.5179 - accuracy: 0.8138 - val_loss: 0.3338 - val_accuracy: 0.8809
Epoch 2/20
1500/1500 [==============================] - 66s 44ms/step - loss: 0.3423 - accuracy: 0.8772 - val_loss: 0.2765 - val_accuracy: 0.8997
Epoch 3/20
1500/1500 [==============================] - 77s 51ms/step - loss: 0.2952 - accuracy: 0.8939 - val_loss: 0.2595 - val_accuracy: 0.9052
Epoch 4/20
1500/1500 [==============================] - 72s 48ms/step - loss: 0.2645 - accuracy: 0.9035 - val_loss: 0.2400 - val_accuracy: 0.9097
Epoch 5/20
1500/1500 [==============================] - 66s 44ms/step - loss: 0.2405 - accuracy: 0.9125 - val_loss: 0.2291 - val_accuracy: 0.9172
Epoch 6/20
1500/1500 [==============================] - 66s 44ms/step - loss: 0.2216 - accuracy: 0.9185 - val_loss: 0.2273 - val_accuracy: 0.9186
Epoch 7/20
1500/1500 [==============================] - 65s 44ms/step - loss: 0.2018 - accuracy: 0.9252 - val_loss: 0.2329 - val_accuracy: 0.9133
Epoch 8/20
1500/1500 [==============================] - 73s 48ms/step - loss: 0.1858 - accuracy: 0.9303 - val_loss: 0.2254 - val_accuracy: 0.9175
Epoch 9/20
1500/1500 [==============================] - 68s 45ms/step - loss: 0.1750 - accuracy: 0.9337 - val_loss: 0.2225 - val_accuracy: 0.9212
Epoch 10/20
1500/1500 [==============================] - 66s 44ms/step - loss: 0.1626 - accuracy: 0.9400 - val_loss: 0.2270 - val_accuracy: 0.9186
Epoch 11/20
1500/1500 [==============================] - 69s 46ms/step - loss: 0.1528 - accuracy: 0.9423 - val_loss: 0.2305 - val_accuracy: 0.9209
# 조기 종료가 잘 이루어졌는지 확인
import matplotlib.pyplot as plt
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 5s 15ms/step - loss: 0.2225 - accuracy: 0.9212
[0.22250305116176605, 0.9211666584014893]
plt.imshow(val_scaled[0].reshape(28, 28), cmap='gray_r')
plt.show()
preds = model.predict(val_scaled[0:1])
print(preds)
# 아홉 번째 값이 1이고 다른 값은 거의 0에 가깝다.
[[6.0322747e-20 4.3387573e-27 1.0130145e-25 1.6237968e-24 2.5624212e-22
9.1326881e-16 4.9221174e-22 5.1596729e-22 1.0000000e+00 2.9412100e-19]]
classes = ['티셔츠', '바지', '스웨터', '드레스', '코트',
'샌달', '셔츠', '스니커즈', '가방', '앵클 부츠']
import numpy as np
print(classes[np.argmax(preds)])
# 가방이라고 예측한다.
가방
test_scaled = test_input.reshape(-1, 28, 28, 1) / 255.0
model.evaluate(test_scaled, test_target)
# 테스트 세트에서의 점수는 검증 세트보다 조금 작다.
313/313 [==============================] - 5s 15ms/step - loss: 0.2555 - accuracy: 0.9140 0s - loss: 0.2546 - accuracy: 0.91
[0.25554993748664856, 0.9139999747276306]
08-3 합성곱 신경망의 시각화¶
# 2절에서 만든 모델이 어떤 가중치를 학습했는지 확인하기 위한 체크포인트 파일 읽기
import wget
url= 'https://github.com/rickiepark/hg-mldl/raw/master/best-cnn-model.h5'
wget.download(url)
100% [..........................................................................] 4044200 / 4044200
'best-cnn-model.h5'
model = keras.models.load_model('best-cnn-model.h5')
model.layers
[<keras.layers.convolutional.Conv2D at 0x25d6425b8e0>,
<keras.layers.pooling.MaxPooling2D at 0x25d63a86670>,
<keras.layers.convolutional.Conv2D at 0x25d64012190>,
<keras.layers.pooling.MaxPooling2D at 0x25d64012eb0>,
<keras.layers.core.Flatten at 0x25d64021fa0>,
<keras.layers.core.Dense at 0x25d64012340>,
<keras.layers.core.Dropout at 0x25d6403ea00>,
<keras.layers.core.Dense at 0x25d6403a490>]
# 첫 번째 합성곱 층의 가중치 조사
# 첫 번째 원소(가중치)와 두번째 원소(절편)의 크기 출력
conv = model.layers[0]
print(conv.weights[0].shape, conv.weights[1].shape)
(3, 3, 1, 32) (32,)
# 가중치 배열의 평균과 표준편차
conv_weights = conv.weights[0].numpy()
print(conv_weights.mean(), conv_weights.std())
-0.03802128 0.27455312
# 가중치 분포 확인
import matplotlib.pyplot as plt
plt.hist(conv_weights.reshape(-1, 1)) # 히스토그램을 그리기 위해 1차원 배열 전달 코드
plt.xlabel('weight')
plt.ylabel('count')
plt.show()
# 가중치가 적용된 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()
# 훈련하지 않은 빈 합성곱 신경망 생성
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()
print(no_training_weights.mean(), no_training_weights.std())
# 평균은 비슷하나 표준편차는 이전과 달리 매우 작다.
-0.001660773 0.084361695
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()
# 전체적으로 가중치가 밋밋하게 초기화되었다.
함수형 API¶
☆ 입력이 2개 또는 출력이 2개인 좀 더 복잡한 모델인 경우 Sequential 클래스를 사용하기 어려워 함수형 API를 사용한다.
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'")
print(model.layers[0].output)
KerasTensor(type_spec=TensorSpec(shape=(None, 28, 28, 32), dtype=tf.float32, name=None), name='conv2d/Relu:0', description="created by layer 'conv2d'")
# 함수형 API를 활용한 확인
conv_acti = keras.Model(model.input,model.layers[0].output)
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
# 데이터 전처리
inputs = train_input[0:1].reshape(-1, 28, 28, 1)/255.0
feature_maps = conv_acti.predict(inputs)
# feature_maps의 크기 확인
print(feature_maps.shape)
(1, 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()
# 두 번째 합성곱 층이 만든 특성
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)
(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()
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:80% !important;}</style>"))
'Book report > 혼자 공부하는 머신러닝 + 딥러닝' 카테고리의 다른 글
[혼자 공부하는 머신러닝 + 딥러닝] Chapter 9 텍스트를 위한 인공 신경망 (0) | 2021.09.29 |
---|---|
[혼자 공부하는 머신러닝 + 딥러닝] Chapter 7 딥러닝을 시작합니다 (0) | 2021.09.04 |
[혼자 공부하는 머신러닝 + 딥러닝] Chapter 6 비지도 학습 (0) | 2021.09.03 |
[혼자 공부하는 머신러닝 + 딥러닝] Chapter 5 트리 알고리즘 (0) | 2021.09.01 |
[혼자 공부하는 머신러닝 + 딥러닝] Chapter 4 다양한 분류 알고리즘 (0) | 2021.08.31 |