Story of CowHacker

딥러닝 4.5 오차역전파법 본문

공부/딥러닝

딥러닝 4.5 오차역전파법

Cow_Hacker 2020. 8. 19. 10:42
728x90

다시 한번 신경망 학습의 절차를 보겠다.

 

전제

 

신경망에는 가중치편향이 있고, 이 가중치와 편향을 훈련 데이터에 적응하도록 조정하는 과정을 학습이라고 한다.

 

1단계 - 미니 배치

훈련 데이터 중 일부를 무작위로 가져온다. 손실 함숫값을 줄이는 것이 목표다.

 

2단계 - 기울기 산출

미니 배치의 손실 함숫값을 줄이기 위해 각 가중치 매개변수의 기울기를 구한다.

 

3단계- 매개변수 갱신

가중치 매개변수를 기울기 방향으로 아주 조금 갱신한다.

 

4단계 - 반복

1 ~ 3 단계를 반복한다.

 

지금까지 알아온 오차 역전파법은 2단계 기울기 산출에 속한다.

 

 

이제 오차 역전파법을 본격적으로 구현할 차례다.

 

먼저 2층 신경망에서 클래스의 인스턴스 변수와 메서드를 알아보겠다.

 

 

그림1
그림2

 

이제 그림 1과 그림 2를 참고해 클래스 코드를 보겠다.

 

 

 

오차 역전파법 클래스 코드

import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
from common.layers import *
from common.gradient import numerical_gradient
from collections import OrderedDict


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)

        # 계층 생성
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.lastLayer = SoftmaxWithLoss()
        
    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        
        return x
        
    # x : 입력 데이터, t : 정답 레이블
    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)
    
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1 : t = np.argmax(t, axis=1)
        
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
        
    # x : 입력 데이터, t : 정답 레이블
    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
        
    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 결과 저장
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads

 

지금까지 알아본 Affine, Relu, 순 전파, 역전파 들이 쓰인 것을 볼 수 있다.

 

 

 

이제 오차 역전파법으로 구한 기울기를 검증하는 코드를 보겠다.

 

 

 

 

오차 역전파법 기울기 검증 코드

 

import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
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)

x_batch = x_train[:3]
t_batch = t_train[:3]

grad_numerical = network.numerical_gradient(x_batch, t_batch)
grad_backprop = network.gradient(x_batch, t_batch)

# 각 가중치의 절대 오차의 평균을 구한다.
for key in grad_numerical.keys():
    diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )
    print(key + ":" + str(diff))

 

 

앞에서 배운 수치 미분이랑은 확연히 속도가 빨라진 걸 알 수 있다.

 

수치 미분은 느린 대신 구현하기가 쉽다는 장점이 있다.

 

 

 

 

이제 마지막으로 이번에 배운 오차 역전파법을 통해 신경망 학습을 구현해보겠다.

 

오차 역전파법을 이용한 신경망 학습 코드

 

import sys, os
sys.path.append(os.pardir)

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) # 수치 미분 방식
    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)
    
    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)

 

 

앞에서 설명한 신경망 학습 코드에서 오차 역전파법으로 구한다는 점만 다르다.

 

 

 

결론

1. 계산 그래프를 이용하면 계산 과정을 시각적으로 파악할 수 있다.

2. 계산 그래프의 노드는 국소적 계산으로 구성된다.

3. 계산 그래프의 순 전파는 통상의 계산을 수행한다. 역전 파는 각 노드의 미분을 구할 수 있다.

4. 신경망의 구성 요소를 계층으로 구현하여 기울기를 효율적으로 계산할 수 있다.

5. 수치 미분을 통해 오차 역전파법의 구현에 잘못이 없는지 확인할 수 있다.

728x90

'공부 > 딥러닝' 카테고리의 다른 글

딥러닝 5.1 학습 관련 기술들  (0) 2020.08.19
딥러닝 5.0 학습 관련 기술들  (0) 2020.08.19
딥러닝 4.4 오차역전파법  (0) 2020.08.19
딥러닝 4.3 오차역전파법  (0) 2020.08.18
딥러닝 4.2 오차역전파법  (0) 2020.08.17
Comments