文章目录
- 摘要
- Abstract
- 一、计算雅各比行列式
- 二、向量微分
- 三、矩阵微分
- 总结
摘要
本文介绍了在PyTorch中进行向量微分、矩阵微分以及计算雅各比行列式的方法。通过对自动微分(Autograd)功能的讲解,展示了如何轻松实现复杂的数学运算,如向量和矩阵的导数计算,以及通过雅各比矩阵和雅各比行列式对函数的线性变换特性进行分析。
Abstract
This article introduces methods for performing vector differentiation, matrix differentiation, and computing Jacobian determinants in PyTorch. Through an explanation of the automatic differentiation (Autograd) functionality, it demonstrates how to easily implement complex mathematical operations, such as calculating derivatives of vectors and matrices, and analyzing the properties of functions’ linear transformations via Jacobian matrices and Jacobian determinants.
一、计算雅各比行列式
需要传入函数和函数的输入
import torch
from torch.autograd.functional import jacobiandef func(x):return x.exp().sum(dim=1)x = torch.randn(2, 3)
y = func(x)print(x)
'''
tensor([[-0.2497, -0.8842, 0.6314],[-0.0687, -1.5360, 1.4695]])
'''
print(y) # tensor([3.0724, 5.4959])# exp(-0.2497)+exp(-0.8842)+exp(0.6314)=3.0724# exp(-0.0687)+exp(-1.5360)+exp(1.4695)=5.4959print(jacobian(func, x))
输出结果是:
tensor([[-0.2497, -0.8842, 0.6314],[-0.0687, -1.5360, 1.4695]])
tensor([3.0724, 5.4959])
tensor([[[0.7791, 0.4130, 1.8803],[0.0000, 0.0000, 0.0000]],[[0.0000, 0.0000, 0.0000],[0.9336, 0.2152, 4.3470]]])
暂记: y 1 = e x p ( − 0.2497 ) + e x p ( − 0.8842 ) + e x p ( 0.6314 ) = 3.0724 y1=exp(-0.2497)+exp(-0.8842)+exp(0.6314)=3.0724 y1=exp(−0.2497)+exp(−0.8842)+exp(0.6314)=3.0724
y 2 = e x p ( − 0.0687 ) + e x p ( − 1.5360 ) + e x p ( 1.4695 ) = 5.4959 y2=exp(-0.0687)+exp(-1.5360)+exp(1.4695)=5.4959 y2=exp(−0.0687)+exp(−1.5360)+exp(1.4695)=5.4959
输出结果的雅可比行列式中,第一行分别为: ∂ y 1 / ∂ x 11 , ∂ y 1 / ∂ x 12 , ∂ y 1 / ∂ x 13 ∂y_1/∂x_{11},∂y_1/∂x_{12},∂y_1/∂x_{13} ∂y1/∂x11,∂y1/∂x12,∂y1/∂x13,即分别为:exp(-0.2497),exp(-0.8842),exp(0.6314)。
第二行为: ∂ y 1 / ∂ x 21 , ∂ y 1 / ∂ x 22 , ∂ y 1 / ∂ x 23 ∂y_1/∂x_{21},∂y_1/∂x_{22},∂y_1/∂x_{23} ∂y1/∂x21,∂y1/∂x22,∂y1/∂x23。依次类推,剩下的是 y 2 y_2 y2对x的偏导
二、向量微分
import torch
from torch.autograd.functional import jacobiana = torch.randn(3)
def func(x):return x+ax = torch.randn(3, requires_grad=True) # x需要求梯度
y = func(x)print(y)y.backward(torch.ones_like(y))
print(x.grad)
输出结果:
tensor([-0.4841, -0.0149, -2.0035], grad_fn=<AddBackward0>)
tensor([1., 1., 1.])
backward()
中为什么要传入torch.ones_like(y)
?
backward默认前面的是一个标量,从输出结果上来看 y 并不是标量。因此,我们假定存在一个标量 l l l, 并假定 l l l对y的偏导数是全1的张量,即torch.ones_like(y). 根据链式法则,即 ∂ l / ∂ x = ∂ l / ∂ y ∗ ∂ y / ∂ x ∂l/∂x=∂l/∂y*∂y/∂x ∂l/∂x=∂l/∂y∗∂y/∂x
使用雅各比进行验证
print(torch.ones_like(y) @ jacobian(func, x))
# 前面是 偏l/偏y,默认是全1的张量,即v
输出结果为:
tensor([1., 1., 1.])
与上面使用 backward()
求得的 x.grad结果相同
三、矩阵微分
a与b分别为两个矩阵,y是a与b矩阵相乘的结果。目标是分别求y对a、y对b的微分。
import torch
from torch.autograd.functional import jacobiana = torch.randn(2, 3, requires_grad=True)
b = torch.randn(3, 2, requires_grad=True)y = a@bprint(y)
矩阵a和b:
tensor([[-0.1741, -0.5847, -0.4218],[-0.2234, 0.1794, 0.3404]], requires_grad=True)
tensor([[ 0.6367, 1.6814],[-1.6992, 0.0563],[-0.1178, -0.9835]], requires_grad=True)
y的值即输出结果为:
tensor([[ 0.9323, 0.0892],[-0.4873, -0.7004]], grad_fn=<MmBackward0>)
使用backward
进行计算:
y.backward(torch.ones_like(y)) # y不是标量,需要传入全1且与y形状相同的张量
print(a.grad)
print(b.grad)
得到a的梯度和b的梯度分别为:
tensor([[ 2.3181, -1.6430, -1.1012], #两行相同[ 2.3181, -1.6430, -1.1012]])
tensor([[-0.3975, -0.3975], # 两列相同[-0.4053, -0.4053],[-0.0814, -0.0814]])
使用雅各比进行验证
如果y对a进行偏微分,那么b相当于是一个常数矩阵。固定b的值,建立关于a的函数func(a)
,因为a@b
是a的每一行与b的每一列进行相乘,所以这里先取a的第一行元素a[0]。
def func1(a):return a@b
print("雅可比行列式,a@b对a")
print(jacobian(func1, a))print(func1(a[0]))
# 前面是 偏l/偏y,默认是全1的张量,即v
print(torch.ones_like(func1(a[0])) @ jacobian(func1, a[0]))
输出结果如下:
tensor([0.9323, 0.0892], grad_fn=<SqueezeBackward4>)
tensor([ 2.3181, -1.6430, -1.1012])
输出结果的第2行,与上面矩阵y对a的微分的第1行结果相同。
同理,我们使用a[1]进行验证:
print(func1(a[1]))
# 前面是 偏l/偏y,默认是全1的张量,即v
print(torch.ones_like(func1(a[1])) @ jacobian(func1, a[1]))
输出结果:
tensor([-0.4873, -0.7004], grad_fn=<SqueezeBackward4>)
tensor([ 2.3181, -1.6430, -1.1012])
如果y对b进行偏微分,那么a相当于是一个常数矩阵。固定a的值,建立关于b的函数func(b)
。因为a@b
是a的每一行与b的每一列进行相乘,所以这里取b的第一列元素b[:, 0]和第二列元素b[:, 1]。
def func2(b):return a@bprint(func2(b[:, 0]))
print(torch.ones_like(func2(b[:, 0])) @ jacobian(func2, b[:, 0]))print(func2(b[:, 1]))
print(torch.ones_like(func2(b[:, 1])) @ jacobian(func2, b[:, 1]))
结果如下:
tensor([ 0.9323, -0.4873], grad_fn=<MvBackward0>)
tensor([-0.3975, -0.4053, -0.0814])
tensor([ 0.0892, -0.7004], grad_fn=<MvBackward0>)
tensor([-0.3975, -0.4053, -0.0814])
可以看到第2、4行的输出结果,分别与上面矩阵y对b的微分的第1、2列结果相同。
总结
PyTorch可以方便地实现各种数学操作,尤其是微分和梯度计算。通过掌握如何计算向量和矩阵的导数、雅各比矩阵以及雅各比行列式,我们可以在模型优化和误差传播中获得更深的洞察。