微积分
如下图所示,内接多边形的等长边越多,就越接近圆。 这个过程也被称为逼近法(method of exhaustion)。
事实上,逼近法就是**积分(integral calculus)**的起源
微积分的另一支,**微分(differential calculus)**被发明出来。
在微分学最重要的应用是优化问题,即考虑如何把事情做到最好,这种问题在深度学习中是无处不在的。
在深度学习中,我们“训练”模型,不断更新它们,使它们在看到越来越多的数据时变得越来越好。
通常情况下,变得更好意味着最小化一个损失函数(loss function)
真正关心的是生成一个模型,它能够在从未见过的数据上表现良好。
但“训练”模型只能将模型与我们实际能看到的数据相拟合, 因此,我们可以将拟合模型的任务分解为两个关键问题:
- 优化(optimization):用模型拟合观测数据的过程;
- 泛化(generalization):生成出有效性超出用于训练的数据集本身的模型。
导数和微分
这是几乎所有深度学习优化算法的关键步骤。
在深度学习中,通常选择对于模型参数可微的损失函数。
简而言之,对于每个参数, 如果把这个参数增加或减少一个无穷小的量,我们可以知道损失会以多快的速度增加或减少,
假设我们有一个函数𝑓:Rn→R,其输入和输出都是标量。 (如果𝑓的导数存在,这个极限被定义为)
(
)
如果𝑓′(𝑎)存在,则称𝑓在𝑎处是可微(differentiable)的。
如果𝑓在一个区间内的每个数上都是可微的,则此函数在此区间中是可微的。
可以将图中的导数𝑓′(𝑥)解释为𝑓(𝑥)相对于𝑥的瞬时(instantaneous)变化率。 所谓的瞬时变化率是基于𝑥中的变化ℎ,且ℎ接近0。
为了更好地解释导数,举个例子
定义𝑢=𝑓(𝑥)=3𝑥2−4𝑥如下:
%matplotlib inline
import numpy as np
import d2l
from IPython import display
from d2l import torch as d2ldef f(x):return 3 * x ** 2 - 4 * x
通过令𝑥=1并让ℎ接近0
的数值结果接近2**)。 稍后会看到,当𝑥=1时,导数𝑢′是2。
def numerical_lim(f, x, h):return (f(x + h) - f(x)) / hh = 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
为了对导数的这种解释进行可视化,我们使用matplotlib(这是一个Python中的绘图库)
要配置matplotlib
生成图形的属性,需要定义几个函数。
在下面输出svg图表以获得清晰的图像。
注释#@save
是一个特殊的标记,会将对应的函数、类或语句保存在d2l
包中。 因此,以后无须重新定义就可以直接调用它们(例如,d2l.use_svg_display()
)。
def use_svg_display(): #@save"""使用svg格式在Jupyter中显示绘图"""display.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
生成图表的轴的属性。
#@save
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
函数来简洁地绘制多条曲线,方便以后使用
#@save
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有一个轴,输出Truedef 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), Xelif 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)
现在可以绘制函数𝑢=𝑓(𝑥)及其在𝑥=1处的切线𝑦=2𝑥−3, 其中系数2是切线的斜率。
x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
偏导数
目前只讨论了仅含一个变量的函数的微分。
在深度学习中,函数通常依赖于许多变量。 因此,需要将微分的思想推广到多元函数(multivariate function)上。
设𝑦=𝑓(𝑥1,𝑥2,…,𝑥𝑛)是一个具有𝑛个变量的函数。 𝑦关于第𝑖个参数𝑥𝑖的偏导数(partial derivative)为:
梯度
- 梯度指向的是值变化最大的方向
可以连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量。
具体而言,设函数𝑓:Rn→R的输入是 一个𝑛维向量𝐱=[𝑥1,𝑥2,…,𝑥𝑛]⊤,并且输出是一个标量。
函数𝑓(𝐱)相对于𝐱的梯度是一个包含𝑛个偏导数的向量:
其中∇𝐱𝑓(𝐱)通常在没有歧义时被∇𝑓(𝐱)取代。
假设𝐱为𝑛维向量,在微分多元函数时经常使用以下规则:
- 对于所有𝐀∈Rm×n,都有∇𝐱𝐀𝐱=𝐀⊤
- 对于所有𝐀∈Rn×m,都有∇𝐱𝐱⊤𝐀=𝐀
- 对于所有𝐀∈Rn×n,都有∇𝐱𝐱⊤𝐀𝐱=(𝐀+𝐀⊤)𝐱
- ∇𝐱‖𝐱‖2=∇𝐱𝐱⊤𝐱=2𝐱
同样,对于任何矩阵𝐗,都有∇𝐗‖𝐗‖2𝐹=2𝐗
进一步了解梯度
梯度下降法详情
链式法则
然而,上面方法可能很难找到梯度。 这是因为在深度学习中,多元函数通常是**复合(composite)**的, 所以我们可能没法应用上述任何规则来微分这些函数。
但是链式法能够微分复合函数。
先考虑单变量函数。假设函数𝑦=𝑓(𝑢)和𝑢=𝑔(𝑥)都是可微的,根据链式法则:
函数具有任意数量的变量的情况时。 假设可微分函数𝑦有变量𝑢1,𝑢2,…,𝑢𝑚
其中每个可微分函数𝑢𝑖都有变量𝑥1,𝑥2,…,𝑥𝑛注意,𝑦是𝑥1,𝑥2,…,𝑥𝑛的函数。 对于任意𝑖=1,2,…,𝑛链式法则给出:
总结
- 微分和积分是微积分的两个分支,前者可以应用于深度学习中的优化问题。
- 导数可以被解释为函数相对于其变量的瞬时变化率
- 梯度是一个向量,其分量是多变量函数相对于其所有变量的偏导数。
- 链式法则使我们能够微分复合函数。