수학/딥러닝 이론

04-4 오차역전파법 (4) - 오차역전파법을 사용한 학습 구현

AI 꿈나무 2020. 9. 12. 20:05
반응형

(밑바닥부터 시작하는 딥러닝, 사이토고키)를 바탕으로 제작하였습니다.


 

오차역전파법(4) - 오차역전파법을 사용한 학습 구현

 이전의 포스팅에서는 역전파를 적용한 활성화함수(ReLu, Sigmoid) 계층, Affine 계층, Softmax-with-Loss 계층을 구현해 보았습니다.

 이번 포스팅에서는 지금까지 구현한 계층을 조합해서 오차역전파법을 사용한 학습을 구현해보겠습니다.

 


 

7. 오차역전파법 구현하기

 

Artificial Neural Networks

 

 7.1 신경망 학습의 전체 그림

 우선 신경망 학습의 전체 그림을 복습해보겠습니다. 다음은 신경망 학습의 순서입니다.

 

전체
 신경망에는 적응 가능한 가중치와 편향이 있고, 이 가중치와 편향을 훈련 데이터에 적응하도록 조정하는 과정을 '학습'이라 합니다. 신경망 학습은 다음과 같이 4단계로 수행합니다.

1단계 - 미니배치
 훈련 데이터 중 일부를 무작위로 가져옵니다. 이렇게 선별한 데이터를 미니배치라 하며, 그 미니배치의 손실 함수 값을 줄이는 것이 목표입니다.

2단계 - 기울기 산출
 미니배치의 손실 함수 값을 줄이기 위해 각 가중치 매개변수의 기울기를 구합니다. 기울기는 손실 함수의 값을 가장 작게하는 방향을 제시합니다.

3단계 - 매개변수 갱신
 가중치 매개변수를 기울기 방향으로 아주 조금 갱신합니다.

4단계 - 반복
 1~3단계를 반복합니다.

 

 오차역전파법이 등장하는 단계는 두 번째인 '기울기 산출'입니다. 이전에서는 이 기울기를 구하기 위해서 수치 미분을 사용했습니다. 하지만 수치 미분은 구현하기는 쉽지만 계산이 오래 걸렸습니다. 오차역전파법을 이용하면 느린 수치 미분과 달리 기울기를 효율적이고 빠르게 구할 수 있습니다.

 

 

 7.2 오차역전파법을 적용한 신경망 구현하기

 

 2층 신경망을 TwoLayerNet 클래스로 구현해보겠습니다.

class TwoLayerNet:

    # 초기화 수행, (입력층 뉴런, 은칙층 뉴런, 출력층 뉴런, 가중치 초기화시 정규분포 스케일)
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
    
        # 가중치 초기화
        # parmas : 딕셔너리 변수로 신경망의 매개변수를 보관
        self.params = {}
        self.params['W1'] = weight_init_std * \
                           np.random.randn(input_size, hidden_size)
        self.parmas['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)
    
        # 계층 생성
        # layers : 순서가 있는 딕셔너리 변수로, 신경망의 계층을 보관
        #          각 계층을 순서대로 유지
        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.parmas['b2']
    
        # 마지막 계층
        self.LastLayer = SoftmaxWithLosee()
        
        
    # 예측(추론)을 수행한다.
    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):
    
        #순전파
        self.loss(x,t)
        
        # 역전파
        dout = 1
        dout = self.lastLayer.backward(dout)
        
        layers = lisr(self.layers.valus())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)
            
        # 결과 저장
        grads = {}
        grads['W1'] = self.layers['Affine1'].dW
        grads['b1'] = self.layers['Affine1'].db
        grads['W2'] = self.layers['Affine2'].dW
        grads['b1'] = self.layers['Affine1'].db
        
        return grads

 

 이어서 각 계층 내부에 구현된 순전파와 역전파를 활용해 인식 처리와 학습에 필요한 기울기를 구해보겠습니다.

 

 

 7.3 오차역전파법으로 구한 기울기 검증하기

 수치 미분은 오차역전파법을 정확히 구현했는지 확인하기 위해 필요합니다. 수치 미분의 이점은 구현하기 쉽다는 것입니다. 오차역전파법은 구현하기 복잡해서 종종 실수를 하곤 합니다. 그래서 수치 미분의 결과와 오차역전파법의 결과를 비교하여 오차역전파법을 제대로 구현했는지 검증합니다. 이처럼 두 방식으로 구한 기울기가 일치함을 확인하는 작업을 기울기 확인(gradient check)라고 합니다.

 

 기울기 확인 구현

# 데이터 읽기
(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))
    
# 실행결과
W1:4.992444822296182e-10 
b1:2.7775308568029376e-09 
W2:6.1427267257827926e-09 
b2:1.4103044333468872e-07

차이가 매우 작은 것을 확인할 수 있습니다. (오차역전파법 제대로 구현)

 

 

 7.4 오차역전파법을 사용한 학습 구현하기

 

 마지막으로 오차역전파법을 사용한 신경망 학습을 구현해보겠습니다. 이전에 배웠던 신경망학습과 다른 부분은 기울기를 오차역전파법으로 구한다는 점입니다.

 

 오차역전파법을 사용한 신경망 학습 구현

# 데이터 읽기
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

# 2층 신경망 생성
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)

 


 

8. 정리

 04. 오차역전파법에서는 계산 과정을 시각적으로 보여주는 방법인 계산 그래프를 배웠습니다.

 계산 그래프를 이용하여 신경망의 동작과 오차역전파법을 설명하고, 그 처리 과정을 계층이라는 단위로 구현했습니다. 예를 들어 ReLU 계층, Softmax-with-Loss 계층, Affine 계층, Softmax 계층 등 입니다. 모든 계층에서 forwardbackward라는 메서드를 구현했습니다. 이처럼 동작을 계층으로 모듈화한 덕분에, 신경망의 계층을 자유롭게 조합하여 원하는 신경망을 쉽게 만들 수 있습니다.

 

04 오차역전파법 에서 배운 내용

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

2. 계산 그래프의 노드는 국소적 계산으로 구성된다. 국소적 계산을 조합해 전체 계산을 구성한다.

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

4. 신경망의 구성 요소를 계층으로 구현하여 기울기를 효율적으로 계산할 수 있다. (오차역전파법)

5. 수치 미분과 오차역전파법의 결과를 비교하면 오차역전파법의 구현에 잘못이 없는지 있는지 확인할 수 있다. (기울기 확인)

 

 

반응형