본문 바로가기
밑바닥 딥러닝/밑바닥부터 시작하는 딥러닝1

5장

by sxlvxrjxms2s2itsmes2s2 2023. 6. 2.

5장

5.1 계산 그래프

5.2 국소적 계산
5.3 왜 계산 그래프로 푸는가?
5.4 연쇄법칙
5.5 역전파
5.6 단순한 계층 구현하기
5.7 활성화 함수 계층 구현하기
5.8 Affine/Softmax 계층 구현하기
5.9 오차역전파법 구현하기
5.10 정리

 

오차역전파법

: 인공 신경망에서 가중치와 편향을 업데이트하는데 사용되는 학습 알고리즘

오차역전파법을 제대로 이해하는 두 가지 방법

- 수식을 통한 것

- 계산 그래프를 통한 것

 

5.1 계산 그래프

계산 그래프는 계산 과정을 그래프로 나타낸 것.

그래프는 복수의 노드와 에지로 표현된다.

 

예시 문제 2 : 현빈 군은 슈퍼에서 사과를 2개, 귤을 3개 샀다. 사과는 1개 100원, 귤은 1개에 150원, 소비세 10%일때 지불금액은?

계산 그래프로 풀어본 문제 2의 답

계산 그래프를 이용한 문제풀이는 다음 흐름으로 진행됨

- 계산 그래프를 구성한다.

- 계산 그래프는 왼쪽에서 오른쪽으로 진행된다. (순전파)

- 순전파는 계산그래프의 출발점부터 종착점으로의 전파

- 역전파 : 종착점(오른쪽)에서 출발점(왼쪽)으로의 전파 (미분계산시 중요역할)

 

5.2 국소적 계산

  • 계산 그래프의 특징은 국소적 계산을 전파함으로써 최종 결과를 얻음
  • 국소적 = 자신과 직접 관계된 작은 범위
  • 국소적 계산 = 전체에서 어떤 일이 벌어지든 상관없이 자신과 관계된 정보만으로 결과를 출력할 수 있다는 것
  • 계산 그래프는 각 노드에서 국소적계산을 하므로 전체 계산이 아무리 복잡하더라도 단순하게 처리가능

각 노드에서의 계산은 국소적 계산임.

4000이라는 숫자가 어떻게 계산되었느냐와는 상관없이, 단지 두 숫자를 더하면 됨.

각 노드는 자신과 관련한 계산 외에는 아무것도 신경 쓸 게 없음.

 

국소적 계산은 단순하지만 그 결과를 전달함으로써 전체를 구성하는 복잡한 계산을 해낼 수 있다.

 

5.3 왜 계산 그래프로 푸는가?

이점1) 국소적 계산

: 전체가 아무리 복잡해도 각 노드에서는 단순한 계산에 집중하여 문제를 단순화할 수 있다

이점2) 중간계산결과 모두 보관 가능

이점3) 역전파를 통해 미분을 효율적으로 계산할 수 있음

 

문제 1을 예시로 한 설명

사과 가격이 오르면 최종 금액에 어떤 영향을 끼치는지 알고 싶을 때

= 사과 가격에 대한 지불 금액의 미분을 구하는 문제임. -> 역전파를 하면 구할 수 있다.

위의 그래프로 사과 가격에 대한 지불 금액의 미분 값은 2.2임을 알 수 있음.

- 사과가 1원 오르면 최종 금액은 2.2원 오른다는 의미

 

  • 소비세, 사과 개수에 대한 지불 금액의 미분도 같은 순서로 구할 수 있다.
  • 중간까지 구한 미분 결과를 공유할 수 있어 다수의 미분을 효율적으로 계산할 수 있다.
  • 즉 순전파와 역전파를 활용하여 각 변수의 미분을 효율적으로 구할 수 있다는 것이 이점이다.

 

5.4 연쇄법칙

국소적 미분을 전달하는 원리는 연쇄법칙에 따른 것

연쇄법칙은 계산그래프 상의 역전파와 같다.

 

1) 계산 그래프의 역전파

: 순방향과는 반대 방향으로 국소적 미분을 곱한다.

y=f(x)라는 계산의 역전파

- 역전파의 계산 절차는 신호 E에 노드의 국소적미분(ay/ax)을 곱한 후 다음노드로 전달

( 국소적 미분은 순전파 때의 y = f(x) 계산의 미분을 구하는 것 즉, x에 대한 y의 미분을 구한다는 뜻 )

