目录(注意本文jupyterlab编写)
简介
在进行梯度下降时,需要损失函数对权重偏移等系数进行偏导。偏导的主要方法有手动微分、有限差分近似、前向模式自动微分、反向模式自动微分。
手动微分:拿出纸笔,用微积分的知识手动推导。
有限差分近似:根据导数定义,当x无限接近x0时,通过x0点和函数上另一个点x的直线的斜率的极限。例如 {h(x0+eps)-h(x0)} / eps。缺陷:结果不够精确。
前向模式自动微分:建立原函数计算图,再从原图进行符号微分产生另一个计算图(即导数的计算图,可以优化)。缺陷:1000个参数至少要1000次遍历计算图。
反向模式自动微分:它首先以向前的方向(从输入到输出)遍历原图计算每个节点的值。然后再反向(从输出到输入),计算所有偏导数,只需要遍历两次计算图就可以计算所有参数。
反向模式自动微分(见图计算过程)
import matplotlib.pyplot as plt
前向(从输入到输出),计算每个节点的值。(最下面方形为变量,圆形为操作。)
plt.figure(figsize=(20,18))
plt.imshow(plt.imread('./反向模式自动微分前向.jpg'))
反向(从输出到输入),反向偏导依赖于链式规则
plt.figure(figsize=(20,18))
plt.imshow(plt.imread('./反向模式自动微分反向.jpg'))
import tensorflow as tf
tensorflow实例
在
tf.GradientTape()
中,会记录函数f
的计算过程,记录完,用tape.gradient()
一次可对所有变量进行偏导
注意:tape.gradient()
使用一次后即擦除,不能再使用。(也可以在tf.GradientTape()
中设置persistent=True
使其具有持久性,每次使用完后要删除以释放资源。)
注意:正常无法对设置为tf.constant()
的常数求导,除非在with
作用域下的开头设置tape.watch()
def f(x,y):
return x**2*y+y+2
x=tf.Variable(3.)
y=tf.Variable(4.)
with tf.GradientTape() as tape:
loss=f(x,y)
gradients=tape.gradient(loss,[x,y])
gradients
[<tf.Tensor: shape=(), dtype=float32, numpy=24.0>,
<tf.Tensor: shape=(), dtype=float32, numpy=10.0>]
def my_softplus(x):
return tf.math.log(tf.exp(x)+1.0)
差错
由于有浮点数精度误差,会导致一些数值上的困难。但是我们发现my_softplus的导数是1/(1+1/exp(x)),在数值上稳定。我们可以用
@tf.custom_gradient
来修饰函数,该函数既返回正常输出,又返回计算导数的函数。
注意:返回的函数是要计算接收到目前为止反向传播的梯度,知道softplus函数,根据链式规则,应该将它们乘以该函数的梯度
x=tf.Variable([100.0])
with tf.GradientTape() as tape:
loss=my_softplus(x)
gradients=tape.gradient(loss,[x])
gradients
[<tf.Tensor: shape=(1,), dtype=float32, numpy=array([nan], dtype=float32)>]
@tf.custom_gradient
def my_best_softplus(x):
exp=tf.exp(x)
def my_softplus_gradients(grad):
return grad/(1.0+1.0/exp)
return tf.math.log(exp+1.0),my_softplus_gradients
with tf.GradientTape() as tape:
loss=my_best_softplus(x)
gradients=tape.gradient(loss,[x])
gradients
[<tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.], dtype=float32)>]