持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
自己动手去一个神经网络框架,代码和相关视频可以文章结尾处找到
基础类
Tensor
属性
| 属性名 | 类型 | 说明 |
|---|---|---|
| data | numpy array | 持有数据 |
| grad | numpy array | 梯度 |
| ctx | Context | 反向传播的环境 |
方法
- backward
Context
| 属性名 | 类型 | 说明 |
|---|---|---|
| arg | Fucntion | |
| parents | Tensor | y = wx |
| saved_tensors | []:numpy array |
- save_for_backward
Functions
这里 Fucntion 是运算操作符的基类,apply 函数主要
class Function:
def apply(self:Tensor,arg,*x):
ctx = Context(arg,self,*x)
ret = Tensor(arg.forward(ctx, self.data,*[t.data for t in x]))
ret._ctx = ctx
return ret
这里 apply 方法会被添加到 Tensor 上,所以这里 self 是指向 Tensor 实例
- 创建 Context,将运算操作符类如 Sum、Dot 或者 Add 传入保存在 arg
self,*x就是参与运算的 Tensor,比例 y=wx 中的 w 和 x 不过这时他们都是 Tensor 类型- 然后调用 arg 前向方法,这里传入刚刚创建好的 ctx 就是 y=wx 的 numpy类型传入,因为 x 和 w 都是 Tensor 类型,而他们 x.data 和 w.data 都应该是 numpy 类型,然后保存在 context 的
saved_tensors数组 - 最后将更新后的 ctx 赋值给前向传播计算结果 Tenesor 的
_ctx属性
regiseter
这个方法是将基于 Function 的运算算子例如 Sum、Add 或者 Dot 等添加到 Tensor 类
def register(name,fxn):
setattr(Tensor,name,partialmethod(fxn.apply,fxn))
这里有 2 点值得说一说
- 使用
setattr方法将Sum、Add 或者 Dot 这些函数添加到 Tensor 这个类上 - 使用
partialmethod将 apply 方法注入 arg 参数,返回一个新的方法做 Tensor 的方法
Dot、Sum 和 Add 等操作类
class Dot(Function):
@staticmethod
def forward(ctx, input, weight):
ctx.save_for_backward(input,weight)
return input.dot(weight)
@staticmethod
def backward(ctx,grad_output):
input,weight = ctx.saved_tensors
grad_input = grad_output.dot(weight.T)
grad_weight = grad_output.T.dot(input).T
return grad_input,grad_weight
forward(前向传播)
- 这里传入的 input 和 weight 是 numpy array 类型,然后调用 ctx 对于 y =wx 那么 x 和 w 就分别对一个 input 和 weight y 都是前向传播的结果,最终 ctx 添加到前向传播的结果 y 这个 Tensor 上
- 然后就是计算 input 和 weight 的内积
backward(反向传播)
- 首先从 ctx 取出 weight 和 input
- 计算 local 梯度也就是 w 和 x 对于 y 梯度,并且将 local 梯度乘以 y 对于 L 梯度,就是分别 input 和 weight 梯度
- 注意这里 grad (图中) 不是tensor类型而是 numpy array 类型
前向传播
反向传播
def backward(self,allow_fill=True):
if self._ctx is None:
return
if self.grad is None and allow_fill:
assert self.data.size == 1
self.grad = np.ones_like(self.data)
assert(self.grad is not None)
grads = self._ctx.arg.backward(self._ctx,self.grad)
if len(self._ctx.parents) == 1:
grads = [grads]
for t,g in zip(self._ctx.parents,grads):
if g.shape != t.data.shape:
print("grad shape must match tensor shape in %r, %r != %r" %(self._ctx.arg, g.shape, t.data.shape))
#assert(False) "print something"
t.grad = g
t.backward(False)
结合下图对反向传播实现简单说明一下,out_sum 是一个标量,是对向量每个分量的求
相关资源
github 代码地址 github.com/zideajang/a… 视频资源