200行写一个自动微分工具

2019 年 11 月 5 日 AINLP

作者:tiandi,小米AI实验室,智能问答、智能客服方向。


专栏地址:

http://www.52nlp.cn/author/tiandiweizun


简介

机器学习工具包(PyTorch/TensorFlow)一般都具有自动微分(Automatic Differentiation)机制,微分求解方法包括手动求解法(Manual Differentiation)、数值微分法(Numerical Differentiation)、符号微法(Symbolic Differentiation)、自动微分法(Automatic Differentiation),具体的详细介绍可以参见自动微分(Automatic Differentiation)简介,这里主要说一下自动微分法的实现。

自动微分法实现

github地址:https://github.com/tiandiweizun/autodiff

git上有不少自动微分的实现,如autograd等,这里还有一个特别简单的AutodiffEngine更适合作为教程,但AutodiffEngine是静态图,整个过程对于初学者还是有点复杂的,主要是不直观,于是动手autodiff写了一个简单的动态图的求导,里面的大部分算子的实现还是参照AutodiffEngine的。

设计:其实主要是2个类,一个类Tensor用于保存数据,另一个类OP支持forward和backward,然后各种具体的运算类,如加减乘除等继承OP,然后实现具体的forward和backward过程

过程:分为forward和backward两个过程,forward从前往后计算得到最终的输出,并返回新的tensor(如下图中的v1),新的tensor保存通过哪些子tensor(v-1),哪个具体的算子(ln)计算得到的(计算图),backward按照计算图计算梯度,并赋值给对应的子tensor(v-1)

实现:

先贴一点代码

class Tensor:
def __init__(self, data, from_tensors=None, op=None, grad=None):
self.data = data # 数据
self.from_tensors = from_tensors # 是从什么Tensor得到的,保存计算图的历史
self.op = op # 操作符运算
# 梯度
if grad:
self.grad = grad
else:
self.grad = numpy.zeros(self.data.shape) if isinstance(self.data, numpy.ndarray) else 0

def __add__(self, other):
# 先判断other是否是常数,然后再调用
return add.forward([self, other]) if isinstance(other, Tensor) else add_with_const.forward([self, other])

def backward(self, grad=None):
# 判断y的梯度是否存在,如果不存在初始化和y.data一样类型的1的数据
if grad is None:
self.grad = grad = numpy.ones(self.data.shape) if isinstance(self.data, numpy.ndarray) else 1
# 如果op不存在,则说明该Tensor为根节点,其from_tensors也必然不存在,否则计算梯度
if self.op:
grad = self.op.backward(self.from_tensors, grad)
if self.from_tensors:
for i in range(len(grad)):
tensor = self.from_tensors[i]
# 把梯度加给对应的子Tensor,因为该Tensor可能参与多个运算
tensor.grad += grad[i]
# 子Tensor进行后向过程
tensor.backward(grad[i])

# 清空梯度,训练的时候,每个batch应该清空梯度
def zero_gard(self):
self.grad = numpy.zeros(self.data.shape) if isinstance(self.data, numpy.ndarray) else 0
class OP:
def forward(self, from_tensors):
pass

def backward(self, from_tensors, grad):
pass


class Add(OP):
def forward(self, from_tensors):
return Tensor(from_tensors[0].data + from_tensors[1].data, from_tensors, self)

def backward(self, from_tensors, grad):
return [grad, grad]


add = Add()

这里以加法为例,讲一下具体的实现。

Tensor类有四个属性,分别用于保存数据、子Tensor、操作符、梯度,OP类有两个方法,分别是forward和backword,其中Add类继承OP,实现了具体的forward和backword过程,然后Tensor重载了加法运算,如果是两个Tensor相加,则调用Add内部的forward。

x1_val = 2 * np.ones(3)
x2_val = 3 * np.ones(3)
x1 = Tensor(x1_val)
x2 = Tensor(x2_val)
# x1+x2 调用了Add的forward方法,并用[5,5,5]、x1与x2、加法操作构造新的Tensor,然后赋值给y
y = x1 + x2
assert np.array_equal(y.data, x1_val + x2_val)

backward过程先是计算梯度,然后把梯度赋值给各个子Tensor

# 判断梯度是否存在,此时不存在则初始化为[1,1,1]
# 调用Add的backward计算得到梯度[[1,1,1],[1,1,1]]
# 把梯度累加给对应的子Tensor,并调用x1和x2的backward
# 由于此时梯度存在,则不需要初始化
# 由于x1和x2无op和from_tensors,停止并退出
y.backward()
assert np.array_equal(x1.grad, np.ones_like(x1_val))
assert np.array_equal(x2.grad, np.ones_like(x2_val))

add_with_const和其他运算符参见代码

利用现有的自动求导来训练一个线性回归模型,绝大部分代码来自于AutodiffEngine里面的lr_autodiff.py,其中gen_2d_data方法用于生成数据,每个样例有3维,其中第一维是bias,test_accuracy判断sigmoid(w*x)是否大于0.5来决定分类的类别,并与 y进行对比计算准确率。

