Notes
深度学习
微积分

微积分

在深度学习中,我们经常会遇到微积分。微积分对我们解决深度学习中的问题非常有帮助。在这一节中,我们将简要介绍微积分的一些基本概念。

导数

导数是指函数相对于自变量的变化率。给定函数f(x)f(x),其导数f(x)f'(x)是另一个函数,定义为

f(x)=limh0f(x+h)f(x)h.f'(x) = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}.

我们也可以将 f'(x) 解释为 x 处的切线斜率。如果 f'(x) > 0,意味着 f(x) 在 x 处是递增的。如果 f'(x) < 0,意味着 f(x) 在 x 处是递减的。如果 f'(x) = 0,意味着 f(x) 在 x 处达到了局部最小值或最大值。

常用的一些标量的导数包括:

(xn)=nxn1(x^n)' = nx^{n-1}
(ex)=ex(e^x)' = e^x
(ax)=axlog(a)(a^x)' = a^x \log(a)
(log(x))=1/x(\log(x))' = 1/x
(sin(x))=cos(x)(\sin(x))' = \cos(x)
(cos(x))=sin(x)(\cos(x))' = -\sin(x)

若 y=u+v,则:

dydx=dudx+dvdx\frac{dy}{dx} = \frac{du}{dx} + \frac{dv}{dx}

若 y=uv,则:

dydx=udvdx+vdudx\frac{dy}{dx} = u\frac{dv}{dx} + v\frac{du}{dx}

若 y=f(u)且u=g(x),则:

dydx=dydududx\frac{dy}{dx} = \frac{dy}{du}\frac{du}{dx}

有一种特殊情况,即亚导数。

偏导数

偏导数是多元函数相对于其各个变量的导数。给定函数f(x)f(\mathbf{x}),其中x=[x1,x2,,xd]\mathbf{x} = [x_1, x_2, \ldots, x_d]^\top是一个具有dd个变量的向量。函数f(x)f(\mathbf{x})相对于xix_i的偏导数记作fxi\frac{\partial f}{\partial x_i}。偏导数的计算方式和单变量函数的导数类似,即将其他变量视为常数进行求导。

梯度

在深度学习中,梯度(Gradient)是指一个多变量函数在某一点处的导数或者偏导数。它表示函数在该点处的变化率和方向。梯度通常用向量表示,包含每个输入变量的偏导数。

对于一个具有多个参数的函数,梯度告诉我们如何调整这些参数,以使函数值增加最快。在深度学习中,我们通常使用梯度来更新模型的参数,以便最小化(或最大化)一个损失函数。

假设有一个函数 f(x1, x2, ..., xn),它具有 n 个参数,那么该函数在某个点 (x1*, x2*, ..., xn*) 的梯度是一个向量,记作∇f(x1*, x2*, ..., xn*)。这个向量的每个分量都是对应参数的偏导数,表示函数在每个参数方向上的变化率。

链式法则是微积分中的一个重要概念,它用于计算复合函数的导数。在深度学习中,我们经常需要计算复合函数的导数。假设函数 f(x) 和 g(x) 都是可导的,那么复合函数 f(g(x)) 也是可导的,且其导数为 f'(g(x))g'(x)。

根据链式法则:

dydx=dydududx\frac{dy}{dx} = \frac{dy}{du}\frac{du}{dx}

现在让我们考虑函数具有任意数量的输入的情况。假设函数 f(x1, x2, ..., xn) 和每个输入 xi 都是可导的,那么复合函数 f(g1(x1, x2, ..., xn), g2(x1, x2, ..., xn), ..., gm(x1, x2, ..., xn)) 也是可导的。其导数为:

fxi=j=1mfgjgjxi\frac{\partial f}{\partial x_i} = \sum_{j=1}^m \frac{\partial f}{\partial g_j} \frac{\partial g_j}{\partial x_i}

示例:求
f(x)=3x12+5ex2f(x) = 3x_1^2 + 5e^{x_2} 的梯度。

解:

fx1=6x1\frac{\partial f}{\partial x_1} = 6x_1

fx2=5ex2\frac{\partial f}{\partial x_2} = 5e^{x_2}

所以梯度为:

f=[6x1,5ex2]\nabla f = [6x_1, 5e^{x_2}]

💡

GPT 解释

想象你站在一个山谷的某个位置,而你的目标是站在山谷的最高点。你不能看见整个地形,但你想找到一种方法,可以告诉你朝哪个方向迈出步伐才能最快地到达山顶。这时,梯度就可以用来帮助你。

在这个例子中,地形的曲率就像函数的梯度。如果你站在山谷的某个点,梯度向量会指示你站在哪个方向上山的坡度最陡。沿着梯度的方向走,你将会更快地上升。

梯度下降的过程就像你盲目地尝试朝着更陡峭的方向迈步,逐步接近山顶。梯度的大小表示坡度的陡峭程度,而梯度的方向告诉你应该往哪个方向走。

总体来说,梯度就像是你站在地形上时的感觉,告诉你哪个方向是最陡的,从而帮助你找到最快的上升路径。在深度学习中,我们使用梯度来指导模型参数的更新,以最小化损失函数,就像在地形中找到最高点一样。

自动微分

手动求导是一项繁琐且容易出错的工作。幸运的是,我们可以使用自动微分(Automatic Differentiation)来自动地计算导数。自动微分是一种计算机程序用来对其他程序进行微分的技术,它可以精确地计算导数。

如我们相对函数:

y=2xTxy = 2x^T x

求导,我们可以使用自动微分来计算导数。

import torch
 
x = torch.arange(4.0)
x 
# tensor([0., 1., 2., 3.])
x.requires_grad_(True) # 等价于 `x = torch.arange(4.0, requires_grad=True),表示我们需要对 x 求导
x.grad # 此时为 None
y = 2 * torch.dot(x, x)
y
# tensor(28., grad_fn=<MulBackward0>) # 0*0 + 1*1 + 2*2 + 3*3 = 14*2 = 28

接下来,我们可以通过调用反向传播函数来自动计算 y 关于 x 每个分量的梯度。

y.backward()
x.grad
# tensor([ 0.,  4.,  8., 12.])

函数 y = 2x^T x 关于 x 的梯度应为 4x,因为我们可以将其看作是 y = 2sum(xi^2)。因此,其梯度就是 4x。

我们来验证一下:

x.grad == 4 * x
# tensor([True, True, True, True])

因此,让我们总结一下自动微分的过程:

# 1. 为需要求导的变量分配内存
x = torch.arange(4.0)
# 2. 通过调用 `requires_grad` 函数来告诉 PyTorch 我们需要对 x 求导
x.requires_grad_(True)
# 3. 通过调用函数来计算 y
y = 2 * torch.dot(x, x)
# 4. 通过调用反向传播函数来自动计算 y 关于 x 每个分量的梯度
y.backward()
# 5. 梯度就存储在 x.grad 中
x.grad

如果要对 x 求其他函数的导数,我们需要先调用 x.grad.zero_() 来清除之前的梯度。

x.grad.zero_()

如果 y 不是一个标量,那么导数是一个矩阵。我们可以先进行求和,然后再调用反向传播函数。(为什么要先求和?因为 PyTorch 要求梯度的输入是一个标量。)

x.grad.zero_()
y = x * x   # y 是一个长度为 4 的向量
y.sum().backward()
x.grad
# tensor([0., 2., 4., 6.])

我们可以将某些计算移动到记录的计算图之外。

x.grad.zero_()
y = x * x
u = y.detach()  # 将 y 的值分离出来,相当于把 y 当作常数
z = u * x
z.sum().backward()
x.grad == u
# tensor([True, True, True, True])