Skip to content

Instantly share code, notes, and snippets.

@kjsman

kjsman/README.md

Last active Jun 21, 2021
Embed
What would you like to do?
Autograd

Autograd

Warning

  • 어디선가 오류 날 가능성 100%
  • 변수명 다 쌩까고 단일 변수로 취급함. 따라서 편미분 못함 ㅎㅎ

Update Log

v1.1.0
 * 로그 연산 가능해짐
 * n계도미분이 지원됨

v1.0.0
 * 출시

Usage

from autograd import magic, Log

x = magic('x')

expr = x ** 2
print(expr(4), expr.grad(4)) # 16.0 8.0

expr = (3 ** x + x ** 2 + 7) / (x + 3) + x ** (x + 7)
print(expr(2), expr.grad(2), expr.grad.grad(2)) # 516.0 2660.8688585662944 13169.79565842797

from math import e
expr = Log(e ** x)
print(expr(7), expr.grad(7)) # 7.0 1.0
import math
from abc import ABC, abstractmethod
def magic(k):
if isinstance(k, Gradable):
return k
elif type(k) in [int, float]:
return Number(k)
elif type(k) == str:
return Variable(k)
else:
print(k, type(k))
raise TypeError("Not Appropriate")
class Gradable(ABC):
def __init__(self):
pass
def __repr__(self):
if isinstance(self, Number):
return f"<autograd: Number {self.value}>"
elif isinstance(self, Variable):
return f"<autograd: Variable {self.alias}>"
else:
return "<autograd: Gradable expression>"
def __call__(self, x):
return self.calc(x)
@abstractmethod
def calc(self):
pass
@property
@abstractmethod
def grad(self):
pass
def __add__(left, right):
return Add(left, right)
def __sub__(left, right):
return Sub(left, right)
def __mul__(left, right):
return Mul(left, right)
def __truediv__(left, right):
return Div(left, right)
def __pow__(left, right):
return Pow(left, right)
def __radd__(right, left):
return Add(left, right)
def __rsub__(right, left):
return Sub(left, right)
def __rmul__(right, left):
return Mul(left, right)
def __rtruediv__(right, left):
return Div(left, right)
def __rpow__(right, left):
return Pow(left, right)
class Number(Gradable):
def __init__(self, a):
self.value = a
def calc(self, x):
return self.value
@property
def grad(self):
return Number(0)
class Variable(Gradable):
def __init__(self, a):
self.alias = a
def calc(self, x):
return x
@property
def grad(self):
return Number(1)
class Add(Gradable):
def __init__(self, a, b):
self.a = magic(a)
self.b = magic(b)
def calc(self, x):
return self.a(x) + self.b(x)
@property
def grad(self):
return self.a.grad + self.b.grad
class Sub(Gradable):
def __init__(self, a, b):
self.a = magic(a)
self.b = magic(b)
def calc(self, x):
return self.a(x) - self.b(x)
@property
def grad(self):
return self.a.grad - self.b.grad
class Mul(Gradable):
def __init__(self, a, b):
self.a = magic(a)
self.b = magic(b)
def calc(self, x):
return self.a(x) * self.b(x)
@property
def grad(self):
return self.a.grad * self.b + self.a * self.b.grad
class Div(Gradable):
def __init__(self, a, b):
self.a = magic(a)
self.b = magic(b)
def calc(self, x):
return self.a(x) / self.b(x)
@property
def grad(self):
return (self.a.grad * self.b - self.a * self.b.grad) / (self.b ** 2)
class Pow(Gradable):
def __init__(self, a, b):
self.a = magic(a)
self.b = magic(b)
def calc(self, x):
return math.pow(self.a(x), self.b(x))
@property
def grad(self):
return Pow(self.a, self.b) * self.b.grad * Log(self.a) + Pow(self.a, self.b - 1) * self.b * self.a.grad
class Log(Gradable):
def __init__(self, *args):
if not 1 <= len(args) <= 2:
raise TypeError(f"Log() takes 1 or 2 positional arguments but {len(args)} were given")
self.a = magic(args[0])
self.b = magic(args[1]) if len(args) == 2 else Number(math.e)
def calc(self, x):
return math.log(self.a(x), self.b(x))
@property
def grad(self):
return self.a.grad / (self.a * Log(self.b)) - (Log(self.a) * self.b.grad) / (self.b * Log(self.b) ** 2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment