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

2021. 3. 15. 18:20개인 공부 공간/딥러닝

ndarray와 함께 사용하기

이전 단계에서 Variable 인스턴스 끼리의 연산은 a + b, a * b 와 같이 쉽고 직관적으로 작성할수 있도록 코드를 수정했습니다. 하지만 아직 ndarray 인스턴스와는 사용이 불가능한데, 이를 가능하도록 코드를 수정했습니다.

이를 위해 주어진 객체를 Variable 인스턴스로 변환하는 as_variable 함수를 구현했습니다.

 

def as_variable(obj):
    if isinstance(obj, Variable):
        return obj
    return Variable(obj)

 

이제 이 함수를 이용해 Function 클래스를 수정해보겠습니다.

 

class Function:
    def __call__(self, *inputs):
        inputs = [as_variable(x) for x in inputs]

        xs = [x.data for x in inputs]
        ys = self.forward(*xs)
        ....

 

inputs 에 담긴 모든 원소들을 Variable 인스턴스로 변환하여 ndarray 인스턴스가 주어져도 Variable 인스턴스로 변환합니다.

 

x = Variable(np.array(2.0))
y = x + np.array(3.0)
print(y)

 

실행 결과

variable(5.0)

 

ndarray 인스턴스를 Variable 인스턴스로 자동 변환해주는 기능이 추가되어서 위와 같은 연산이 가능해졌습니다.

float, int와 함께 사용하기

이제 ndarray 뿐만 아니라 float , int 와도 사용할 수 있게 코드를 수정하겠습니다. 여기에서는 전에 구현했었던 float 이나 intndarray 인스턴스로 변환하는 as_array 함수를 이용했습니다. float , intas_array 를 이용해서 ndarray 인스턴스로 변환 후에 Function 클래스 내의 as_variable 을 통해 최종적으로 Variable 클래스로 변환됩니다.

 

def add(x0, x1):
    x1 = as_array(x1)
    return Add()(x0, x1)
x = Variable(np.array(2.0))
y = x + 3.0
print(y)

 

실행 결과

variable(5.0)

 

문제점 1: 첫 번째 인수가 float나 int인 경우

현재의 DeZero는 x * 2.0 는 실행 가능하지만 2.0 * x 는 제대로 실행할 수 없습니다.

 

y = 2.0 * x
print(y)

 

실행 결과

TypeError: unsupported operand type(s) for *: 'float' and 'Variable'

 

이러한 에러가 발생하는 이유는 다음과 같습니다.

  1. 연산자 왼쪽에 있는 2.0의 __mul__ 메서드를 호출하려 시도한다.
  2. 하지만 2.0은 float 타입이므로 __mul__ 메서드는 구현되어 있지 않다.
  3. 다음은 * 연산자 오른쪽에 있는 x 의 특수 메서드를 호출하려 시도한다.
  4. x 가 오른쪽에 있기 때문에 (__mul__ 대신) __rmul__ 메서드를 호출하려 시도한다.
  5. 하지만 Variable 인스턴스에는 __rmul__ 메서드가 구현되어 있지 않다

쉽게 얘기하면 * 와 같은 이항 연산자의 경우 피연산자(항)의 위치에 따라 호출되는 특수 메서드가 다르기 때문이고, __rmul__ 메서드를 구현하면 쉽게 해결이 가능하다.

 

Variable.__radd__ = add
Variable.__rmul__ = mul
x = Variable(np.array(2.0))
y = 3.0 * x + 1.0
print(y)

 

실행 결과

variable(7.0)

 

문제점 2: 좌항이 ndarray 인스턴스인 경우

x = Variable(np.array([1.0]))
y = np.array([2.0]) + x

 

위의 경우 우항인 Variable 인스턴스가 먼저 호출되길 원하는 상황이기 때문에 연산자 우선순위를 지정해야 합니다. 이러기 위해서는 Variable 클래스에 __array_priority__ 를 추가하고 그 값을 큰 정수로 지정해주어야 합니다.

 

class Variable:
    __array_priority__ = 200
    ....

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

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