0%

手动实现计算图

手动实现计算图

  • 之前打过一次手动求导,但是学了pytorch之后发现自己并没有打backward之类的操作,不能偏导

  • 计算图的思路都很显然,问题是要怎样实现才好

    • 我们要定义一个类:点类(Node)
    • 因为实际计算中可能会有多个图,所以我们还要定义一个图类(Graph)
    • 为了长得像我们再定义一个tensor类
    • 还要定义一些计算类,来封装对于某个计算中自己的计算和求导方式

  • 对于图来说,我们还是要建立图的
  • 不过python的列表是动态的,所以肯定是邻接表会好一点
    • 对于每一个点,记录他的儿子和父亲
    • 记录值和导数,属于哪个图

封装的方法

backward_single,backward

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def backward_single(self, y):
"""
反向传播,计算本节点对y节点的导数
"""
if self.grad is None:
if self is y:
self.grad = np.mat(np.eye(self.dimension()))
else:
self.grad = np.mat(np.zeros((y.dimension(), self.dimension())))
for child in self.children:
if child.data is not None:
self.grad += child.backward_single(y) * child.get_grad(self)
return self.grad
def backward(self, y=None):
for x in self.father:
if y is None: x.backward(self)
else: x.backward(y)
if len(self.father) == 0:
self.backward_single(y)
Copy
  • backward_single是沿着儿子的,毕竟是递归方法,所以是单链的。后面的backward就是多链的

求导和计算

  • 为什么要封装求导和计算呢
  • 因为在各种继承类中有各种各样的求导和计算

重载运算符

1
2
3
4
5
6
7
8
9
10
11
12
def __add__(self, other):
y = Add(self, other)
y.compute()
return y
def __sub__(self, other):
y = Sub(self, other)
y.compute()
return y
def __mul__(self, other):
y = Mul(self, other)
y.compute()
return y
Copy

运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Add(Node):
def compute(self):
self.data = self.father[0].data + self.father[1].data
def get_grad(self, father):
return np.eye(self.dimension())
class Sub(Node):
def compute(self):
self.data = self.father[0].data - self.father[1].data
def get_grad(self, father):
if father is self.father[0]: return np.eye(self.dimension())*(-1)
else: return np.eye(self.dimension())
class Mul(Node):
# 点对点再求和
def compute(self):
self.data = self.father[0].data.T * self.father[1].data
def get_grad(self, father):
if father is self.father[0]: return self.father[1].data.T
else: return self.father[0].data.T
Copy

上面的各种运算类封装了自己的对应的运算和求导

  • 图的方法就很简单

    • 建立一个节点(没了)

      1
      2
      3
      4
      5
      class Graph(object):
      def __init__(self):
      self.nodes = []
      def add_node(self, node):
      self.nodes.append(node)
      Copy

最后就能搞出一个简单的计算图