使用numpy搭建自己的深度学习框架(一)

注:本系列搭建的深度学习框架名称叫numpyflow,缩写nf,用以熟悉目前主流的深度学习框架的基础和原理。本系列的目标是使用nf可以训练resnet。
开源地址:RanFeng/NumpyFlow

简介

这里的自动求导,我们可以理解为自动求解梯度而不是导函数。道理都是一样的,就是基础的链式法则,参考知乎的这个问题tensorflow的函数自动求导是如何实现的?

自动求导常用的方法有三种:

  • Numerical differentiation 数值微分
  • Symbolic differentiation 符号微分
  • Automatic differentiation 自动微分

广泛应用于深度学习框架的是后面两种微分。

Numerical differentiation

对于求导,我们第一时间能想到的就是根据导数定义来计算:

f ′ ( x ) = lim ⁡ Δ x → 0 f ( x + Δ x ) − f ( x ) Δ x f^{\prime}(x)=\lim _{\Delta x \rightarrow 0} \frac{f(x+\Delta x)-f(x)}{\Delta x} f(x)=Δx0limΔxf(x+Δx)f(x)
数值微分最大的特点就是很直观,好计算,利用了导数定义。

不过这里有一个很大的问题: Δ x \Delta x Δx 的选取怎么选择?选大了,误差会很大;选小了,不小心就陷进了浮点数的精度极限里,造成舍入误差。所以数值微分仅仅只能在手算的时候稍微计算一下,实际应用并不广泛。若一定要用的话,经常使用如下的公式,从两侧逼近:
f ′ ( x ) = lim ⁡ Δ x → 0 f ( x + Δ x ) − f ( x − Δ x ) 2 Δ x f^{\prime}(x)=\lim _{\Delta x \rightarrow 0} \frac{f(x+\Delta x)-f(x-\Delta x)}{2 \Delta x} f(x)=Δx0lim2Δxf(x+Δx)f(xΔx)

Symbolic differentiation

还有一种求导方法,就是——写出导函数,然后求解梯度。这个很容易理解,比如: f ( x ) = x 2 f(x)=x^2 f(x)=x2的导函数是 f ′ ( x ) = 2 x f'(x)=2x f(x)=2x,那么其在 x = 3 x=3 x=3处的梯度就是 f ′ ( 3 ) = 6 f'(3)=6 f(3)=6
这个过程就是符号微分做的事了,很明显,符号微分的优势就是计算出来的梯度比较精确。但是缺点就很多了:

  • 难以计算导函数,每写一个函数,符号微分框架都会封装一个人到框架中用于求解导函数😨。比起数值微分,符号微分难度要大很多。
  • 时间复杂度高,每写出一个函数,符号微分都要分析函数的表达式,合并表达式等等操作,然后得出其导函数。

实现符号微分的技术点:

  • 统一封装算子和运算
  • 预定义初等函数的导数运算
    • 线性运算
    • 三角函数运算
    • 指数对数
  • 预定义导数的求导规律:
    • d ( u / v ) = ( u d v − v d u ) / v 2 d(u/v)=(udv−vdu)/v2 d(u/v)=(udvvdu)/v2
    • d ( u ∗ v ) = u d v + v d u d(u∗v)=udv+vdu d(uv)=udv+vdu
  • 预定义导数的链式法则:
    • d y d x = d y d u ∗ d u d x \frac{dy}{dx}=\frac{dy}{du}*\frac{du}{dx} dxdy=dudydxdu

而TensorFlow中,就使用了这种求导方式,它将每个计算比如a+b看成一个op,我们可以将op当做基本的算子,整个框架中的所有计算都是由有限多个基本算子组合而成的。TensorFlow对每个算子都计算出对应的导函数,这样就能完成整个框架的导函数。

Automatic differentiation

接下来就是自动微分方式了,自动微分不关注导函数什么样,它只负责求解对应的梯度,这个正是我们所需要的。搞那些花里胡哨的导函数干啥?但是它跟符号求导的共同点就是,都要预定义好一堆的基本算子op,对每个op计算出当前的梯度,然后根据这些基本op的梯度推算出整个计算图的梯度。

自动求导分成两种模式,一种是 Forward Mode,另外一种是 Reverse Mode。

