jam 블로그

Artificial Neural Network 2 본문

인공지능

Artificial Neural Network 2

kid1412 2019. 10. 20. 22:51
728x90

신경망 (Artificial Neural Network, ANN)

스터디 용으로 Deep Learning from Scratch 2 책을 참고로 정리한 것입니다.

게시물 에 이어서 신경망의 학습에 대해서 정리하였습니다.

신경망의 성능을 나타내는 척도로는 손실 loss을(를) 사용합니다.
손실은 손실함수 (loss function)를 사용하여 구합니다.
손실 함수로는 아래와 같이 있습니다. (계속 추가 예정)

  • 교차 엔트로피 오차 (Cross Entropy Error)
  • 평균 제곱 오차 (Mean Squared Error)
  • Mean Absolute Error
  • Mean Absolute Percentage Error
  • Mean Squared Logarithmic Error
  • Hinge
  • Categorical Cross Entropy
  • Sparse Categorical Cross Entropy
  • Binary Categorical Cross Entropy
  • Kullback Leibler Divergence
  • Poisson Loss
  • Cosine Proximity

책에서는 교차 엔트로피 오차를 사용합니다.
교차 엔트로피 오차는 신경망이 출력하는 각 클래스의 확률과 정답 레이블을 이용해서 구할 수 있습니다.

위에서 만든 소스에 softmax와 교차 엔트로피 오차 계층을 새로 추가합니다.

소프트맥스 함수 (Softmax function)입력값을 0, 1 사이의 값으로 정규화 해주는 함수입니다. 수식은 아래와 같습니다.

$$
y_k = \frac{exp(s_k)}{\displaystyle\sum_{i=1}^{n} exp(s_i)}
$$

출력이 총 n개 일때, k번째의 출력 y를 구하는 계산식 입니다. 분자는 k번째의 output의 지수함수 이고, 분모는 모든 입력신호의 output의 지수 함수의 총합니다.

교차 엔트로피 오차의 공식은 다음과 같습니다.

$$
L = -\displaystyle\sum_{k} (t_k)log(y_k)
$$

t 는 k번째 클래스에 해당하는 정답 레이블 입니다.
log는 오일러의 수 e를 밑으로 하는 로그입니다.

미니배치 처리를 교려하면 위 수식에서 다음과 같이 변경해야합니다.

$$
L = -\frac{1}{N}\displaystyle\sum_{n}\sum_{k} t_{nk}logy_{nk}
$$

N으로 나눈는 이유는 1개당의 평균 손실 함수를 구하기 위함이고 이럴 경우 미니배치의 크기에 관계없이 항상 일관된 값을 구할 수 있습니다.

아래는 Softmax 와 Loss 함수 입니다.

import numpy as np

def softmax(x):
    if x.ndim == 2:
        x = x - x.max(axis=1, keepdims=True)
        x = np.exp(x)
        x /= x.sum(axis=1, keepdims=True)
    elif x.ndim == 1:
        x = x - np.max(x)
        x = np.exp(x) / np.sum(np.exp(x))
    return x

    def corss_entropy_error(y, t):
        if y.ndim == 1:
            t = t.reshape(1, t.size)
            y = y.reshape(1, y.size)
        if t.size == t.size
            t = t.argmax(axis=1)

        batch_size = y.shape[0]
        return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

class SoftmaxWithLoss:
    def __init__(self):
        self.params, self.grads = [], []
        self.y = None
        self.t = None

    def forward(self, x, t):
        self. t = t
        self. y = softmax(x)
        if size.t.size == self.y.size:
            self.t = self.t.argmax(asix=1)

        loss = cross_entropy_error(self.y, self.t)
        return loss

신경망 학습의 목표는 손실을 최소화 하는 매개변수를 찾는 것입니다.

기울기 (gradient) : 벡터의 각 원소에 대한 미분을 정리한 것. 기울기를 얻을 수 있다면 그것을 사용해 매개변수를 갱신할 수 있습니다.

오차역전파법 (back-propagation) : 신경망의 기울기를 구하는 방법.

  • 신경망은 여러 함수가 연결된 것이라고 생각할 수 있으며, 여러 함수에 대해 연쇄 법칙을 효율적으로 적용하여 기울기를 구해낼 수 있습니다.

노드에 따른 계산 방식입니다.

  1. 덧셈 노드
  1. 곱셈 노드
  1. Repeat 노드

  2. Matmul 노드

