Deep learning/모델 구현

6. 신경망 학습 (3) - 학습 알고리즘 구현하기

jwjwvison 2021. 4. 20. 18:01

이 포스팅은 밑바닥부터 시작하는 딥러닝을 공부하고 정리한것 입니다.


학습 알고리즘 구현하기

 신경망 학습의 절차는 다음과 같다.

 이는 경사 하강법으로 매개변수를 갱신하는 방법이며, 이때 데이터를 미니배치로 무작위로 선정하기 때문에 확률적 경사 하강법(stochastic gradient descent, SGD) 라고 부른다. 그럼 실제로 손글씨 숫자를 학습하는 신경망을 구현해보자. 여기에서는 2층 신경망(은닉층이 1개인 네트워크)를 대상으로 MNIST 데이터셋을 사용하여 학습을 수행한다.

 

 1. 2층 신경망 클래스 구현하기

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=sigmoid(a1)
        a2=np.dot(z1,W2)+b2
        y=softmax(a2)
        
        return y
    
    def loss(self,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
net=TwoLayerNet(input_size=784,hidden_size=100,output_size=10)
print(net.params['W1'].shape)
print(net.params['b1'].shape)
print(net.params['W2'].shape)
print(net.params['b2'].shape)

 이와 같이 params 변수에는 이 신경망에 필요한 매개변수가 모두 저장된다.

x=np.random.rand(100,784)   #더미 입력 데이터(100장 분량)
y=net.predict(x)

 

 grads 변수에는 params 변수에 대응하는 각 매개변수의 기울기가 저장된다. 예를 들어 다음과 같이 numerical_gradient() 메서드를 사용해 기울기를 계산하면 grads 변수에 기울기 정보가 저장된다.

x=np.random.rand(100,784)           # 더미 입력 데이터 (100장 분량)
t=np.random.rand(100,10)            # 더미 정답 레이블 (100장 분량)
grads=net.numerical_gradient(x,t)

print(grads['W1'].shape)
print(grads['b1'].shape)
print(grads['W2'].shape)
print(grads['b2'].shape)

 

 

2. 미니배치 학습 구현하기

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=[]

#하이퍼파라미터
iters_nums=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_nums):
    # 미니배치 획득
    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.parmas[key] -= learning_rate * grad[key]
        
    #학습 경과 기록
    loss=network.loss(x_batch,t_batch)
    train_loss_list.append(loss)

 여기에서는 미니배치 크기를 100으로 했다. 그리고 그 100개의 미니배치를 대상으로 확률적 경사 하강법을 수행해 매개변수를 갱신한다. 경사법에 의한 갱신 횟수(반복 횟수)를 10,000번으로 설정하고, 갱신할 때마다 훈련 데이터에 대한 손실 함수를 계산하고 그 값을 배열에 추가한다.

 위 그림을 보면 횟수가 늘어가면서 손실 함수의 값이 줄어든다. 이는 학습이 잘 되고 있다는 뜻이다.

 

 

3. 시험 데이터로 평가하기

 위의 결과는 정확히는 '훈련 데이터의 미니배치에 대한 손실 함수'의 값이다. 훈련 데이터의 손실 함수 값이 작아지는 것은 신경망이 잘 학습하고 있다는 방증이지만, 이 결과만드로는 다른 데이터셋에도 비슷한 실력을 발휘할지는 확실하지 않다. 신경망 학습에서는 훈련 데이터 외의 데이터를 올바르게 인식하는지를 확인해야 한다. 다른 말로 '오버피팅'을 일으키지 않는지 확인해야 한다. 오버피팅 되었다는 것은, 예를 들어 훈련 데이터에 포함된 이미지만 제대로 구분하고, 그렇지 않은 이미지는 식별할 수 없다는 뜻이다.

 

 다음 구현에서는 학습 도중 정기적으로 훈련 데이터와 시험 데이터를 대상으로 정확도를 기록한다. 여기에서는 1에폭별로 훈련 데이터와 시험 데이터에 대한 정확도를 기록한다.

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)
network=TwoLayerNet(input_size=784,hidden_size=50,output_size=10)

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=[]

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)
    # 매개변수 갱신
    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)
    
    # 1에폭당 정확도 계산
    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))

 

결론


 이번 장에서는 신경망 학습에 대해서 공부했다. 가장 먼저 신경망이 학습을 수행할 수 있도록 손실 함수라는 지표를 도입했다. 이 손실 함수를 기준으로 그 값이 가장 작아지는 가중치 매개변수 값을 찾아내는 것이 신경망 학습의 목표이다. 또 가능한 한 작은 손실 함수의 값을 찾는 수법으로 경사법을 공부했다. 경사법은 함수의 기울기를 이용하는 방법이다.