Python可微分编程初步
可微分计算(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} ∂x∂f∂y∂f=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} ∂x∂y,那么形式上的展开差不多是 ∂ ( 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=∂xi∂s=∂xi∂yi。按照这个逻辑,可以用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.])
更多推荐
所有评论(0)