영넌 개발로그

[밑시딥3] 자연스러운 코드로 4 - 변수 사용성 개선, 연산자 오버로드 본문

코딩/ML , Deep

[밑시딥3] 자연스러운 코드로 4 - 변수 사용성 개선, 연산자 오버로드

영넌 2023. 9. 6. 23:41

변수 이름 지정

class Variable:
    def __init__(self,data, name=None):
        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
        self.generation = 0
        self.name = name

ndarray 인스턴스 변수

Variable을 ndarray 처럼 보이도록 만들기

class Variable:
    ...
    @property
    def shape(self):
        return self.data.shape

    @property
    def ndim(self):
        return self.data.ndim

    @property
    def size(self):
        return self.data.size

    @property
    def dtype(self):
        return self.data.dtype

    def __len__(self):
        return len(self.data)

    def __repr__(self):
        if self.data is None:
            return 'variable(None)'
        p = str(self.data).replace('\n', '\n' + ' ' *9)
        return 'variable(' + p + ')'
@property 데코레이션
메서드를 인스턴스 변수처럼 사용 가능
ex ) x.shape() 대신 x.shape로 호출 가능

__repr__ : 객체를 print 함수에 건냈을 때 호출되는 함수

 

연산자 오버로드

class Mul(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
        return gy * x1, gy * x0

def mul(x0,x1):
    return Mul()(x0,x1)

#실행코드
a = Variable(np.array(3.0))
b = Variable(np.array(2.0))
c = Variable(np.array(1.0))

y = add(mul(a,b),c)

y.backward()

print(y)
print(a.grad)
print(b.grad)

매번 add(mul(a,b),c) 를 작성하기엔 번거로우니

y = a * b +c 형태와 같이 연산자를 사용할 수 있도록 Variable 변경

파이썬에서는 __add__와 __mul__ 같은 특수 메서드를 정의함으로써 사용자 지정 함수가 호출 되도록함

class Variable:
    ....
    def __mul__(self, other):
        return mul(self, other)
        
        
a = Variable(np.array(3.0))
b = Variable(np.array(2.0))
y = a*b
print(y)
>> variable(6.0)
Variable.__mul__ = mul
Variable.__add__ = add


#실행코드
a = Variable(np.array(3.0))
b = Variable(np.array(2.0))
c = Variable(np.array(1.0))

y = a * b +c
y.backward()

print(y)
print(a.grad)
print(b.grad)


>> variable(7.0)
>> 2.0
>> 3.0

 

Variable 객체 이외에도 ndarray, int, float 등도 가능하도록 변경

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

class Function:
    def __call__(self, *inputs):
        inputs = [as_variable(x) for x in inputs] #추가
        
        
#실행코드
x = Variable(np.array(3.0))
y = x + np.array(3.0)

print(y)
#float, int를 위함
def add(x0, x1):
    x1 = as_array(x1)
    return Add()(x0,x1)

 

* 이항 연산자의 경우 피연산자의 위치에 따라 호출되는 특수 메소드가 다르다

피연산자가 좌항이면 __mul__ 이 호출되고

피연산자가 우항이면 __rmul__ 이 호출된다.

#좌항이 float나 int인 경우
Variable.__mul__ = mul
Variable.__add__ = add
Variable.__rmul__ = mul
Variable.__radd__ = add

#좌항이 ndarray 인스턴스인 경우
#우선순위 설정 필요
class Variable:
    __array_priority__ = 200

 

 

특수 연산자들

  • __neg__(self) : 부호 변환 연산자
  • __sub__(self, other) : 뺄셈
  • __rsub__(self, other)
  • __truediv__(self, other) : 나눗셈
  • __rturediv__(self, other)
  • __pow__(self, other) : 거듭제곱 
class Neg(Function):
    def forward(self, x):
        return -x

    def backward(self, gy):
        return -gy

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

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)

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)

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.__neg__ = neg
Variable.__sub__ = sub
Variable.__rsub__ = rsub
Variable.__truediv__ = div
Variable.__rturediv__ = rdiv
Variable.__pow__ = pow

#실행코드
x = Variable(np.array(2.0))
print(-x)
print(x-1.0)
print(2.0-x)
print(x**3)

 

Comments