개인 공부 공간/딥러닝

밑바닥부터 시작하는 딥러닝3 - STEP 6

Hoon Kang 2021. 3. 10. 23:02

Variable 클래스 추가 구현

역전파에 대응하는 Variable 클래스 구현 코드는 다음과 같다.

 

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

 

grad라는 인스턴스 변수를 추가한 후 None으로 초기화해 놓은 후에 실제로 나중에 역전파를 하면 미분값을 계산하여 대입한다.

Function 클래스 추가 구현

기존 Function 클래스에 다음 두 기능을 추가.

  • 미분을 계산하는 역전파(backward 메서드)
  • forward 메서드 호출 시 건네받은 Variable 인스턴스 유지
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()

 

__call__ 메서드에서 입력된 input을 self.input 에 저장후 나중에 역전파 과정에서 필요할 시 가져와 사용할 수 있다.

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

 

backward 의 인수 gy 는 출력 쪽에서 전해지는 미분값을 전달하는 역할을 한다.

역전파 구현

6-1.PNG

위와 같은 계산 그래프를 순전파 하는 코드는 다음과 같다.

 

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

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

 

이를 역전파로 y를 미분하는 과정의 계산 그래프와 코드는 다음과 같다.

6-2.PNG

 

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)

 

실행 결과

3.297442541400256

 

역전파는 $dy/dy = 1$ 에서 시작하기 때문에 출력 $y$의 미분값인 y.gradnp.array(1.0) 로 설정해야 한다. 수동으로 역전파를 구현하는 방법은 배웠지만 매번 역전파 순서에 맞춰 코드를 작성한다는 점과 초기 미분값을 자동으로 설정해줄 수 있다면 더욱 편리할 것 같다는 생각이 들었다.


출처: 사이토 고키, 『밑바닥부터 시작하는 딥러닝3』, 개앞맵시, 한빛미디어(2020)