- 역전파의 계산 순서 : 국소적인 미분을 상류에서 전달된 값(E)에 곱해 앞쪽 노드로 전달

이를 따르면 미분 값을 효율적으로 구할 수 있다는 것이 이 전파의 핵심. 

왜 이것이 가능한 지는 연쇄법칙의 원리로 설명 가능.

 

2) 연쇄법칙이란?

연쇄법칙은 합성 함수의 미분에 대한 성질이다.

합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있다. 라는 것이 연쇄법칙의 원리

 

역전파는 합성함수의 미분과 같음을 알 수 있고, 계산그래프의 역전파는 합성함수의 미분으로 구할 수 있다.

 

3) 연쇄법칙과 계산 그래프

 

- 마지막 결과를 보면 x에 대한 z의 미분이 된다.

- 즉 역전파가 하는 일은 연쇄법칙의 원리와 같다.

 

5.5 역전파

1) 덧셈 노드의 역전파

덧셈노드의 역전파는 입력 값을 그대로 흘려보낸다.

  • 상류에서 전해진 미분값이 aL/az인 이유는 최종적으로 L이라는 값을 출력하는 큰 계산 그래프를 가정하기 때문이다.

  • 위의 z = x + y 계산은 큰 계산 그래프의 중간 어딘가에 존재한다고 가정했기 때문에, 이 계산 그래프 상류에서부터 aL/az가 전해진 것이다. 그리고 다시 하류로는 aL/ax 와 aL/ay를 전달한다.

 

상류에서 1.3이 흘러나온다는 가정 하의 덧셈 노드 역전파의 예

 

2) 곱셈노드의 역전파

곱셈노드의 역전파는 상류의 값에 입력 신호들을 서로 바꾼 값을 곱해서 하류로 보냄.

덧셈과 달리 곱셈노드 구현 시에는 순전파의 입력 신호를 변수에 저장한다.

덧셈의 역전파에서는 상류의 값을 그대로 흘려보내 순방향 입력 신호의 값은 필요하지 않았지만 곱셈의 역전파는 순방향 입력 신호의 값이 필요하다.

그러므로 곱셈노드를 구현할 때는 순전파의 입력 신호를 변수에 저장해둔다.

상류에서 1.3이 흘러나온다는 가정 하의 곱셈 노드 역전파의 예

 

3) 사과쇼핑의 예

- 사과의 가격, 개수, 소비세라는 세 변수 각각이 최종 금액에 어떻게 영향을 주느냐를 풀고자 한다.

- 이는 세 변수 각각에 대한 미분을 구하는 것에 해당한다.

사과가격 미분 2.2 / 사과개수 미분 110 / 소비세 미분 200 만큼 최종 금액에 영향을 준다고 해석이 가능하다.

소비세와 사과 가격이 같은 양만큼 오르면 최종 금액에는 소비세가 200의 크기로, 사과 가격이 2.2 크기로 영향을 준다고 해석할 수 있다.

 

5.6 단순한 계층 구현하기

1)곱셈 계층

# 곱셈 계층 구현
class MulLayer:
  def __init__(self): #인스턴스변수 x,y 초기화
    self.x = None
    self.y = None
 
  # 순전파 
  #x와 y를 곱해서 반환
  def forward(self, x, y):
    self.x = x
    self.y = y
    out = x * y
 
    return out
 
  # 역전파 
  #상류에서 넘어온 미분에 순전파의 값을 서로 바꿔 곱한 후 하류로 흘린다.
  def backward(self, dout):
    dx = dout * self.y # x와 y를 바꾼다
    dy = dout * self.x
 
    return dx, dy

 

  • MulLayer 사용하여 사과구매 순전파 구현

from layer_naive import *

apple = 100
apple_num = 2
tax = 1.1

# 계층들
mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()

# 순전파
apple_price = mul_apple_layer.forward(apple,apple_num)
price = mul_tax_layer.forward(apple_price,tax)

print(price) # 220

# 역전파
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print(dapple, dapple_num, dtax) # 2.2 110 200

 

2)덧셈 계층

class AddLayer:
  def __init__(self):
    pass # 아무것도 안함=덧셈 계층에는 초기화 필요없기 때문에
 
  def forward(self, x, y):
    out = x + y
    return out
 
  def backward(self, dout): # 상류에서 내려온 미분 그대로 하류로
    dx = dout * 1
    dy = dout * 1
    return dx, dy

 

