张量是一种多维数组,支持在GPU上进行计算,是计算图中的节点,并且支持自动微分。
在进行运算时,如果两个张量在某个维度上的形状不匹配,那么系统会自动地在这个维度上扩展形状较小的张量,使得两个张量在这个维度上具有相同的形状。
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
print(a + b)
输出:
tensor([[0, 1],
[1, 2],
[2, 3]])
假设函数 f : R n → R f : \mathbb{R}^n \rightarrow \mathbb{R} f:Rn→R 的输入是一个 n 维向量 x = [ x 1 , x 2 , … , x n ] T x = [x_1, x_2, \ldots, x_n]^T x=[x1,x2,…,xn]T ,并且输出是一个标量。而函数 f ( x ) f(x) f(x) 相对于 x x x 的梯度是一个包含 n 个偏导数的向量:
∇ x f ( x ) = [ ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , … , ∂ f ( x ) ∂ x n ] T \nabla_x f(x) = \left[\frac{\partial f(x)}{\partial x_1}, \frac{\partial f(x)}{\partial x_2}, \ldots, \frac{\partial f(x)}{\partial x_n}\right]^T ∇xf(x)=[∂x1∂f(x),∂x2∂f(x),…,∂xn∂f(x)]T
这个梯度向量的每个元素都是函数 f f f 在对应维度上的偏导数,它描述了函数 f f f 在该点的局部变化率。在优化问题中,梯度常常被用来指示函数增长最快的方向。
假设可微函数 y y y 有变量 u 1 , u 2 , … , u m u_1, u_2, \ldots, u_m u1,u2,…,um,其中每个可微分函数 u i u_i ui 都有变量 x 1 , x 2 , … , x n x_1, x_2, \ldots, x_n x1,x2,…,xn。注意, y y y 是 x 1 , x 2 , … , x n x_1, x_2, \ldots, x_n x1,x2,…,xn 的间接函数。我们可以使用链式法则计算 y y y 相对于 x i x_i xi 的偏导数。链式法则表明:
∂ y ∂ x i = ∑ j = 1 m ∂ y ∂ u j ∂ u j ∂ x i \frac{\partial y}{\partial x_i} = \sum_{j=1}^m \frac{\partial y}{\partial u_j} \frac{\partial u_j}{\partial x_i} ∂xi∂y=j=1∑m∂uj∂y∂xi∂uj
深度学习框架可以自动计算导数:根据设计好的模型,系统会构建一个计算图。当你定义一个变量并指定它需要计算梯度时,框架会跟踪所有与该变量有关的计算。然后,当你计算一个目标值(通常是损失函数)并调用反向传播函数时,框架会沿着这些计算的路径反向传播,使用链式法则来计算每个变量的偏导数。每次调用 .backward()
时,新的梯度会加到已有的梯度上。
import torch
# 创建一个包含4个元素的张量(向量)[0., 1., 2., 3.],并设置requires_grad=True以跟踪对其的所有操作。
x = torch.arange(4.0, requires_grad=True)
# 计算y,它是x和x自身的点积乘以2。这里y是一个标量。
y = 2 * torch.dot(x, x)
# 对y进行反向传播,计算y关于x的梯度。由于y是一个标量,这等价于计算y的导数。
y.backward()
# 打印出x的梯度。由于y = 2 * x^T * x,y关于x的梯度是4 * x。
print(x.grad)
# 检查计算出的梯度是否如我们通过手动计算得出的那样,即4 * x。
print(x.grad == 4 * x)
# 清除之前计算出的梯度值。在PyTorch中,如果不手动清零,梯度会累积。
x.grad.zero_()
# 计算一个新的y,它是x的所有元素的和。
y = x.sum()
# 对新的y进行反向传播,计算关于x的梯度。
y.backward()
# 打印出新的梯度。由于y是x的和,y关于x的梯度是一个全1的向量。
print(x.grad)
输出:
tensor([ 0., 4., 8., 12.])
tensor([True, True, True, True])
tensor([1., 1., 1., 1.])