영넌 개발로그
[밑시딥3] 자연스러운 코드로 4 - 변수 사용성 개선, 연산자 오버로드 본문
변수 이름 지정
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)
'코딩 > ML , Deep' 카테고리의 다른 글
[밑시딥3] 자연스러운 코드로 3 - 메모리 관리, 절약 모드 (0) | 2023.09.06 |
---|---|
[밑시딥 3] 자연스러운 코드로 2 - 복잡한 계산 그래프 (0) | 2023.09.06 |
[밑시딥3] 자연스러운 코드로 1 - 가변 길이 인수, 같은 변수 반복 사용 (0) | 2023.09.06 |
[밑시딥3] 미분 자동 계산 4 - 테스트 (0) | 2023.09.05 |
Comments