더북(TheBook)

그림 1-7에서 볼 수 있듯이, 미분 값만 얻으면 가중치와 편향을 적절히 조절할 수 있다. 이 방식을 경사 하강법(gradient descent)이라고 한다.

하지만 손실 함수의 공식에는 가중치와 편향 변수가 없기 때문에 가중치와 편향에 따른 미분도 바로 계산할 수 없다. 대신 연쇄 법칙(chain rule)을 활용해야 한다. 연쇄 법칙의 이론을 이해하려면 다소 복잡한 수학이 필요하므로 책에서는 깊게 설명하지 않는다. 더구나 케라스 등 머신 러닝 라이브러리는 경사 하강법을 자동으로 수행하니 걱정하지 않아도 된다. 여기서는 가중치에 따른 손실 함수의 미분(즉, 기울기)만 얻을 수 있다면 가중치를 적절히 조정할 수 있다는 점만 알아두자.

이제 파이썬 코드에 backprop 함수를 추가한다.

import numpy as np

def sigmoid(x):
    return 1.0/(1 + np.exp(-x))

def sigmoid_derivative(x):
    return x * (1.0 - x)

class NeuralNetwork:
    def __init__(self, x, y):
        self.input = x
        self.weights1 = np.random.rand(self.input.shape[1],4)
        self.weights2 = np.random.rand(4,1)
        self.y = y
        self.output = np.zeros(self.y.shape)

    def feedforward(self):
        self.layer1 = sigmoid(np.dot(self.input, self.weights1))
        self.output = sigmoid(np.dot(self.layer1, self.weights2))

    def backprop(self):
        # weights2와 weights1에 따른 손실 함수의 미분을 찾기 위해 연쇄 법칙을 활용한다
        d_weights2 = np.dot(self.layer1.T, (2*(self.y - self.output) * sigmoid_derivative(self.output)))
        d_weights1 = np.dot(self.input.T, (np.dot(2*(self.y - self.output) * sigmoid_derivative(self.output), self.weights2.T) * sigmoid_derivative(self.layer1)))

        # 손실 함수의 미분 값을 사용해 가중치를 갱신한다
        self.weights1 += d_weights1
        self.weights2 += d_weights2

if __name__ == "__main__":
    X = np.array([[0,0,1], [0,1,1], [1,0,1], [1,1,1]])
    y = np.array([[0],[1],[1],[0]])
    nn = NeuralNetwork(X,y)

    for i in range(1500):
        nn.feedforward()
        nn.backprop()

    print(nn.output)

Note ≡

feedforward 함수에 sigmoid 함수를 호출한 부분을 눈여겨보자. sigmoid 함수는 값을 0과 1 사이로 줄여 넣는 활성화 함수다. 이렇게 줄여 넣는 이유는 이진 값을 예측하는 데 필요하기 때문이다. sigmoid 활성화 함수는 2장에서 더 자세히 설명한다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.