1. 신경망 학습 알고리즘 구현
신경망에는 적응 가능한 가중치와 편향이 있고, 이 가중치와 편향을 training data에 적응하도록 조정하는 과정을 '학습'이라 한다. 신경망의 학습은 4가지 단계로 이루어진다.
1단계 - 미니배치
training data 중 일부를 random으로 가져온다. 선별된 데이터를 미니배치라 하며, 미니배치의 손실 함수 값을 줄이는 것이 목표이다.
2단계 - 기울기 산출
미니배치의 손실 함수 값을 줄이기 위해 손실 함수의 값을 가장 작게하는 방향을 제시해주는 각 가중치 매개변수의 기울기를 구한다.
3단계 - 매개변수 갱신
가중치 매개변수를 기울기 방향으로 갱신한다.
4단계 - 반복
1~3단계를 반복한다.
신경망의 학습은 경사 하강법으로 매개변수를 갱신하는 과정이다. 하지만 매개변수를 한 번 갱신하기 위해 모든 데이터를 다 입력해야 하는데, 이는 엄청난 시간이 걸린다. 때문에 전체 데이터셋 중에서 일부를 랜덤으로 선별하여 학습하는 것을 반복하는 방법을 고안하였는데 이를 이를 확률적 경사 하강법(Stochastic Gradient Descent, SGD)이라 한다. 확률적으로 무작위로 골라낸 데이터에 대해 경사 하강법을 수행한다는 의미이다. 전체 데이터셋을 랜덤으로 선별한 데이터를 미니배치라 한다.
아래 그림에서 파란색은 모든 데이터를 입력하는 즉 full-batch로 학습하는 Gradient Descent(GD)이고, 보라색은 데이터를 나누어 입력하는 Stochastic Gradient Descent(SGD)에 해당하나 데이터를 1개씩 입력한다. 초록색은 mini-batch를 통해 적절한 데이터 개수를 랜덤으로 선별하여 입력하는 Stochastic Gradient Descent(SGD)이다. 화살표 오른쪽 그림의 경사 하강법을 통해 매개변수를 갱신해나가는 과정을 보면 full-batch를 이용한 GD(파란색)는 헤매지 않고 최적의 방법으로 ★best로 찾아가지만 mini-batch를 이용한 SGD(초록색)는 구불구불하게 약간 헤매면서 찾아간다. 그러나 속도는 SGD(초록색)가 GD(파란색)보다 훨씬 빠르다. 이를 통해 SGD는 최적의 방법으로 학습하진 않지만 GD의 속도 문제를 해결한 방법임을 알 수 있다.
※참고로 미니배치와 관련된 내용은 아래 링크에 있다.
<2-layer 신경망 클래스 만들기>
import sys, os sys.path.append(os.pardir) from common.functions import * from common.gradient import numerical_gradient class TwoLayerNet: def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01): self.params = {} self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) self.params['b2'] = np.zeros(output_size) def predict(self, x): W1, W2 = self.params['W1'], self.params['W2'] b1, b2 = self.params['b1'], self.params['b2'] a1 = np.dot(x, W1) + b1 z1 = np.dot(a1) a2 = np.dot(z1, W2) + b2 y = softmax(a2) return y def loss(self, x, t): #x: 입력데이터, t:정답 레이블 y = self.predict(x) return cross_entropy_error(y, t) def accuracy(self, x, t): y = self.predict(x) y = np.argmax(y, axis=1) t = np.argmax(t, axis=1) accuracy = np.sum(y == t) / float(x.shape[0]) return accuracy def numerical_gradient(self, x, t): loss_W = lambda W:self.loss(x, t) grads ={} grads['W1'] = numerical_gradient(loss_W, self.params['W1']) grads['b1'] = numerical_gradient(loss_W, self.params['b1']) grads['W2'] = numerical_gradient(loss_W, self.params['W2']) grads['b2'] = numerical_gradient(loss_W, self.params['b2']) return grads
<위에서 만든 신경망으로 mnist데이터셋 미니배치 학습하기>
import numpy as np from dataset.mnist import load_mnist from two_layer_net import TwoLayerNet (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 network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10) for i in range(iters_num): #미니배치 획득 batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] #기울기 계산 grad = network.numerical_gradient(x_batch, t_batch) #grad = network.gradient(x_batch, t_batch) #성능 개선판 #매개변수 갱신 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)
에폭(epoch)이란 일종의 단위이다. 1epoch이란 전체 훈련 데이터셋에 대해 forward pass와 backward pass 과정을 모두 포함하여 전체적으로 한 번 학습했을 때를 말한다. 예를 들어 전체 훈련 데이터셋에 대해 50번 학습을 하면 epochs=50이 된다. 이때 입력 데이터로 모든 데이터를 한 번에 넣어 학습시키면 시간이 굉장히 오래걸리므로, 전체 데이터를 임의로 나누어서 여러번 학습할 수 있도록 한다. 이렇게 나누어진 데이터를 미니배치라 하며, 나누어진 데이터 뭉탱이들의 개수(=데이터를 나누는 횟수)를 iteration이라 한다. 즉 1epoch을 진행하기 위해 필요한 데이터 뭉탱이(미니배치)들의 개수를 말한다. 각 iteration마다 입력 데이터로 들어가는 미니배치에 포함된 데이터들의 개수를 batch size라 한다. epoch을 너무 작게 설정하면 underfitting이 될 수도 있고, 너무 크게 설정하면 overfitting문제가 발생할 수 있으므로 최적화를 위해서는 적절한 epoch을 설정해야 한다.
예를 들어 훈련 데이터 수 = 1000, epochs = 10, batch_size = 200를 생각해보자.
전체 훈련 데이터를 batch_size인 200개씩으로 나누면 총 5개의 데이터 뭉탱이가 생기므로 iteration=5가 된다.
따라서 1000개의 데이터를 200개로 나누어 5번 학습하는 것을 총 10번(=10 epochs) 반복한다.
<test data로 accuracy평가하기>
import numpy as np from dataset.mnist import load_mnist from two_layer_net import TwoLayerNet (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_labe=True) network = TwoLayerNet(input_size=784, hidden_size=50, output=10) #hyperparameter iters_num = 10000 train_size = x_train.shape[0] batch_size = 100 learning_rate = 0.1 train_loss_list = [] train_acc_list = [] test_acc_list = [] #1epoch당 반복 수 iter_per_epoch = max(train_size / batch_size, 1) for i in range(iters_num): #미니배치 획득 batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] #기울기 계산 grad = network.numerical_gradient(x_batch, t_batch) #grad = network.gradient(x_batch, t_batch) #매개변수 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) #1epoch당 정확도 계산 if i % iter_per_epoch == 0: train_acc = network.accuracy(x_train, t_train) test_acc = network.accuracy(x_test, t_test) train_acc_list.append(train_acc) test_acc_list.append(test_acc) print("train acc, test acc :" +str(train_acc) + ", " +str(test_acc))
출처: 사이토 고키, 『밑바닥부터 시작하는 딥러닝』, 한빛미디어(2017), p136-146.
'AI > 밑딥' 카테고리의 다른 글
딥러닝) 활성화 함수 ReLU 계층 , Sigmoid 계층 구현 (0) | 2021.03.15 |
---|---|
딥러닝) 오차 역전파 backpropagation , 연쇄법칙 chain rule , 기울기 효율적으로 구하기 (0) | 2021.03.08 |
딥러닝) 기울기 , 경사하강법 , gradient descent , learning rate (0) | 2021.02.27 |
딥러닝) 수치 미분 , 해석적 미분 , 편미분 (0) | 2021.02.25 |
딥러닝) 신경망 학습 , 손실 함수 ( 오차 제곱합 , 교차 엔트로피 오차 ), 미니배치 (0) | 2021.02.21 |
댓글