사과 2개와 귤3개 구입 구현

사과 2개와 귤3개 구입

# 덧셈 계층과 곱셈 계층 활용해 사과문제 풀이
# from layer_naive import *
 
apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1
 
# layer
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()
 
# forward
apple_price = mul_apple_layer.forward(apple, apple_num)  # (1)
orange_price = mul_orange_layer.forward(orange, orange_num)  # (2)
all_price = add_apple_orange_layer.forward(apple_price, orange_price)  # (3)
price = mul_tax_layer.forward(all_price, tax)  # (4)
 
# backward
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice)  # (4)
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)  # (3)
dorange, dorange_num = mul_orange_layer.backward(dorange_price)  # (2)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)  # (1)
 
print(price) #715
print(dapple_num, dapple, dorange, dorange_num, dtax) #110 2.2 3.3 165 650

 

5.7 활성화 함수 계층 구현하기

- 활성화함수 ReLU와 Sigmoid구현

 

1) ReLU

ReLU 계층의 식
x에 대한 y의 미분에 대한 식

- 순전파 때의 입력인 x가 0보다 크면 역전파는 상류의 값을 그대로 하류로 흘린다.

- 반면 순전파 때 x가 0이하면 역전파 때는 하류로 신호를 보내지 않는다.(0을 보냄)

 

ReLU계층의 계산 그래프

# ReLU 계층 코드 구현
class Relu:
    def __init__(self):
        self.mask = None
 
    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0
 
        return out
 
    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout
 
        return dx

- Relu클래스는 mask라는 인스턴스 변수 가짐

- mask는 True / False로 구성된 넘파이 배열

- 순전파 입력인 x의 원소값이 0 이하면 True, 아니면 False

 

  • 역전파 때는 순전파 때 만들어둔 mask를 써서 mask의 원소가 True인 곳에는 상류에서 전파된 dout을 0으로 설정
import numpy as np
 
x = np.array( [[1.0, -0.5], [-2.0, 3.0]])
print(x)
#출력: [[ 1.  -0.5]
#	 [-2.   3. ]]

mask = (x <= 0)
print(mask)
#출력: [[False  True]
# 	[ True False]]

 

2) Sigmoid

sigmoid 함수
시그모이드 함수 계산 그래프

x와 +노드 말고도 exp와 /노드가 새롭게 등장했다. exp노드는 y=exp(x)계산을 수행하고 /노드는 y=1/x 계산을 수행한다.

해당 계산은 국소적 계산의 전파로 이뤄진다. 이제 역전파의 흐름을 한단계씩 짚어보자

 

- 1단계

/노드, 즉 1/x를 미분하면 다음 식이 된다.

상류에서 흘러온 값에 -y^2을 곱해서 하류로 전달한다.(-y^2 = 순전파의 출력을 제곱한 후 마이너스를 붙인 값)

 

- 2단계

+노드는 여과없이 하류로 내보낸다.

 

- 3단계

exp노드는 y=exp(x)연산을 수행한다.

이 예에서는 exp(-x)를 곱한다.

 

- 4단계

x노드는 순전파 때의 값을 서로 바꿔 곱한다. 이 예에서는 -1을 곱하면 된다.

 

완성된 시그모이드 계층의 역전파 계산 그래프이다.

역전파의 최종 출력값이 하류노드로 전파된다.

단순화하면 이렇다.

 

식을 정리하여 구하면 이렇다. (sigmoid 계층의 역전파는 순전파의 출력(y)만으로 계산할 수 있다.)

 

시그모이드 계층을 파이썬으로 구현해보자

# sigmoid 파이썬 구현
class Sigmoid:
    def __init__(self):
        self.out = None
 
    def forward(self, x):
        out = sigmoid(x)
        self.out = out
        return out
 
    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out
 
        return dx

- 이 구현에서는 순전파의 출력을 인스턴스 변수 out에 보관했다가, 역전파 계산 때 그 값을 사용한다.

 

5.8 Affine/Softmax 계층 구현하기

1) Affine계층

- 가중합

- 신경망의 순전파에서는 가중치 신호의 총합을 계산하기 때문에 행렬의 곱을 사용했다. (넘파이에서는 np.dot())

X = np.random.rand(2) #입력
W = np.random,rand(2,3) #가중치
B = np.random.rand(3) #편향
Y=np.dot(X,W)+B

