[AI] 3. 신경망의 미니배치 학습 구현하기
MNIST데이터셋을 활용하여 신경망(Neural Network)의 미니배치 학습을 구현해보자.
신경망(Neural Network)은 학습을 통해 최적의 가중치(Weight)와 편향(Bias)을 찾아내야 한다. 손실 함수(Loss Function)가 최솟값일 때 가중치와 편향이 최적의 값을 갖는다.
MNIST 데이터셋
MNIST(Modified National Institute of Standards and Technology)는 머신러닝 및 딥러닝 분야에서 알고리즘의 성능을 시험하기 위해 사용하는 표준 손글씨 숫자 데이터셋이다.
총 70,000장 (학습용 60,000장, 테스트용 10,000장)으로 구성되어있다.
테스트용으로는 학습을 하지 않는다. 이는 과적합(Overfitting)이 일어나는지 확인하기 위함이다.
과적합(Overfitting)
과적합(Overfitting)은 학습 데이터에 대해 너무 과도하게 학습되어 새로운 데이터에 대한 예측 능력이 떨어지는 현상이다. 과도하게 훈련할 경우, 훈련 데이터의 노이즈나 세부적인 규칙까지 외워버리기 때문이다.
MNIST의 손글씨 데이터를 출력해보자. 출력코드와 이미지는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
# 데이터 로드
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
# 확인할 이미지의 인덱스 선택
index = 0
img = x_train[index]
label = t_train[index]
# 데이터 형태 변환 (1차원 784배열 -> 28x28 2차원 배열)
img = img.reshape(28, 28)
# 이미지 시각화
plt.imshow(img, cmap='gray')
plt.title(f"Label: {np.argmax(label)}")
plt.savefig('my_graph.png')
위 이미지처럼 70000개의 손글씨 데이터를 이용하여 신경망의 미니배치 학습을 구현할 것이다.
학습 알고리즘
신경망의 학습은 기본적으로 다음과 같이 진행된다.
미니배치
훈련 데이터 중 무작위로 일부를 가져온다.기울기(gradient)산출
미니배치의 손실함수 값을 줄이기 위해 매개변수의 기울기를 구한다.매개변수갱신
매개변수를 기울기 방향으로 학습률만큼 갱신시킨다.- 반복 위 1~3단계를 반복한다.
미니배치 학습 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import sys, os
sys.path.append(os.path.abspath("...
sys.path.append(os.path.abspath("...
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
#MNIST에서 데이터를 읽어온다.
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
train_loss_list = []
#hyperparameter
iters_num = 10000 #반복횟수
train_size = x_train.shape[0]
batch_size = 100 #미니배치 크기
learning_rate = 0.1 #학습률
#레이어가 2개인 퍼셉트론
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
#4단계. 정해둔 반복횟수 만큼 반복
for i in range(iters_num):
#1단계. 미니배치
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
#2단계. 기울기 산출
grad = network.numerical_gradient(x_batch, t_batch)
#3단계. 매개변수 갱신
for key in ('W1', 'b1', 'W2', 'b2')
network.params[key] -= learning_rate * grad[key]
#학습 경과
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
x = np.arange(len(train_loss_list))
plt.plot(x, train_loss_list)
plt.xlabel("iteration")
plt.ylabel("loss")
plt.xlim(0, 10000)
plt.ylim(0, 9)
plt.show()
plt.savefig('graph.png')
위 그래프는 신경망의 손실 함수의 그래프이다. 값이 낮아질 수록 출력값과 정답 레이블과의 차이가 줄어드는 것이므로 학습이 잘 되고있다는 뜻이다.
왼쪽 그래프는 오른쪽 그래프에서의 가중치의 0.01배의 가중치로 설정해서 구현해보았다.
왼쪽 그래프는 학습 초기 단계에서 손실 함수 값이 바로 떨어지지 않고 일정하게 유지되다가 어느 지점에서 급격히 하락했다. 왜그랬을까?
이는 가중치의 초기값이 너무 작은 값인 것이 원인이었다.
초반에 가중치가 너무 작으면 전달되는 기울기도 작아진다. 즉, 가중치 업데이트가 거의 일어나지 않거나 미세하게만 일어나기 때문에 손실 함수가 정체되어 있는 것처럼 보인다.
그러다가 가중치가 학습을 통해 어느정도 커지면 기울기 전달이 원활해지면서 손실 함수가 떨어진다.
활성화 함수로는 시그모이드(sigmoid)함수를 이용했다.
시그모이드 함수는 포화 구간(양 끝쪽)에서 기울기가 0에 가까워진다. 미분값이 0이면 가중치를 아무리 업데이트하려고 해도 수치가 변하지 않고 기울기 소실(Gradient Vanishing)이 일어난다.
이처럼 입력에 반응하지 않고 0이나 1같은 특정한 값만 출력하는 상태를 포화(Saturation)라고 한다. ReLU와 같은 활성화 함수를 이용하면 포화 현상이 발생하지 않는다.