我这里仅修改了auto_diff_lr方法,去掉了静态图里面的逻辑,并换成Tensor来封装。

下图为训练日志和训练结果


推荐阅读

中文分词工具评估:chinese-segmentation-evaluation

ELECTRA: 超越BERT, 19年最佳NLP预训练模型

T5 模型:NLP Text-to-Text 预训练模型超大规模探索

Google T5速读

BERT 瘦身之路:Distillation,Quantization,Pruning


关于AINLP


AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLP君微信(id:AINLP2),备注工作/研究方向+加群目的。


登录查看更多
1

相关内容

在数学和计算机代数中,自动微分有时称作演算式微分,是一种可以借由计算机程序计算一个函数导数的方法。两种传统做微分的方法为:(1)对一个函数的表示式做符号上的微分,并且计算其在某一点上的值。(2)使用差分。使用符号微分最主要的缺点是速度慢及将计算机程序转换成表示式的困难。此外,很多函数在要计算更高阶微分时会变得复杂。 使用差分的两个重要的缺点是舍弃误差及数值化过程和相消误差。此两者传统方法在计算更高阶微分时,都有复杂度及误差增加的问题。自动微分则解决上述的问题。
最新《自动微分手册》77页pdf
专知会员服务
100+阅读 · 2020年6月6日
【干货书】用于概率、统计和机器学习的Python,288页pdf
专知会员服务
287+阅读 · 2020年6月3日
【新书】Python数据科学食谱(Python Data Science Cookbook)
专知会员服务
114+阅读 · 2020年1月1日
《动手学深度学习》(Dive into Deep Learning)PyTorch实现
专知会员服务
119+阅读 · 2019年12月31日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
87+阅读 · 2019年11月25日
开源书:PyTorch深度学习起步
专知会员服务
50+阅读 · 2019年10月11日
2019年机器学习框架回顾
专知会员服务
35+阅读 · 2019年10月11日
【新书】Python编程基础,669页pdf
专知会员服务
194+阅读 · 2019年10月10日
图解NumPy,这是理解数组最形象的一份教程了
机器之心
6+阅读 · 2019年7月12日
一个牛逼的 Python 调试工具
机器学习算法与Python学习
15+阅读 · 2019年4月30日
基于PyTorch/TorchText的自然语言处理库
专知
28+阅读 · 2019年4月22日
从张量到自动微分:PyTorch入门教程
论智
9+阅读 · 2018年10月10日
终于!TensorFlow引入了动态图机制Eager Execution
机器之心
4+阅读 · 2017年11月1日
手把手教你由TensorFlow上手PyTorch(附代码)
数据派THU
5+阅读 · 2017年10月1日
深度学习实战(二)——基于Keras 的深度学习
乐享数据DataScientists
15+阅读 · 2017年7月13日
Caffe 深度学习框架上手教程
黑龙江大学自然语言处理实验室
14+阅读 · 2016年6月12日
Learning to Weight for Text Classification
Arxiv
8+阅读 · 2019年3月28日
Feature Selection Library (MATLAB Toolbox)
Arxiv
7+阅读 · 2018年8月6日
Arxiv
23+阅读 · 2018年8月3日
Arxiv
3+阅读 · 2017年12月18日
VIP会员
相关VIP内容
最新《自动微分手册》77页pdf
专知会员服务
100+阅读 · 2020年6月6日
【干货书】用于概率、统计和机器学习的Python,288页pdf
专知会员服务
287+阅读 · 2020年6月3日
【新书】Python数据科学食谱(Python Data Science Cookbook)
专知会员服务
114+阅读 · 2020年1月1日
《动手学深度学习》(Dive into Deep Learning)PyTorch实现
专知会员服务
119+阅读 · 2019年12月31日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
87+阅读 · 2019年11月25日
开源书:PyTorch深度学习起步
专知会员服务
50+阅读 · 2019年10月11日
2019年机器学习框架回顾
专知会员服务
35+阅读 · 2019年10月11日
【新书】Python编程基础,669页pdf
专知会员服务
194+阅读 · 2019年10月10日
相关资讯
图解NumPy,这是理解数组最形象的一份教程了
机器之心
6+阅读 · 2019年7月12日
一个牛逼的 Python 调试工具
机器学习算法与Python学习
15+阅读 · 2019年4月30日
基于PyTorch/TorchText的自然语言处理库
专知
28+阅读 · 2019年4月22日
从张量到自动微分:PyTorch入门教程
论智
9+阅读 · 2018年10月10日
终于!TensorFlow引入了动态图机制Eager Execution
机器之心
4+阅读 · 2017年11月1日
手把手教你由TensorFlow上手PyTorch(附代码)
数据派THU
5+阅读 · 2017年10月1日
深度学习实战(二)——基于Keras 的深度学习
乐享数据DataScientists
15+阅读 · 2017年7月13日
Caffe 深度学习框架上手教程
黑龙江大学自然语言处理实验室
14+阅读 · 2016年6月12日
Top
微信扫码咨询专知VIP会员