ch.5 오차역전파법(2)

pasongtak·2024년 5월 13일

딥러닝

목록 보기
6/11

단순한 계층 구현하기

모든 계층은 forward()와 backward()라는 공통의 메서드를 가지도록 구현한다.
forward()는 순전파, backward()는 역전파를 처리한다.

곱셈 계층

곱셈 계층은 MulLayer라는 이름의 클래스로 구현한다.

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
  • 순전파 구현하기
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)
  • 각 변수에 대한 미분: backward()에서 구할 수 있음.
# 역전파
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print(dapple, dapple_num, dtax)

덧셈 계층

덧셈 계층은 AddLayer라는 이름의 클래스로 구현한다.

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

활성화 함수 계층 구현하기

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

Sigmoid 계층

class Sigmoid:
	def __init__(self):
    	self.out = None
        
    def forward(self, x):
    	out = 1 / (1 + np.exp(-x))
        self.out = out
        
      	return out
        
    def backward(self, dout):
    	dx = dout * (1.0 - self.out) * self.out
        
        return dx

Affine/Softmax 계층 구현하기

Affine 계층

신경망의 순전파 때 수행하는 행렬의 곱을 기하학에서 어파인 변환이라고 한다.

  • Affine 계층의 계산 그래프
  • Affine 계층의 역전파

배치용 Affine 계층

  • 배치용 Affine 계층의 계산 그래프
  • 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(s, 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

Softmax-with-Loss 계층

  • 간소화한 Softmax-with-Loss 계층의 계산 그래프
class SoftmaxWithLoss:
	def __init__(self):
    	self.loss = None
        self.y = None
        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]
        dx = (self.y - self.t) / batch_size
        
        return dx

오차역전파법 구현하기

  • 신경망 구현하기
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
        
    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
        
    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 = list(self.layers.values())
        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['b2'] = self.layers['Affine2'].db
        
        return grads

기울기 검증하기

수치 미분의 결과와 오차역전파법의 결과를 비교하여 오차역전파법을 제대로 구현했는지 검증하는데, 이 두 방식으로 구한 기울기가 일치함을 확인하는 작업을 기울기 확인 이라고 한다.

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_backdrop = network.gradient(x_batch, t_batch)

for key in grad_numerical.keys():
	diff = np.average( np.abs(grad_backdrop[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.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)

정리

오차역전파법을 이용한 각각의 계층을 구현했고, 모든 계층에서는 forward와 backward라는 메서드를 구현한다. forward 메서드는 데이터를 순방향으로 전파하고, backward 메서드는 데이터를 역방향으로 전파한다.
너무 어렵다. 내일 다시 보기,,,,,

0개의 댓글