배치 정규화는 신경망의 깊이가 깊을수록 원래 의도했던 데이터 분포가 달라지는 것을 막아주는 효과가 있습니다.
네트워크의 깊이가 깊어 질수록 레이어 통과할때마다 데이터 분포가 치우치는 현상이 더 많이 발생하며, 배치 정규화를 사용했을 때 일반적으로 성능이 좋아집니다.
최근에 개발되는 네트워크에는 대부분 배치 정규화를 사용하고 있습니다.
주어진 모델은 LeNet의 신경망 깊이를 늘린 변형 모델입니다.
이 변형된 LeNet에 배치 정규화를 추가해보고 어떤 효과가 있는지 확인해봅시다.
배치 정규화
BN이라고도 불리며 Keras에서 다음과 같이 레이어를 사용할 수 있습니다.
layers.BatchNormalization()
Copy
지시사항
앞서 구현한 LeNet에 아래의 구조와 같은 배치 정규화를 추가하세요. 그리고 test_loss와 test_acc를 확인하세요.
image
Tips!
train_cnt와 test_cnt가 동일한 상태에서 배치 정규화를 추가하기 전과 후의 test_loss와 test_acc를 비교해보세요.
train_cnt, test_cnt = 50000, 10000 일 때 정상적으로 정답 처리가 되는 것에 유의하세요.
import os
import cv2
import numpy
# Fix seed
import tensorflow as tf
tf.random.set_seed(1)
import numpy as np
np.random.seed(1)
from tensorflow.keras import datasets, layers, models, activations, losses, optimizers, metrics
from tensorflow.keras import utils
# mnist 데이터 셋을 로드합니다.
# 각각 학습셋(이미지, 라벨), 테스트 셋(이미지, 라벨)으로 구성이 되어 있습니다.
data_path = os.path.abspath("./mnist.npz")
(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data(path=data_path)
train_cnt, test_cnt = 50000, 10000
train_images, train_labels = train_images[:train_cnt], train_labels[:train_cnt]
test_images, test_labels = test_images[:test_cnt], test_labels[:test_cnt]
# 학습 셋은 60000개의 28x28 이진 이미지이므로 reshaping을 해줍니다.
train_images = train_images.reshape((train_cnt, 28, 28, 1))
# 테스트 셋은 10000개의 28x28 이진 이미지이므로 reshaping을 해줍니다.
test_images = test_images.reshape((test_cnt, 28, 28, 1))
# LeNet의 입력은 32x32 이미지 입니다. 패딩을 주어서 28 x 28에서 32 x 32 이미지로 만듭니다.
train_images = numpy.pad(train_images, [[0, 0], [2,2], [2,2], [0,0]], 'constant')
test_images = numpy.pad(test_images, [[0, 0], [2,2], [2,2], [0,0]], 'constant')
print('train_images :', train_images.shape, type(train_images))
print('test_images :', test_images.shape, type(test_images))
# 픽셀 값을 0~1 사이로 정규화합니다.
train_images, test_images = train_images / 255.0, test_images / 255.0
# 모델을 구조를 선언합니다.
model=models.Sequential()
model.add(layers.Conv2D(6,(5,5),strides=(1,1),activation='tanh'))
model.add(layers.BatchNormalization())
model.add(layers.AveragePooling2D((2,2),strides=(2,2)))
model.add(layers.Conv2D(16,(5,5),strides=(1,1),activation='tanh'))
model.add(layers.BatchNormalization())
model.add(layers.AveragePooling2D((2,2),strides=(2,2)))
model.add(layers.Conv2D(120,(5,5),strides=(1,1),activation='tanh'))
model.add(layers.BatchNormalization())
model.add(layers.Flatten())
model.add(layers.Dense(84,activation='tanh'))
model.add(layers.Dense(10,activation='softmax'))
# 모델을 컴파일 합니다.
model.compile(loss=losses.sparse_categorical_crossentropy,
optimizer=optimizers.Adam(),
metrics=[metrics.categorical_accuracy])
# 모델을 학습합니다.
model.fit(train_images, train_labels, epochs=1)
test_loss, test_acc = model.evaluate(test_images, test_labels)
# 모델에 테스트 이미지를 넣고 예측값을 확인해봅니다.
test_img = cv2.imread("2.png", cv2.IMREAD_GRAYSCALE)
# 입력 이미지의 픽셀을 0~1 사이로 정규화 합니다.
test_img = test_img / 255.0
row, col, channel = test_img.shape[0], test_img.shape[1], 1
confidence = model.predict(test_img.reshape((1, row, col, channel)))
for i in range(confidence.shape[1]):
print(f"{i} 일 확률 = {confidence[0][i]}")
# 학습 결과를 출력합니다.아래 내용을 수정하면 채점이 되지 않습니다.
print(numpy.argmax(confidence, axis=1), round(test_loss, 2), round(test_acc, 2))
shuffle은 데이터셋을 랜덤으로 섞는 것을 의미합니다.
데이터 순서가 학습에 영항을 끼치지 않도록 데이터 순서를 섞어야 합니다. 매번 epoch 마다 데이
터의 순서가 같다면 순서가 학습에 영향을 끼칠 수 있습니다.
데이터가 1반 2반 3반 나눠있다고 가정하고,
입력노드가 책상이라고 가정하면,
각 반에 1번학생이 앉는다. 에폭이 계속 진행되어도 자리가 계속 같다면 데이터가 특정방향으로 과적합이 발생한다.
0~9 까지의 숫자 데이터가 100개씩 10종류가 있을 때, batch를 10으로 하면 첫 번째 batch에서는 0만 100개, 두 번째 batch에서는 1만 100개 학습하게 됩니다. 이렇게 학습하는 것보다 데이터를 잘 섞어서(shuffle) 학습했을 때 더 높은 성능(낮은 loss)을 얻을 수 있습니다.
Keras에서 shuffle은 fit()함수의 shuffle=옵션으로 기능을 활성화 할 수 있습니다.
shuffle=True가 되면 Epoch마다 데이터가 랜덤하게 섞이게 되고, shuffle=False가 되면 데이터가 섞이지 않습니다.
두 가지 옵션을 줘서 각각의 fit()을 해보고 loss를 비교하는 실습을 해봅니다. 코드의 빈 부분을 채우고, 결과를 확인해 보세요.
지시사항
results_no_shuffle 변수에 shuffle을 하지 않은 모델을 학습하여 저장하세요.
results_shuffle 변수에 shuffle을 한 모델을 학습하여 저장하세요.
import os
import cv2
import numpy
import matplotlib.pyplot as plt
# Fix seed
import tensorflow as tf
tf.random.set_seed(1)
import numpy as np
np.random.seed(1)
from tensorflow.keras import datasets, layers, models, activations, losses, optimizers, metrics
from tensorflow.keras import utils
# mnist 데이터 셋을 로드합니다.
# 각각 학습셋(이미지, 라벨), 테스트 셋(이미지, 라벨)으로 구성이 되어 있습니다.
data_path = os.path.abspath("./mnist.npz")
(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data(path=data_path)
train_cnt, test_cnt = 5000, 1000
train_images, train_labels = train_images[:train_cnt], train_labels[:train_cnt]
test_images, test_labels = test_images[:test_cnt], test_labels[:test_cnt]
# 학습 셋은 60000개의 28x28 이진 이미지이므로 reshaping을 해줍니다.
train_images = train_images.reshape((train_cnt, 28, 28, 1))
# 테스트 셋은 10000개의 28x28 이진 이미지이므로 reshaping을 해줍니다.
test_images = test_images.reshape((test_cnt, 28, 28, 1))
# LeNet의 입력은 32x32 이미지 입니다. 패딩을 주어서 28 x 28에서 32 x 32 이미지로 만듭니다.
train_images = numpy.pad(train_images, [[0, 0], [2,2], [2,2], [0,0]], 'constant')
test_images = numpy.pad(test_images, [[0, 0], [2,2], [2,2], [0,0]], 'constant')
print('train_images :', train_images.shape, type(train_images))
print('test_images :', test_images.shape, type(test_images))
# 픽셀 값을 0~1 사이로 정규화합니다.
train_images, test_images = train_images / 255.0, test_images / 255.0
# 모델을 구조를 선언합니다.
model = models.Sequential()
model.add(layers.Conv2D(6, kernel_size=(5, 5), strides=(1, 1), activation='tanh', input_shape=(32, 32, 1)))
model.add(layers.BatchNormalization())
model.add(layers.AveragePooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(layers.Conv2D(16, kernel_size=(5, 5), strides=(1, 1), activation='tanh'))
model.add(layers.BatchNormalization())
model.add(layers.AveragePooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(layers.Conv2D(120, kernel_size=(1, 1), strides=(5, 5), activation='tanh'))
model.add(layers.BatchNormalization())
model.add(layers.Flatten())
model.add(layers.Dense(84, activation='tanh'))
model.add(layers.Dense(10, activation='softmax'))
# 모델을 컴파일합니다.
model.compile(loss=losses.sparse_categorical_crossentropy,
optimizer=optimizers.Adam(),
metrics=[metrics.categorical_accuracy])
# 모델을 학습하는 코드를 작성하세요. (shuffle을 하지 않습니다.)
results_no_shuffle = model.fit(train_images,train_labels,epochs=5,shuffle=False)
no_shuffle_test_loss, no_shuffle_test_acc = model.evaluate(test_images, test_labels)
model.compile(loss=losses.sparse_categorical_crossentropy,
optimizer=optimizers.Adam(),
metrics=[metrics.categorical_accuracy])
# 모델을 학습하는 코드를 작성하세요. (shuffle을 사용해 봅니다.)
results_shuffle = model.fit(train_images,train_labels,epochs=5,shuffle=True)
shuffle_test_loss, shuffle_test_acc = model.evaluate(test_images, test_labels)
# 코드 작성 후 epoch별 loss를 비교한 결과를 확인해보세요.
print('No Shuffle loss :', [round(x, 3) for x in results_no_shuffle.history['loss']])
print(' Shuffle loss :', [round(x, 3) for x in results_shuffle.history['loss']])
print(results_no_shuffle.history['loss'][-1], results_shuffle.history['loss'][-1])