딥러닝과 인공신경망: 2층 신경망 구현하기

Yougurt_Man·2022년 8월 11일
0

Machine Learning Theory

목록 보기
18/18

Two Layer 구현

아래는 코드는, 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등..) 그래도 여러모로 역전파에 대한 필요성을 간단히 체험해 볼 수 있었다. 이제 부터는, 새로운 프로젝트로 들어가봐야 겠다 !!

profile
Greek Yogurt

0개의 댓글