可微分计算(Differentiable Computing)或可微分编程(Differentiable Programming)是近年来计算机科学和科学计算领域最重要的范式转变之一。它的核心思想是:将计算机程序变成可微分的,从而可以通过梯度下降等优化方法,从输出结果反推并优化输入参数或程序内部的逻辑。

不用想就知道,这种观念的直接来源是深度学习,但其应用领域却足以泛化到整个科学计算和工程领域。

一元标量函数

PyTorch支持可微分计算,只需在创建张量时,开启【requires_grad】,即可自动计算梯度。例如,对于 y = x 2 y=x^2 y=x2而言, d y d x = 2 x \frac{\mathbf dy}{\mathbf dx}=2x dxdy=2x。现令 x = 3 x=3 x=3,则其PyTorch可以依次计算出

import torch

x = torch.tensor(3.0, requires_grad=True)
y = x ** 2
print(f"y 的值: {y.item()}")    # y 的值: 9.0

y.backward()
print(f"x 的梯度 (dy/dx): {x.grad.item()}")    # x 的梯度 (dy/dx): 6.0

多元函数

下面考虑多元函数 f ( x , y ) = x 2 sin ⁡ y + e x y f(x,y) = x^2\sin y+e^{xy} f(x,y)=x2siny+exy,其偏导数为

∂ f ∂ x = 2 x sin ⁡ y + y e x y ∂ f ∂ y = x 2 cos ⁡ y + x e x y \begin{aligned} \frac{\partial f}{\partial x}&=2x\sin y+ye^{xy}\\ \frac{\partial f}{\partial y}&=x^2\cos y+xe^{xy}\\ \end{aligned} xfyf=2xsiny+yexy=x2cosy+xexy

x = 1 , y = π 2 x=1, y=\frac{\pi}{2} x=1,y=2π时,

f = 1 + e π 2 ≈ 5.81 f x = 2 + π 2 e π 2 ≈ 9.56 f y = e π 2 ≈ 4.81 \begin{aligned} f&=1+e^\frac{\pi}{2}\approx5.81\\ f_x&=2+\frac{\pi}{2}e^\frac{\pi}{2}\approx9.56\\ f_y&=e^\frac{\pi}{2}\approx4.81 \end{aligned} ffxfy=1+e2π5.81=2+2πe2π9.56=e2π4.81

PyTorch的自动微分仍旧可以完美运行

x = torch.tensor(1.0, requires_grad=True)
y = torch.tensor(torch.pi / 2, requires_grad=True)

f = (x**2) * torch.sin(y) + torch.exp(x * y)
f.backward()

print(f"df/dx: {x.grad.item():.4f}")    # df/dx: 9.5563
print(f"df/dy: {y.grad.item():.4f}")    # df/dy: 4.8105

一元数组函数

回到 y = x 2 y=x^2 y=x2,如果 x x x是个数组,那么在Python的计算规则中, y y y也是同样维度的数组。此时把求导操作视作 ∂ y ∂ x \frac{\partial y}{\partial x} xy,那么形式上的展开差不多是 ∂ ( y 1 , y 2 , ⋯   ) ∂ ( x 1 , x 2 , ⋯   ) \frac{\partial (y_1, y_2, \cdots)}{\partial(x_1, x_2,\cdots)} (x1,x2,)(y1,y2,),有些意义不明,感觉最后求出来的是个矩阵。

但是,如果 y i y_i yi求和,那么就会得到 s = ∑ i y i ( x i ) s=\sum_i y_i(x_i) s=iyi(xi),此时 ∂ s ∂ ( x 1 , x 2 , ⋯   ) \frac{\partial s}{\partial(x_1, x_2,\cdots)} (x1,x2,)s的意义却相对明确,即 s x i = ∂ s ∂ x i = ∂ y i ∂ x i s_{x_i} = \frac{\partial s}{\partial x_i}=\frac{\partial y_i}{\partial x_i} sxi=xis=xiyi。按照这个逻辑,可以用PyTorch继续做反向传播,示例如下

x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x ** 2  # y 是一个向量 [1, 4, 9]

# 注意:直接 y.backward() 会报错!

loss = y.sum() 
loss.backward()

print(f"x 的梯度: {x.grad}")    # x 的梯度: tensor([2., 4., 6.])

更多推荐