페이지

2022년 8월 19일 금요일

14.3 미분값 재설정

 방금 역전파 시 미분값을 더해주도록 코드를 수정했습니다. 그런데 이 변경으로 인해 새로운 주의사항이 뛰어나옵니다. 바로 같은 변수를 사용하여 '다른' 계산을 할 경우 계산이 꼬이는 문제입니다. 다음 코드르 예로 살펴봅시다.


# 첫 번째 계산
x = Variable(np.array(3.0))
y = add(x, x)
y.backward()
print(x.grad)

# 두 번째 계산(같은 x를 사용하여 다른 계산을 수행)
y = add(add(x, x), x)
y.backward()
print(x.grad)


2.0 5.0

앞의 코드는 서로 다른 두 가지 미분 계산을 수행했습니다. 그러면서 메모리를 절약하고자 Variable 인스턴스인 x를 재사용했다고 해봅시다. 그 결과 두 번째 x의 미분값에 첫 번째 미분값이 더해지고, 5.0이라는 잘못된 값을 돌려줍니다(3.0이 되어야 합니다).

이문제를 해결하기 위해 Variabvle 클래스에 미분값을 초기화하는 cleargrad메서드를 추가하겠습니다.


class Variable:

  def __init__(selfdata):
    if data is not None:
      if not isinstance(data, np.ndarray):
        raise TypeError('{}은(는) 지원하지 않습니다.' .format(type(data)))
    self.data = data
    self.grad = None
    self.creator = None

  def set_creator(selffunc):
    self.creator = func


  def backward(self):
    if self.grad is None:
      self.grad = np.ones_like(self.data)

    funcs = [self.creator]
    while funcs:
      f = funcs.pop()
      gys = [output.grad for output in f.outputs]  
      gxs = f.backward(*gys)   
      if not isinstance(gxs, tuple):  
        gxs = (gxs,)
      
      for x, gx in zip(f.inputs, gxs):  
        if x.grad is None:
          x.grad = gx
        else:
          x.grad = x.grad + gx

        if x.creator is not None:
          funcs.append(x.creator)

  def cleargrad(self):
    self.grad = None


cleargrad는 미분값을 초기화하는 메서드로, 단순히 self.grad에 None을 대입합니다. 이 메서드를 사용하면 여러가지 미분을 연달아 계산할 때 똑같은 변수를 재사용할 수 있습니다. 앞의 예는 다음과 같이 수정하면 됩니다.

# 첫 번째 계산
x = Variable(np.array(3.0))
y = add(x, x)
y.backward()
print(x.grad)

# 두 번째 계산(같은 x를 사용하여 다른 계산을 수행)
x.cleargrad() #미분값 초기화
y = add(add(x, x), x)
y.backward()
print(x.grad)

2.0 3.0

이번에는 두 번째 미분값도 재대로 구했습니다(두 번째 미분의 올바른 값은 3.0입니다). 예시처럼 두 번째 x.backward()를 호출하기 전에 x.cleargrad()를 호출하면 변수에 누적된 미분값이 초기화 됩니다. 이것으로 다른 계산에 똑같은 변수를 재사용할 때 생기던 문제가 사라졌습니다.


DeZero의 cleargrad메서드는 최적화 문제를 풀 때 유용하게 사용할 수 있습니다. 최적화 문제란 함수의 최솟값과 최댓값을 찾는 문제를 말합니다. 실제로 28단계에서 로젠브록 함수(Rosenbrock function)를 최적화할 때 cleargrad메서드를 사용합니다.


이상으로 이번 단계를 마무리 합니다. 이번 단곌르 거치며  Variable 클래스는 한층 성장했습니다. 그러나 아직도 중요한 문제가 하나 남아 있습니다. 그 문제는 이어지는 15, 16단계에서 해결하겠슷ㅂ니다. 16단계에서 마치면 마침내 Variable클래스가 완성됩니다.

댓글 없음: