아래는 코드는, Affine Layer를 통해 추론과정을 하고 Softmax와 손실함수를 통해 정답 분포와 오차를 구한다음 역전파를 통해 가중치를 학습하는 과정을 간단하게 나타낸 코드이다.
# 오차역전파법 구현하기 - Two Layer 클래스 구현
## 1단계 - 미니배치: 훈련 데이터 중 일부를 무작위로 가져온다. 선별한 데이터를 미니배치라 하며, 미니배치의 손실 값을 줄이는 것이 목표다.
## 2단계 - 기울기 산출: 미니배치의 손실 함수 값을 줄이기 위해, 가중치 매개변수의 기울기를 구한다 (오차역전파 사용). 기울기는 손실함수의 값을 가장 작게하는 방향으로 학습한다.
## 3단계 - 매개변수 갱신: 가중치 매개변수를 기울기 방향으로 아주 조금씩 갱신한다.
## 4단계 - 반복
import sys
import os
import numpy as np
from collections import OrderedDict
sys.path.append(os.pardir)
from common.layers import *
from common.gradient import numerical_gradient
# TwoLayerNet 클래스 구현하기
# * 클래스 인스턴스 변수
# 1. params: 신경망의 매개변수를 보관하는 딕셔너리 변수
# - params['W1']은 1번째 층의 가중치, params['b1']은 1번째 층의 편향.
# - params['W2']은 2번째 층의 가중치, params['b2']은 2번째 층의 편향.
# 2. layers: 신경망의 계층을 순서대로 보관하는 딕셔너리 변수
# - layers['Affine1'], layers['Relu1'], layers['Affine2'] 처럼, 각 계층을 순서대로 유지한다.
# 3. lastLayer: 신경망의 마지막 계층.
# * 클래스의 메서드
# - __init__: 초기화 수행
# - predict(x): 추론
# - loss(x, t): 손실함수의 값을 구한다. x는 이미지 데이터, t는 정답 레이블
# - accuracy(x, t): 정확도를 구한다.
# - numerical_gradient(x, t): 가중치의 기울기를 수치미분으로 구함
# - gradient(x, t): 가중치의 기울기를 오차 역전파로 구함
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)
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
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
mnist가 테스트 데이터셋으로 사용되었다.
신경망 학습은 아래 4가지로 구성된다.
1. 미니배치 : 훈련 데이터 중 일부를 무작위로 가져옴 (= 미니배치)
2. 기울기 산출 : 미니배치의 손실 함수 값을 줄이기 위해 각 가중치 매개변수의 기울기를 구함 (역전파)
3. 매개변수 갱신 : 가중치 매개변수를 기울기 방향으로 아주 조금 갱신
4. 반복 : 1~3단계를 반복
import sys
import os
import numpy as np
sys.path.append(os.pardir)
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 = []
# Iteration
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)
# 1에폭 당 정확도 계산
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 | " + str(train_acc) + ", " + str(test_acc))
train acc, test acc | 0.11973333333333333, 0.119
train acc, test acc | 0.90375, 0.9077
train acc, test acc | 0.9187, 0.9206
train acc, test acc | 0.9325166666666667, 0.9335
train acc, test acc | 0.9435833333333333, 0.9423
train acc, test acc | 0.95005, 0.9484
train acc, test acc | 0.956, 0.9522
train acc, test acc | 0.9601166666666666, 0.9533
train acc, test acc | 0.9648166666666667, 0.9588
train acc, test acc | 0.9665333333333334, 0.9605
train acc, test acc | 0.9701166666666666, 0.9631
train acc, test acc | 0.97055, 0.9629
train acc, test acc | 0.9737166666666667, 0.9669
train acc, test acc | 0.9758, 0.9676
train acc, test acc | 0.9762, 0.9685
train acc, test acc | 0.9768333333333333, 0.9679
train acc, test acc | 0.9779333333333333, 0.9703
오차역전파를 이해하기위해 상당히 먼길을 돌아왔다 (Affine, Foward, Backward등..) 그래도 여러모로 역전파에 대한 필요성을 간단히 체험해 볼 수 있었다. 이제 부터는, 새로운 프로젝트로 들어가봐야 겠다 !!