728x90
◎ 밑바닥부터 시작하는 딥러닝(1)¶
● chapter 5. 오차역전파법¶
- 신경망의 가중치 매개변수의 기울기를 효율적으로 계산하는 '오차역전파법(backpropagation)'
5.1 계산 그래프
- 계산 그래프는 계산 과정을 그래프로 나타낸 것이며 노드(node)와 에지(edge)로 표현됩니다.
계산 그래프로 풀다
- 계산을 왼쪽에서 오른쪽으로 진행하는 단계를 순전파(forward propagation)이라고 한다.
- 오른쪽에서 왼쪽으로 진행하는 단계는 역전판(backward propagation)이라고 한다.
국소적 계산
- 계산 그래프의 특징은 '국소적 계산'을 전파함으로써 최종 결과를 얻는다는 점에 있다.
- 국소적 계산은 결국 전체에서 어떤 일이 벌어지든 상관없이 자신과 관계된 정보만으로 결과를 출력할 수 있다는 것이다. 즉, 각 노드는 자신과 관련한 계산 외에는 아무것도 신경 쓸게 없다.
왜 계산 그래프로 푸는가?
- 계산 그래프의 이점은 전체가 아무리 복잡해도 각 노드에서는 단순한 계산에 집중하여 문제를 단순화할 수 있다.
- 또 다른 이점은 중간 계산 결과를 모두 보관할 수 있다.
- 계산 그래프를 사용하는 가장 큰 이유는 역전파를 통해 '미분'을 효율적으로 계산할 수 있기 때문
5.2 연쇄법칙
연쇄법칙이란
- 합성 함수 : 여러 함수로 구성된 함수
- 연쇄법칙은 합성 함수의 미분에 대한 성질이며, 합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있다. 이것이 연쇄법칙의 원리이다.
5.3 역전파
- 덧셈의 역전파에서는 상류의 값을 그대로 흘러보내서 순방향 입력 신호의 값은 필요하지 않았지만, 곱셈의 역전파는 순방향 입력 신호의 값이 필요하다.
5.4 단순한 계층 구현하기
In [1]:
# 곱셈 계층
class MulLayer:
def __init__(self):
self.x = None
self.y = None
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
In [2]:
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.00000000000003
In [3]:
# 역전파
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.00000000000001 200
☆ 이처럼 계산 그래프에서의 계층은 쉽게 구현할 수 있으며, 이를 사용해 복잡한 미분도 계산할 수 있다.
5.5 활성화 함수 계층 구현하기
In [4]:
# ReLU 구현 코드
class Relu:
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x <= 0) # True, False로 이루어진 배열
out = x.copy()
out[self.mask] = 0
return out
def backward(self, dout):
dout[self.mask] = 0
dx = dout
return dx
In [5]:
# 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
5.6 Affine/Softmax 계층 구현하기
- 신경망의 순전파 때 수행하는 행렬의 곱은 기하학에서는 '어파인 변환'이라고 한다.
In [6]:
# Affine 구현 코드
class Affine:
def __init__(self, W, b):
self.W = W
self.b = b
self.x = None
self.original_x_shape = None
# 가중치와 편향 매개변수의 미분
self.dW = None
self.db = None
def forward(self, x):
# 텐서 대응
self.original_x_shape = x.shape
x = x.reshape(x.shape[0], -1)
self.x = x
out = np.dot(self.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)
dx = dx.reshape(*self.original_x_shape) # 입력 데이터 모양 변경(텐서 대응)
return dx
☆ 신경망에는 학습과 추론이 있다.
☆ 추론할 때는 마지막 Affine 계층의 출력을 인식 결과로 이용. 즉, 답을 하나만 내는 추론 경우에는 가장 높은 점수만 알면 되니 Softmax 계층은 필요 없다.
☆ 반면, 신경망 학습할 때는 Softmax 계층 필요
Softmax-with-Loss 계층
- Softmax 계층은 출력과 정답 레이블을 받고, 이 데이터들로부터 손실 L을 출력한다.
- (y1, y2, y3)는 Softmax 계층의 출력이고 (t1, t2, t3)는 정답 레이블일 때 (y1-t1, y2-t2, y3-t3)는 Softmax 계층의 역전파이다.
In [7]:
# Softmax-with-Loss 계층 구현 코드
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.7 오차역전파법 구현하기
신경망 학습 4 단계
미니배치 -> 기울기 산출 -> 매개변수 갱신 -> 반복
- 이때까지 기울기를 구하기 위해서 수치 미분을 사용했다. 하지만 수치 미분은 구현하기는 쉽지만 계산이 오래 걸리는 단점이 있다.
- 오차역전파법을 이용하면 느린 수치 미분과 달리 기울기를 효율적이고 빠르게 구할 수 있다.
In [8]:
import sys, os
sys.path.append('C:/Users/설위준/Desktop/deep-learning-from-scratch-master') # 부모 디렉터리의 파일을 가져올 수 있도록 설정
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
In [9]:
# 오차역전파법을 사용한 신경망 학습 구현
import sys, os
sys.path.append('C:/Users/설위준/Desktop/deep-learning-from-scratch-master')
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)
0.12393333333333334 0.1228 0.9014666666666666 0.9028 0.9206166666666666 0.9226 0.9330166666666667 0.9327 0.9443333333333334 0.9427 0.9496 0.9461 0.9552166666666667 0.9521 0.9594333333333334 0.9563 0.9620666666666666 0.9575 0.9659833333333333 0.9619 0.9681333333333333 0.962 0.9701166666666666 0.9639 0.9724666666666667 0.9652 0.9747666666666667 0.9671 0.97575 0.9673 0.97535 0.968 0.9780333333333333 0.9691
In [10]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:80% !important;}</style>"))
728x90
'Book report > Deep Learning from Scratch' 카테고리의 다른 글
Chapter 6. 학습 관련 기술들 (0) | 2021.10.10 |
---|---|
Chapter 4. 신경망 학습 (0) | 2021.10.06 |
Chapter 3. 신경망 (0) | 2021.08.16 |
Chapter 2. 퍼셉트론 (0) | 2021.08.15 |