举个例子: f ( x , y ) = x 2 + y f(x,y)=x^2+y f(x,y)=x2+y,可以分为两个op,一个是指数op,一个是加法op,我们拆分一下,
f 1 = x 2 f 2 = f 1 + y f1=x^2\\ f2=f1+y f1=x2f2=f1+y
这样,带入 x = 2 , y = 2 x=2,y=2 x=2,y=2,我们求得 f 2 = 6 , f 1 = 4 f2=6,f1=4 f2=6,f1=4,求解梯度的时候
∂ f 2 ∂ f 1 ( 2 , 2 ) = 1 ∂ f 2 ∂ y ( 2 , 2 ) = 1 ∂ f 2 ∂ x ( 2 , 2 ) = ∂ f 2 ∂ f 1 ( 2 , 2 ) ∗ ∂ f 1 ∂ x ( 2 , 2 ) = 1 ∗ 4 = 4 \frac{∂f2}{∂f1}(2,2)=1 \\ \\ \frac{∂f2}{∂y}(2,2)=1 \\ \frac{∂f2}{∂x}(2,2)=\frac{∂f2}{∂f1}(2,2) *\frac{∂f1}{∂x}(2,2) =1*4=4 f1f2(2,2)=1yf2(2,2)=1xf2(2,2)=f1f2(2,2)xf1(2,2)=14=4
所以我们用这种方式求得了 x = 2 , y = 2 x=2,y=2 x=2,y=2时候的梯度,而整个过程,我们都没有求解其导函数, f 2 f2 f2也没有直接对 x x x求导过。这样一看,天然的递归!而我们上面所使用的方法,也就是reverse mode。该方法首先正向遍历整个图,计算出每个节点的值;然后逆向(从上到下)遍历整个图,计算出节点的偏导值。 这是一种自顶向下的求梯度方式。

那foward mode是怎么样的呢?
∂ f 1 ∂ x ( 2 , 2 ) = 4 ∂ f 2 ∂ f 1 ( 2 , 2 ) = 1 ∂ f 2 ∂ x ( 2 , 2 ) = ∂ f 2 ∂ f 1 ( 2 , 2 ) ∗ ∂ f 1 ∂ x ( 2 , 2 ) = 1 ∗ 4 = 4 ∂ f 2 ∂ y ( 2 , 2 ) = 1 \frac{∂f1}{∂x}(2,2)=4 \\ \frac{∂f2}{∂f1}(2,2)=1 \\ \frac{∂f2}{∂x}(2,2)=\frac{∂f2}{∂f1}(2,2) *\frac{∂f1}{∂x}(2,2) =1*4=4 \\ \frac{∂f2}{∂y}(2,2)=1 \\ xf1(2,2)=4f1f2(2,2)=1xf2(2,2)=f1f2(2,2)xf1(2,2)=14=4yf2(2,2)=1

好像两个差距不大,只是顺序问题,下面通过一个更加复杂的例子来说明两者的区别,顺便熟悉自动求导的细节。

一个例子🌰

接下来,我们从一个🌰来了解求解梯度的过程,然后下一章了解自动求导的必须组件。

我们随机设定一个函数
f = x y 3 z ( y + z ) = x y 4 z + z y 3 z 2 f = xy^3z(y+z)=xy^4z+zy^3z^2 f=xy3z(y+z)=xy4z+zy3z2
拆解开来,它的计算图如下
在这里插入图片描述
我们带入 x = 5 , y = 2 , z = 3 x=5,y=2,z=3 x=5,y=2,z=3,求解如下图
在这里插入图片描述

forward mode

假设我们要求出 ∂ f 6 ∂ y ( 5 , 2 , 3 ) \frac{∂f6}{∂y}(5,2,3) yf6(5,2,3)的值,那么forward mode计算过程如下:
在这里插入图片描述

对于forward mode,求解梯度是从底层开始的,这样经过一次自底向上的计算,我们求得了原函数对于 y y y的梯度,如果要求解原函数对于 x x x z z z的梯度,那还得按照上面的方法计算一次。所以,forward mode一次只能计算一个变量的梯度,多少个变量就得forward多少次。

如果函数输入输出为:
R → R m R \to R^m RRm

那么利用forward mode只需计算一次上图的过程即可,非常高效。对于输入输出映射为如下的:
R n → R m R^n \to R^m RnRm

这样一个有 n n n个输入的函数,求解函数梯度需要 n n n遍如上计算过程。
而在神经网络中,输入输出一般来说是 n > > m n>>m n>>m,所以forward mode实在是太不适合了

那么利用forward mode进行自动微分就太低效了,因此便有下面要介绍的reverse mode。

reverse mode

在这里插入图片描述
可以看出,一次的reverse mode就可以求出整个图中变量的梯度,这在深度学习中是非常适合的。

由于这种方法比较简单而且适合深度学习,所以在numpyflow中,我们采用此种(reverse mode)求解梯度的方法。

下一节,我们开始实现自动微分reverse mode代码。

引用

1.自动微分(Automatic Differentiation)简介
2.自动微分方法(auto diff)
3.自动求导–Deep Learning框架必备技术二三事

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值