뉴런의 가중치 합은 Y=np.dot(X, W) + B 처럼 계산, 이 Y를 활성화 함수로 변환해 다음 층으로 전파하는 것이 신경망 순전파의 흐름이었다.

계산 그래프로 표현

 

위 그래프의 역전파를 알아보자.

W^T는 전치행렬

 

계산 그래프의 역전파를 구해보자.

Affine 계층의 역전파

- W와 미분값 W는 형상이 같다. 차원의 원소를 같게 해야 계산이 가능하기 때문이다.

- 그것을 이용하여 행렬 내적의 역전파는 행렬에 대응하는 차원의 원소 수가 일치하도록 내적을 조립하여 구할 수 있다.

 

1-2) 배치용 Affine 계층

위의 Affine계층은 입력데이터로 x하나만을 고려한 것이다. 

이제 데이터 N개를 묶어 순전파하는 경우, 즉 배치용 Affine계층을 생각해보자. (묶은 데이터를 배치라고 부른다.)

배치용 Affine 계층의 계산 그래프

- 기존과 다른 부분은 X의 형상이 (N,2)로 변한 것

- 편향 덧셈 주의: 순전파 때의 편향 덧셈은 X*W에 대한 편향이 각 데이터에 더해짐

- ex) N = 2일때, 편향은 그 두 데이터 각각의 계산결과에 더해짐

 

구체적인 예시

import numpy as np
 
# 순전파 때의 편향 덧셈
X_dot_W = np.array([[0,0,0], [10,10,10]])
B = np.array([1,2,3])

print(X_dot_W)
#출력: [[ 0  0  0]
#   [10 10 10]]

print(X_dot_W + B)
#출력: [[ 1  2  3]
#     [11 12 13]]
  • 순전파의 편향 덧셈이 각각의 데이터에 더해지기 때문에 역전파 때는 각 데이터의 역전파 값이 편향의 원소에 모여야 함 
# 역전파 때의 편향 덧셈
dY = np.array([[1,2,3],[4,5,6]])
print(dY)
#출력: [[1 2 3]
#      [4 5 6]]

dB = np.sum(dY, axis=0)
print(dB)
#출력: [5 7 9]

- 예시에서는 데이터가 2개(N=2)

- 편향의 역전파는 그 두 데이터에 대한 미분을 데이터마다 더해서 구함

- np.sum()에서 0번째 축에 대해 axis=0 의 총합을 구하는 것이다.

 

affine구현

class Affine:
  def __init__(self, W, b):
    self.W = W
    self.b = b
    self.x = None
    self.dW = None
    self.db = None
 
  def forward(self, x):
    self.x = x
    out = np.dot(x, self.W) + self.b
 
    return out
 
  def backward(self, dout):
    dx = np.dot(dout, self.W.T)
    self.dW = np.dot(self.x.T, dout)
    self.db = np.sum(dout, axis=0)
 
    return dx

 

2) softmax with loss 계층

소프트맥스 함수는 출력층에서 사용한다.

소프트맥스 함수는 입력 값을 정규화하여 출력한다.

소프트맥스 계층은 입력값을 정규화(출력의 합이 1이 되도록 변형)하여 출력한다. 

 

softmax-with-loss 계층의 계산 그래프
간소화된 sotemax-with-loss 계층의 계산 그래프

- 이전 계층에서 3개의 입력(점수)를 받는다.

- Softmax계층은 입력 (a1,a2,a3)를 정규화하여 (y1,y2,y3)를 출력한다.

- cross entropy error계층은 softmax의 출력 (y1,y2,y3)와 정답 레이블 (t1,t2,t3)를 받고 이 데이터들로부터 손실L을 출력한다.

 

- 역전파의 결과는 (y1-t1, y2-t2, y3-t3)이다.

- softmax의 출력과 정답레이블의 차분인 것이다.

- 신경망의 역전파에서는 이 오차가 앞 계층에 전해진다.

 

  • 신경망 학습의 목적은 신경망의 출력(softmax의 출력)이 정답레이블과 가까워지도록 가중치 매개변수의 값을 조정하는 것이다.
  • 따라서 오차를 효율적으로 앞 계층에 전달해야한다.

이(softmax with loss)를 구현한 코드

# Softmax-with-Loss 계층 구현
# common / layer.py
 
class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None # 손실
        self.y = None # softmax의 출력
        self.t = None # 정답레이블 (원-핫 벡터)
 
    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        
        return self.loss
 
    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        if self.t.size == self.y.size: # 정답레이블이 원-핫 벡터일 경우
            dx = (self.y - self.t) / batch_size
        else:
            dx = self.y.copy()
            dx[np.arange(batch_size), self.t] -= 1
            dx = dx / batch_size
        
        return dx

 

5.9 오차역전파법 구현하기

전제

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

1단계 미니배치

-> 훈련 데이터 중 일부를 무작위로 가져온다. 선별한 데이터를 미니배치라고 하며 이 미니배치의 손실 함수 값을 줄이는 것이 목표이다.

2단계 기울기 산출

미니배치의 손실 함수 값을 줄이기 위해 각 가중치 매개변수의 기울기를 구한다. 기울기는 손실 함수의 값을 작게 하는 방향을 제시한다.

3단계 매개변수 갱신

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

4단계

1~3단계를 반복한다.

 

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

import sys, os
sys.path.append('/deep-learning-from-scratch')
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)
 
        # 계층 생성
        # OrderedDict = 순서가 있는 딕셔너리, 순서 기억
        # 순전파 때는 계층을 추가한 순서대로 / 역전파 때는 계층 반대 순서로 호출
        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

- 신경망의 계층을 OrderedDict(순서가 있는 딕셔너리)에 보관한다.

- 순전파 때는 추가한 순서대로, 역전파 때는 반대 순서로 호출한다.

- Affine 계층과 ReLU계층이 각자의 내부에서 순전파와 역전파를 처리하고 있으니 계층을 올바른 순서로 연결한 다음 호출해주면 된다.

- 계층으로 구현한 덕분에 신경망 쉽게 구축 가능했다.

 

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

  • 기울기를 구하는 방법 : 수치 미분, 해석적 방법(오차역전파법을 이용해 효율적 계산 가능)
  • 해석적 방법이 효율적이나 수치 미분 또한 필요하다.
  • 수치 미분은 오차역전파법을 정확히 구현했는지 확인 위해 필요
  • 수치 미분은 구현하기 쉬우나, 오차역전파법은 구현 복잡해 실수가 있을 수 있음
  • 수치미분의 결과와 오차역전파법의 결과를 비교하여 검증해야한다. 기울기가 일치함을 확인하는 작업을 기울기 확인 이라고 한다.
# 기울기 확인 (gradient check)
import sys, os
sys.path.append('/deep-learning-from-scratch')
import numpy as np
from dataset.mnist import load_mnist
from ch05.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))
    
#출력: 
#W1:4.1821647831167817e-10
#b1:2.534937764494963e-09
#W2:5.183343681548899e-09
#b2:1.4008996131881224e-07

 

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

import sys, os
sys.path.append('/content/drive/MyDrive/deep-learning-from-scratch')
 
import numpy as np
from dataset.mnist import load_mnist
from ch05.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)
        
>>>
0.16111666666666666 0.1669
0.9040833333333333 0.9045
0.9236666666666666 0.9269
0.93625 0.9373
0.94525 0.944
0.9503833333333334 0.948
0.9547166666666667 0.951
0.9602166666666667 0.9569
0.9626333333333333 0.9588
0.9652166666666666 0.9598
0.9688 0.9619
0.9709833333333333 0.9641
0.9729 0.9653
0.9746166666666667 0.9667
0.97505 0.9663
0.97645 0.967
0.9784833333333334 0.9692

 

5.10 정리

계산 그래프: 계산 과정을 시각적으로 보여주는 방법

계산 그래프를 이용해 신경망의 동작과 오차역전파법을 설명하고 그 처리 과정을 계층이라는 단위로 구현했다.(ReLU, softmax with loss, Affine, softmax)

모든 계층에서 순전파(통상의 계산을 수행)와 역전파(각 노드의 미분을 구할 수 있다.)라는 메서드를 구현한다.

이를 통해 가중치 매개변수의 기울기를 효율적으로 구한다.

'밑바닥 딥러닝 > 밑바닥부터 시작하는 딥러닝1' 카테고리의 다른 글

7장  (0) 2023.06.29
6장  (0) 2023.06.22
4장  (0) 2023.06.01
손글씨 숫자 인식  (0) 2023.05.13
2장  (0) 2023.04.07