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

2021. 3. 10. 23:07개인 공부 공간/딥러닝

파이썬 함수로 이용하기

기존 Square클래스를 사용하는 코드는 다음과 같다.

 

x = Variable(np.array(0.5))
f = Square()
y = f(x)

 

f 를 이용해 Square 클래스의 인스턴스를 생성후 그 인스턴스를 호출해야 하는 번거로움이 있다. 이를 효율적으로 변경하기 위해 파이썬 함수를 사용하면 편리하다.

 

def square(x):
  f = Square()
  return f(x)

def exp(x):
  f = Exp()
  return f(x)

 

생성한 square , exp 두 함수를 이용해 역전파 과정을 코드로 구현하면 다음과 같다.

 

x = Variable(np.array(0.5))
y = square(exp(square(x)))

y.grad = np.array(1.0)
y.backward()
print(x.grad)

 

실행 결과

3.297442541400256

 

위의 코드를 보면 기존의 코드와 또 다른 차이점이 하나 존재한다. square(exp(square(x))) 이 부분을 보면 기존과 다르게 함수를 연속으로 한번에 적용에 한줄에 표현하였다.

backward 메서드 간소화

기존의 코드를 조금 더 단순화 하기 위해 매번 역전파 과정에서 y.grad = np.array(1.0)를 입력하는 대신 Variablebackward 메서드에 다음과 같은 코드를 추가한다.

 

class Variable:
    ... # 생략

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

    funcs = [self.creator]
    while funcs: # 빈 리스트가 아닐때까지
      f = funcs.pop() # 함수를 가져온다
      x, y = f.input, f.output # 함수의 입력과 출력을 가져온다
      x.grad = f.backward(y.grad) # backward 메서드를 호출한다

      if x.creator is not None:
        funcs.append(x.creator) # 하나 앞의 함수를 리스트에 추가한다

 

np.ones_like 를 이용해 변수의 gradNone 이면 즉, 변수의 생성자가 없으면 자동으로 1을 채운 self.data 형상과 데이터 타입이 같은 ndarray 인스턴스를 생성한다. 이를 통해 매번 y.grad 값을 1로 지정해주고 시작해야 하는 번거로움이 사라진다.

 

x = Variable(np.array(0.5))
y = square(exp(square(x)))
y.backward()
print(x.grad)

 

실행 결과

3.297442541400256

 

ndarray만 취급하기

Variable 의 데이터로 ndarray 의 데이터 타입만 취급하도록 의도했지만 실수로 float, int 등의 다른 데이터 타입을 입력하는 경우가 발생할수도 있다. 이를 방지하기 위해 다른 데이터타입을 입력할 경우 오류를 발생시키게 할 필요성이 존재한다.

 

class Variable:
    def __init__(self, data):
        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

        ... # 생략
x = Variable(np.array(1.0)) # Ok
x = Variable(None) # ok

x = Variable(1.0) # RaiseError

 

실행 결과

TypeError: <class 'float'>은(는) 지원하지 않습니다.

 

이와 같이 데이터 타입이 None 이거나 ndarray 인 경우에는 문제가 없지만 그 외의 데이터 타입을 입력하면 예외가 발생한다.

하지만 이 경우 주의해야 할 부분이 하나 존재한다. 0차원 ndarray 를 이용해 연산을 하면 출력이 np.float64 의 형태가 되어 문제가 발생한다. 이를 방지하기 위해 다음과 같은 함수를 생성해 Function 클래스에 추가해야 한다.

 

def as_array(x):
    if np.isscalar(x):
        return np.array(x)
    return x

 

위의 함수는 입력 데이터가 스칼라 타입인지 확인해주는 기능을 제공한다.

 

class Function:
  def __call__(self, input):
    x = input.data
    y = self.forward(x)
    output = Variable(as_array(y))
    output.set_creator(self) # 출력 변수에 창조자를 설정
    self.input = input
    self.output = output # 출력도 저장
    return output

 

위와 같이 순전파의 결과인 yVariable 로 감쌀 때 as_array() 를 이용한다. 이를 통해 출력 결과는 항상 ndarray 인스턴스임을 보장해준다.


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

출처: https://privatedevelopnote.tistory.com/81 [개인노트]