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

[스터디/밑시딥3] 제 2고지 자연스러운 코드로_22. 연산자 오버로드(3)

by LENA-cfg 2023. 8. 28.

이번에 추가될 연산자들을 아래에 표로 만들었습니다.

특수 메서드 설명
__neg__(self) -self 양수를 음수로 or 음수를 양수로 바꿔주는 부호 연산자, 단항 연산자
__sub__(self, other) self - other 뺄셈 연산자, 이항 연산자
__rsub__(self, other) other - self 뺄셈 연산자, 이항 연산자
__truediv__(self, other) self / other 나눗셈 연산자, 이항 연산자
__rtruediv__(self, other) other / self 나눗셈 연산자, 이항 연산자
__pow__(self, other) self** other 거듭제곱 연산자, 이항 연산자

이러한 연산자를 추가하는 순서는 

1. Function 클래스를 상속하여 원하는 함수 클래스를 구현합니다.(예: Mul 클래스)

2. 파이썬 함수로 사용할 수 있도록 합니다.(예: mul 함수)

3. Variable 클래스의 연산자를 오버로드합니다.(Variable.__mul__=mul)

22.1 음수(부호 변환)

음수의 미분은 y = -x일 때 ∂y/∂x = -1입니다. 

역전파는 출력 쪽에서 전해지는 미분에 -1을 곱하여 앞단으로 흘려보내주기 때문에

 Neg 클래스를 구현한다음 Neg 클래스의 인스턴스를 생성해주는 neg 함수를 구현해주고

특수 메서드인 __neg__에 함수 neg를 대입해주면 됩니다.

 

class Neg(Function):
    def forward(self, x):
        return -x

    def backward(self, gy):
        return -gy


def neg(x):
    return Neg()(x)

Variable.__neg__ = neg

22.2 뺄셈

뺄셈의 미분은 y = x0 - x1일때 ∂y/∂x0 = 1, ∂y/∂x1 = -1이 됩니다.

x0의 미분 결과는 출력에서 전해지는 미분값에 1을 곱한 값이되고x1의 미분 결과는 출력에서 전해지는 미분값에 -1을 곱한 값이 됩니다.

class Sub(Function):
    def forward(self, x0, x1):
        y = x0 - x1
        return y

    def backward(self, gy):
        return gy, -gy

def sub(x0, x1):
    x1 = as_array(x1)
    return Sub()(x0, x1)
    
def rsub(x0, x1):
    x1 = as_array(x1)
    return sub(x1, x0)
    
Variable.__sub__ = sub
Variable.__rsub__ = rsub

y1 = 2.0 - x
y2 = x - 1.0
print(y1)  # variable(0.0)
print(y2)  # variable(1.0)

22.3 나눗셈

나눗셈 미분은 y = x0/x1일때  ∂y/∂x0 = 1,  ∂y/∂x1 = -x0/(x1)**2입니다.

나눗셈도 좌우항 중 어느 것에 적용할지에 다라 적용된는 함수가 다릅니다.

class Div(Function):
    def forward(self, x0, x1):
        y = x0 / x1
        return y

    def backward(self, gy):
        x0, x1 = self.inputs[0].data, self.inputs[1].data
        gx0 = gy / x1
        gx1 = gy * (-x0 / x1 ** 2)
        return gx0, gx1


def div(x0, x1):
    x1 = as_array(x1)
    return Div()(x0, x1)


def rdiv(x0, x1):
    x1 = as_array(x1)
    return div(x1, x0)
    
Variable.__sub__ = sub
Variable.__rsub__ = rsub

y = 3.0 / x
print(y)  # variable(1.5)

22.4 거듭제곱

거듭제곱은 y = x**c일때 x를 밑이라고하고 c를 지수라합니다.

거듭제곱의 미분공식은 ∂y/∂x = cx**c-1입니다.

class Pow(Function):
    def __init__(self, c):
        self.c = c

    def forward(self, x):
        y = x ** self.c
        return y

    def backward(self, gy):
        x = self.inputs[0].data
        c = self.c

        gx = c * x ** (c - 1) * gy
        return gx


def pow(x, c):
    return Pow(c)(x)
    
Variable.__pow__ = pow

y = x ** 3
y.backward()
print(y)  # variable(8.0)

코드를 보면 Pow 클래스를 초기화  할 때 지수 c를 제공합니다. 

순전파 메서드에서는 x 하나의 항만 받고

특수 메서드 __pow__에서는 함수 pow를 할당합니다.