아래는 Matmul 노드의 역전파 까지 만든 소스입니다.

class Matmul:
    def __init__(self, W):
        self.params = [W]
        self.grads = [np.zeros_like(W)]
        self.x = None

    def forward(self, x):
        W, = self.params
        out = np.matmul(x, W)
        self.x = x
        return out

    def backward(self, dout):
        W, = self.params
        dx = np.matmul(dout, W.T)
        dW = np.matmul(self.x.T, dout)
        self.grads[0][...] = dW
        return dx

이번에는 Sigmoid, FCN 계층, Softmax with Loss 계층에 역전파를 넣어봅시다.

Sigmoid 함수를 수식으로 나타내면

$$
y = \frac{1}{1 + exp(-x)}
$$

인데, 이것을 미분 하면 다음과 같습니다.

$$
\frac{\partial{y}}{\partial{x}} = y(1 - y)
$$

따라서, 출력쪽으로 부터 넘어오는 기울기($ \frac{\partial{L}}{\partial{y}} $)에 방금 구한 미분 $ y(1 - y) $를 곱해서 입력 계층으로 전파합니다.

class Sigmoid:
    def __init__(self):
        self.params, self.grads = [], []
        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

FCN 계층 같은 경우 순전파는 y = np.matmul(x, W) + b로 구현이 가능합니다.
x, W의 Matmul 노드를 거쳐서 b의 Repeat 노드를 거친 두개를 덧셈 노드로 합치는 형식입니다.

class FCL:
    def __init__(self, W, b):
        self.params= [W, b] #  초기화 시 가중치, 편향을 인자값을 받음
        self.grads = [np.zeros_like(W), np.zeros_like(b)]
        self.x = None

    def forward(self, x):
        W, b = self.params
        out = np.matmul(x, W) + b
        self.x = x
        return out

    def backward(self, dout):
        W, b = self.params
        dx = np.matmul(dout, W.T)
        dW = np.matmul(self.xT, dout)
        db = np.sum(dout, axis=0)

        self.grads[0][...] = dW
        self.grads[1][...] = db
        return dx

Softmax with Loss 계층는 softmax와 cross entropy error 계층으로 되어 있습니다.

class SoftmaxWithLoss:
    def __init__(self):
        self.params, self.grads = [], []
        self.y = None
        self.t = None

    def forward(self, x, t):
        self. t = t
        self. y = softmax(x)
        if size.t.size == self.y.size:
            self.t = self.t.argmax(asix=1)

        loss = cross_entropy_error(self.y, self.t)
        return loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]

        dx = self.y.copy()
        dx[np.arange(batch_size), self.t] -= 1
        dx *= dout
        dx = dx / batch_size
    return dx

위에서 오차역전파법으로 기울기를 구하였기 때문에 이걸 사용하여 신경망의 매개변수를 갱신합니다.
학습 순서는 다음과 같습니다.

  1. 미니배치 : 훈련 데이터 중에서 무작위로 다수의 데이터를 골라낸다.
  2. 기울기 계산 : 오차역전파법으로 각 가중치 매개변수에 대한 손실 함수의 기울기를 구한다.
  3. 매개변수 갱신 : 기울기를 사용하여 가중치 매개변수를 갱신한다.
  4. 반복 : 1 ~ 3 단계를 반복합니다.

매개변수 갱신(Optimizer)에는 다양한 방법이 있습니다.

  1. 경사하강법 (Gradient Descent)
  2. 확률적경사하강법 (Stochastic Gradient Descent, SGD)
  3. 모멘텀 (Momentum)
  4. NAG (Nesterov Accelerated Gradient)
  5. Nadam
  6. AdaGrad
  7. RMSProp
  8. AdaDelta
  9. Adam

이 중 확률적 경사하강법으로 구현합니다.

$$
W \leftarrow W - \eta \frac{\partial L}{\partial W}
$$

$ W $는 매개변수 이며, $ \frac{\partial L}{\partial W} $는 손실 함수의 기울기 , $ \eta $는 학습률 (learning rate)입니다.

class SGD:
    '''
    lr : learning rate
    '''
    def __init__(self, lr = 0.01):
        self.lr = lr

    '''
        params : weight
        grads : 기울기
    '''
    def update(self, params. grads):
        for i in range(len(params)):
            params[i] -= self.lr * grads[i]
Comments