while (1): study();

Ch3. 신경망 본문

독서

Ch3. 신경망

전국민실업화 2021. 7. 18. 02:08
728x90

* 책 내용 요약이 아니니 유의하시기 바랍니다.

 

1. 소프트맥스

 소프트맥스(Softmax) 함수의 지수연산은 값을 Overflow Error를 발생시킬 수 있기 때문에, 다음과 같이 지수가 너무 커지지 않도록 조정해주는 과정이 필요합니다.

Scaling softmax

 C라는 임의의 정수가 분모와 분자에 곱해졌을 때, 로그가 취해진 형태로 exp 안에 들어갈 수 있게 됩니다. 이때 logC를 C'라고 표기해봅시다. 즉 우리는 소프트맥스 함수의 분자, 분모의 exp 안에 어떤 수든 더할 수 있다는 것을 알게 되었습니다. 일반적으로 너무 큰 수가 되는 것을 막기 위함이기 때문에, C'는 배열의 최대값에 음수를 취한 값입니다.

 이렇게 구한 소프트맥스 확률값 중 가장 큰 인덱스를 정답 라벨로 반환하기 때문에, 사실상 소프트맥스 연산은 출력층에서 생략하더라도 정답에는 차이가 없습니다.

 

2. 시그모이드

신기하게도 책에서는 소프트맥스의 오버플로우 방지는 설명하는데, 시그모이드의 오버플로우 방지는 설명하지 않고 있습니다. 시그모이드는 다음과 같이 계산됩니다.

Sigmoid

따라서 x가 너무 작은 음수일 경우 충분히 오버플로우가 발생할 수 있습니다. 그렇다면 이 경우에는 어떻게 처리해야 할까 고민해보았습니다. 기본적인 접근법은 다음과 같습니다.

Stable sigmoid

 x가 양수일 때는 아무리 커지더라도 exp(-x)가 0에 수렴할 것이고, x가 음수일 때는 아무리 작아지더라도 exp(x)가 0에 수렴할 것입니다. 따라서 위의 식은 시그모이드 함수를 더 안정적으로 만들어줄 것이 분명합니다. 처음에는 다음과 같이 구현해보았습니다. 관건은 속도와 안정성입니다.

Stable sigmoid code - 1

 단순히 매핑 함수를 정의하고, 해당 함수를 벡터화하여 x에 적용시켰습니다. 이 경우에도 문제없이 잘 작동합니다만, 문제는 시간입니다. 비록 기존 시그모이드 함수가 오버플로우의 위험성이 존재한다고 한들, 30배의 속도 차이는 도저히 말이 안 됩니다. 따라서 다른 방법을 찾아야만 했습니다.

Stable sigmoid
Original Sigmoid

 두 번째 방법은 다음과 같습니다.

Stable sigmoid code - 2

 미리 양수와 음수의 인덱스를 구한 뒤, 입력과 같은 크기의 넘파이 배열을 만들고, 양수와 음수마다 각각 다른 함수를 브로드캐스팅해서 넣어줬습니다. 약간 느려졌지만, 분명 시간과 안정성의 중간 지점에서 Sweet spot을 찾았다고 할 수 있습니다.

전체 코드는 다음과 같습니다. 자세한 코드는 깃허브를 참조바랍니다.

깃허브: https://github.com/jeongchanyoung-1234/My_Deep_Framework

 

jeongchanyoung-1234/My_Deep_Framework

Constructing my own deep-learning framework with Python! - jeongchanyoung-1234/My_Deep_Framework

github.com

(출력층에 소프트맥스가 아닌 항등함수를 쓰고 argmax를 취했는데 결과가 같은 것을 확인했습니다. 음, 정확도 구하는 과정을 나름 병렬처리를 잘 했다고 생각했는데, 책에서도 같은 방식으로 해서 뭔가 아쉽습니다.)

import pickle
from time import time

from activation import *
from data.mnist import load_mnist


def get_data(flatten=True, normalize=False):
    (train_X, train_y), (test_X, test_y) = load_mnist(flatten=flatten, normalize=normalize)
    return train_X, train_y, test_X, test_y

def init_network():
    with open('data/sample_weight.pkl', 'rb') as f:
        network = pickle.load(f)

    return network

def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    start = time()
    a1 = x @ W1 + b1
    z1 = sigmoid(a1)
    a2 = z1 @ W2 + b2
    z2 = sigmoid(a2)
    a3 = z2 @ W3 + b3
    y = identity_function(a3)
    end = time()

    print('INFERENCE TIME:', end - start)

    return y

if __name__ == '__main__':
    train_X, train_y, test_X, test_y = get_data(True, True)
    network = init_network()

    test_size = test_X.shape[0]
    batch_size = None

    if batch_size is None:
        batch_size = test_size

    accuracy = 0
    for i in range(0, test_size, batch_size):
        y_hat = np.argmax(predict(network, test_X[i: i + batch_size]), axis=1)
        accuracy += (test_y[i:i + batch_size] == y_hat).sum()

    print('ACCURACY : {}'.format(accuracy / test_size))
728x90

'독서' 카테고리의 다른 글

Ch7. 합성곱 신경망  (0) 2021.07.22
Ch6. 학습 관련 기술들  (0) 2021.07.21
Ch5. 오차역전파법  (0) 2021.07.19
Ch4. 신경망 학습  (0) 2021.07.18
Ch2. 퍼셉트론  (0) 2021.07.18
Comments