深度学习-04-数值的微分
本文是《深度学习入门2-自製框架》 的学习笔记,记录自己学习心得,以及对重点知识的理解。如果内容对你有帮助,请支持正版,去购买正版书籍,支持正版书籍不仅是尊重作者的辛勤劳动,也是鼓励更多优秀作品问世。
当前笔记内容主要为:步骤4 数值的微分 章节的相关理解。
书籍总共分为5个阶段,每个阶段分很多步骤,最终是一步一步实现一个深度学习框架。例如前两个阶段为:
第 1 阶段共包括 10 个步骤 。 在这个阶段,将创建自动微分的机制
第 2 阶段,从步骤11-24,该阶段的主要目标是扩展当前的 DeZero ,使它能够执行更复杂的计算 ,使它能 够处理接收多个输入的函数和返回多个输出的函数
1.什么是导数
经过上面几节的学习及开发,我们已经有了变量 Variable,函数Function,复合函数的定义,接下来我们学习导数及微分。
先复习高数以及高中的导数,然后实现一种数值微分的方法来求导,最后我们实现一种替代数值微分的更高效算法-反向传播算法。
什么是导数
导数是微积分中的一个基本概念,它描述了函数在某一点的变化率。更准确地说,导数是函数在该点附近的变化量与自变量变化量之比的极限。
导函数定义
导函数是函数的导数在自变量取值范围内所形成的函数。换句话说,导函数就是将导数的概念推广到整个定义域,形成一个新的函数。
更精确定义:设函数
𝑓(𝑥)在定义域D内可导,则其导函数 𝑓′(𝑥)是一个定义在 𝐷上的函数,其对应于 𝑥的函数值为 𝑓(𝑥)在 𝑥 处的导数。
2.数值微分的实现
由于计算机不能处理极限值,所以我们在计算极限时都是采用的逼近的方法,例如设定 h = 0.0001(1e-4) 这种非常小的数值来计算导数,可想而知这种方法是一种近似的方法。这种方法包括误差。解决方法之一就是使用中心差分近似。中心差分近似不是使用f(x)与 f(x+h) 而是使用f(x-h) 与f(x+h) 的差。
前向差分近似:x,x+h 两点的斜率,f(x) ,f(x+h)
中心差分近似: x-h,x+h 两点的斜率,f(x-h), f(x+h)
相关定理已经证明结论有(使用泰勒展开式进行证明),中心差分近似相比前向差分近似实际产生的误差更小。
代码实现如下:
import numpy as np
from step01 import *
from step02_1 import *
def numerical_diff(f,x,eps=1e-4):
x0 = Variable(x.data -eps)
x1 = Variable(x.data +eps)
y0= f(x0)
y1 = f(x1)
return (y1.data -y0.data)/(2*eps)
验证y=x^2 在x =0, x=2 处的斜率
if __name__ == '__main__':
A = Square()
x = Variable(np.array(0)) # y = x^2 在x = 0 的斜率= 0.0
diff = numerical_diff(A, x)
print(diff)
x = Variable(np.array(2)) # y = x^2 在x = 0 的斜率= 4.0
diff = numerical_diff(A, x)
print(diff)
输出结果
C:\Python\Python39-32\python.exe D:/pyworkspace/dezero-01/step04.py
0.0
4.000000000004
3.复合函数的导数
我们考虑复合函数求导,我们看下函数y= (exp(x^2))^2的导数怎么计算dy/dx ,代码
def f(x):
A = Square()
B = Exp()
C = Square()
return C(B(A(x)))
# 使用
# 复合函数求导
x = Variable(np.array(0.5))
dy = numerical_diff(f, x)
print(dy)
执行结果:
3.2974426293330694
上面的代码,由于在python的特点,函数Function 也是对象,所以可以传递给其函数。上述结果说明,在x=0.5 处,x变化一点点,就会导致 y 变化3.297倍,这就是斜率,变化率。
现在我们已经成功实现了"自动"求导 。 只要用代码来定义要完成的计 算(例子函数 f) ,程序就会自动求山导数。使用此方法,要函数可微,无论复杂的函数都可以计算出来。但是数值微分计算存在一定的问题。
4.数值微分存在的问题
首先,数值微分结果存在误差(主要为精度丢失)。多数case 下,误差很小,有些case 误差很大。中心差分近似计算方法计算的是同数量级直之间的差,但是由于精度丢失,会出现计算结果出现位数减少的情况。
举例:
1.234 - 1.233 = 0.001 有效位数为1位
本来可能是 1.234 - 1.233... = 0.001434... 之类的结果。误差也会发生在数值微分差值计算中。
其次,数值微分一个严重的问题是,计算成本高。在求多个变量是,程序需要计算每个变量的导数。有些神经网络包括几百万个参数以上变量,通过数值微分计算不现实。
解决上面问题,我们需要 反向传播。
数值微分可以轻松实现,并能计算出大体正确的数值。 而反向传播是一 种复杂的算法,实现时容易出现 bug。
可以使用数值微分法计算处的结果检验反向传播的实现是否正确。这种方法叫做 梯度检验(gradient checking)
下一节,我们介绍反向传播算法相关理论及实现。
5.项目代码
'''
step03.py
函数的连续调用
'''
import numpy as np
from step01 import *
from step02_1 import *
from step03 import *
def numerical_diff(f,x,eps=1e-4):
x0 = Variable(x.data -eps)
x1 = Variable(x.data +eps)
y0= f(x0)
y1 = f(x1)
return (y1.data -y0.data)/(2*eps)
def f(x):
A = Square()
B = Exp()
C = Square()
return C(B(A(x)))
if __name__ == '__main__':
A = Square()
x = Variable(np.array(0)) # y = x^2 在x = 0 的斜率= 0.0
diff = numerical_diff(A, x)
print(diff)
x = Variable(np.array(2)) # y = x^2 在x = 0 的斜率= 0.0
diff = numerical_diff(A, x)
print(diff)
# 复合函数求导
x = Variable(np.array(0.5))
dy = numerical_diff(f, x)
print(dy)
输出结果:
C:\Python39-32\python.exe D:/pyworkspace/dezero-01/step04.py
0.0
4.000000000004
3.2974426293330694
我们可以看到 y=x^2 平方函数在x=0 处的导数是0,通过高中数学知识,我们知道此结果是正确的。
6.总结
本节介绍数值微分概念及计算,以及分析了数值微分的缺点。