본문 바로가기
Study 공부할레나

[스터디/밑시딥3] 제 1고지 미분 자동 계산_6.수동 역전파

by LENA-cfg 2023. 8. 13.

6.1 Variable 클래스 추가 구현

class Variable:
    def __init__(self, data):
        self.data = data # 통상값
        self.grad = None # 미분값

- grad는 None으로 초기화해두고 실제 역전파 계산 할때 미분값 계산

- 벡터나 행렬등 다변수에 대한 미분은 기울기라고 함

 

6.2 Function 클래스 추가 구현

class Function:
    def __call__(self, input):
        x = input.data
        y = self.forward(x)
        output = Variable(y)
        self.input = input # 입력 변수 기억
        return output

    def forward(self, x):
        raise NotImplementedError()

    def backward(self, gy):
        raise NotImplementedError()

 

6.3 Square Exp 클래스 추가 구현

class Square(Function):
    def forward(self, x):
        y = x ** 2
        return y

    def backward(self, gy):
        x = self.input.data
        gx = 2 * x * gy
        return gx

class Exp(Function):
    def forward(self, x):
        y = np.exp(x)
        return y

    def backward(self, gy):
        x = self.input.data
        gx = np.exp(x) * gy
        return gx

- x ** 2은 미분하면 2x인데 여기에 인수로 전달된 미분(=gy)를 곱해줌

- 지수함수는 상수여서 미분해도 그대로라서 np.exp(x)에 인수로 전달된 미분(=gy)를 곱해줌

 

6.4 역전파 구현 

순전파 먼저!

위의 이미지처럼 순전파 먼저 진행된다고 했을 때

a =  A(x)만 봤을때

x는 파란색 글자인 <__main__.Variable object at 0x0000028F5D55D060> 메모리 주소에 저장되고

x.data로 0.5의 값을 꺼내

square의 forward 메서드를 통해 제곱을 연산해서 0.25을 리턴 후 y에 담아서

y(0.25)를 Variable에 담아(주소는 보라색 글자) 값(output)을 반환해줌

 

그리고, input(파란색 메모리 주소 있는거)을 역전파 시 꺼내 쓸수 있게 self.input안에 담아줌

 

위의 순서는 b = B(a), y = C(b)도 메서드나 인풋값이 다를 뿐 동일한 순서대로 진행됨

그리고, self.input 부분때문에 input 3개가 저장되어있음

순전파 순서에 따른 값 정리 이미지

 

역전파 구현

이제, 위의 이미지 처럼 역전파의 값을 구해줘야하는데 역전파는 dy/dy=1에서 시작하기 때문에 y.grad = np.array(1.0)이 된다.

x 변수에 들어간 self.input.data는 y = C(b)일때 self.input에 저장된 값 그대로 가져온것임

 

각각의 역전파 연산을 순서대로 가져오면 아래 이미지 처럼 됨

# 전체 소스 코드

import numpy as np


class Variable:
    def __init__(self, data):
        self.data = data
        self.grad = None


class Function:
    def __call__(self, input):
        x = input.data
        y = self.forward(x)
        output = Variable(y)
        self.input = input
        input2 = self.input
        return output

    def forward(self, x):
        raise NotImplementedError()

    def backward(self, gy):
        raise NotImplementedError()


class Square(Function):
    def forward(self, x):
        y = x ** 2
        return y

    def backward(self, gy):
        x = self.input.data
        gx = 2 * x * gy
        return gx


class Exp(Function):
    def forward(self, x):
        y = np.exp(x)
        return y

    def backward(self, gy):
        x = self.input.data
        gx = np.exp(x) * gy
        return gx

A = Square()
B = Exp()
C = Square()

x = Variable(np.array(0.5))
a = A(x)
b = B(a)
y = C(b)

y.grad = np.array(1.0)
b.grad = C.backward(y.grad)
a.grad = B.backward(b.grad)
x.grad = A.backward(a.grad)
print(x.grad)