学深度学习-预备知识4-微积分
大约2500年前,古希腊人通过将多边形分成小三角形,再把这些三角形的面积加起来,找到了计算多边形面积的方法。为了计算像圆这种曲线形状的面积,他们发明了“内接多边形”的方法:在曲线内画一个多边形,随着边数越来越多,多边形就越来越接近圆。这种方法叫做逼近法。
image.png
实际上,逼近法就是积分的雏形。几千年后,另一种微积分分支——微分,也被发明了。微分主要用于解决优化问题,简单来说,就是找到让事情做到最好的方法。在深度学习中,优化问题随处可见。
在深度学习里,我们通过“训练”模型,不断调整它的参数,让模型在看到更多数据后表现得越来越好。通常,表现更好意味着我们在最小化损失函数,也就是一个用来衡量模型表现有多差的分数。最终,我们希望模型在从未见过的数据上也能有良好表现,但训练过程只能让模型适应已知数据。
因此,训练模型可以分为两个核心问题:
- 优化:通过调整模型参数,使它更好地拟合已有数据;
- 泛化:通过数学理论和经验,确保模型在未见过的数据上也能有好表现。
为了更好理解后续的优化问题和方法,本文提供了一个简明的微分基础教程,帮助读者快速掌握深度学习中的常用微分知识。
1. 导数和微分
我们首先讨论如何计算导数,这是几乎所有深度学习优化算法的核心步骤。在深度学习中,通常选择对于模型参数可微的损失函数。简单来说,对于每个参数,我们希望知道当这个参数增加或减少一个极小的量时,损失函数的变化速度。
假设我们有一个函数 𝑓(𝑥),其输入和输出都是标量。如果这个函数的导数存在,那么导数的定义可以用下面的公式表示:
1753060654914.png
如果这个极限存在,我们就说 𝑓(𝑥)在 𝑥 处是可微的。若函数在某个区间的每个点上都是可微的,则称该函数在该区间内是可微的。导数 𝑓′(𝑥)可以解释为 𝑓(𝑥)在 𝑥 处的瞬时变化率,即当 ℎ 很小时,𝑓(𝑥)随着 𝑥 的变化速度。
1.1 实验:计算导数
为了更好地理解导数,假设我们定义一个简单的函数:f(x)=3x^2−4x
def f(x):
return 3 * x ** 2 - 4 * x
现在,我们通过减小 ℎ 值,来计算数值导数。我们将使用以下代码进行实验:
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
h *= 0.1
运行结果如下:
h=0.10000, numerical limit=2.30000
h=0.01000, numerical limit=2.03000
h=0.00100, numerical limit=2.00300
h=0.00010, numerical limit=2.00030
h=0.00001, numerical limit=2.00003
从上面的结果可以看出,随着 h越来越小,数值结果越来越接近 2,这说明当
h→0时,导数的精确值为 2。
1.2 导数符号
对于导数,以下符号是等价的:
1753060994265.png
其中,d/dx,df/dx都是微分运算符,表示对 𝑥 求导数。为了求出常见函数的导数,可以使用以下规则:
- 常数的导数为 0。
-
幂函数的导数:
1753061109722.png
(其中 𝑛 是实数)。
1.3 微分法则
若 𝑓(𝑥) 和 𝑔(𝑥) 都是可微的函数,且 𝑐 是常数,那么以下法则适用:
1753061689182.png
1.4 导数的可视化
我们需要定义几个函数,保存在一个独立的包d2l中,以后无须重新定义就可以直接调用它们(例如,d2l.use_svg_display())。
import sys
from matplotlib import pyplot as plt
from matplotlib_inline import backend_inline
# 设置中文字体(SimHei 是黑体字)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号 '-' 显示成方块的问题
d2l = sys.modules[__name__]
def use_svg_display(): # @save
"""使用svg格式在Jupyter中显示绘图"""
backend_inline.set_matplotlib_formats('svg')
# 我们定义set_figsize函数来设置图表大小。
# 注意,这里可以直接使用d2l.plt,因为导入语句 from matplotlib import pyplot as plt已标记为保存到d2l包中。
def set_figsize(figsize=(3.5, 2.5)): # @save
"""设置matplotlib的图表大小"""
use_svg_display()
d2l.plt.rcParams['figure.figsize'] = figsize
# 下面的set_axes函数用于设置由matplotlib生成图表的轴的属性。
def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
"""设置matplotlib的轴"""
axes.set_xlabel(xlabel)
axes.set_ylabel(ylabel)
axes.set_xscale(xscale)
axes.set_yscale(yscale)
axes.set_xlim(xlim)
axes.set_ylim(ylim)
if legend:
axes.legend(legend)
axes.grid()
# 通过这三个用于图形配置的函数,定义一个plot函数来简洁地绘制多条曲线,因为我们需要在整个书中可视化许多曲线。
def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None,
ylim=None, xscale='linear', yscale='linear',
fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None):
"""绘制数据点"""
if legend is None:
legend = []
set_figsize(figsize)
axes = axes if axes else d2l.plt.gca()
# 如果X有一个轴,输出True
def has_one_axis(X):
return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list)
and not hasattr(X[0], "__len__"))
if has_one_axis(X):
X = [X]
if Y is None:
X, Y = [[]] * len(X), X
elif has_one_axis(Y):
Y = [Y]
if len(X) != len(Y):
X = X * len(Y)
axes.cla()
for x, y, fmt in zip(X, Y, fmts):
if len(x):
axes.plot(x, y, fmt)
else:
axes.plot(y, fmt)
set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
plt.show()
为了更好地理解导数的几何意义,我们可以绘制函数 f(x) 及其在 x=1处的切线y=2x−3。切线的斜率就是该点的导数值。使用 matplotlib 绘图库,我们可以创建以下可视化:
import numpy as np
import d2l
def f(x):
return 3 * x ** 2 - 4 * x
x = np.arange(0, 3, 0.1)
d2l.plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line(切线) (x=1)'])
image.png
2. 偏导数
到目前为止,我们只讨论了仅含一个变量的函数的微分。在深度学习中,函数通常依赖于许多变量。因此,我们需要将微分的思想推广到 多元函数 ( multivariate function ) 上。
1753064883291.png
3. 梯度
我们可以连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量。具体而言,设函数 f 的输入是一个 𝑛 维向量 x=[x1,x2,…,xn]T,并且输出是一个标量。函数 𝑓相对于 𝑥 的梯度是一个包含 𝑛 个偏导数的向量:
1753065213839.png
4. 链式法则
然而,上面方法可能很难找到梯度。这是因为在深度学习中,多元函数通常是复合(composite)的,所以难以应用上述任何规则来微分这些函数。幸运的是,链式法则可以被用来微分复合函数。
让我们先考虑单变量函数。假设函数 y=f(u)和 u=g(x)都是可微的,根据链式法则:
1753065279773.png
现在考虑一个更一般的场景,即函数具有任意数量的变量的情况。 假设可微分函数 y 有变量 u1,u2,...,um其中每个可微分函数 ui都有变量 x1,x2,...,xn。 注意,y 是 x1,x2,...,xn的函数。对于任意i=1,2,...,n,链式法则给出:
1753065359500.png
雅可比矩阵(Jacobian Matrix)是向量值函数的一阶偏导数矩阵,它描述了一个向量函数在某一点处的局部变化特性。简单来说,它可以看作是对多元函数的偏导数进行组织和排列的一种方式。
定义
1753065402402.png
雅可比矩阵的作用
- 线性近似:雅可比矩阵可以用来线性近似非线性函数的局部变化。函数在某一点的雅可比矩阵描述了它在该点附近的线性近似。
- 牛顿法:在多元非线性方程求解中,牛顿法依赖于雅可比矩阵来更新解的近似值。
- 机器学习与神经网络:在反向传播算法中,雅可比矩阵用于计算梯度,以调整模型的参数。
4.1 平方函数的复合
设 f(x)=(2x+3)^2,我们可以将其看作 f(x) = u^2,其中 u=2x+3。这是一个复合函数,外层是平方函数,内层是线性函数。
推导过程:
1753065502705.png
4.2 三角函数的复合
设 f(x)=sin(3x),我们可以将其看作 f(x)=sin(u),其中 u=3x。这是一个三角函数的复合。
推导过程:
1753065569815.png
4.3 指数函数的复合
设 f(x)=e ^5x,我们可以将其看作 f(x)=e ^u ,其中 u=5x。这是一个指数函数的复合。
推导过程:
1753065639451.png
4.4 向量链式法则
1753065666195.png
推导过程
1753065699383.png
1753065723099.png