torch.autograd为用户方便使用,而专门开发的一套自动求导引擎,可以根据输入和前向传播过程自动构建计算图,并执行反向传播。
计算图:现代深度学习框架如PyTorch和TensorFlow等的核心,其为高效自动求导算法——反向传播提供理论支持。
autograd包为张量上的所有操作提供了自动求导。它是一个在运行时定义的框架,这意味着反向传播是根据你的代码来确定如何运行,并且每次迭代可以是不同的。
torch.Tensor是这个包的核心类。如果设置.requires_grad为True,那么将会追踪该张量的操作。
backward:当完成计算后通过调用 .backward(),自动计算所有的梯度, 这个张量的所有梯度将会自动积累到 .grad 属性。
detach:要阻止张量跟踪历史记录,可以调用.detach()方法将其与计算历史记录分离,并禁止跟踪它将来的计算记录。
grad:为了防止跟踪历史记录(和使用内存),可以将代码块包装在with torch.no_grad():中。 在评估模型时特别有用,因为模型可能具有requires_grad = True的可训练参数,但是我们不需要梯度计算。
model = nn.Linear(1, 1) # w * input + b
loss_fn = nn.MSELoss() # 损失函数
opt = torch.optim.SGD(model.parameters(), lr=0.0001) # 随机梯度下降
# 开始训练
for epoch in range(1000):
for x, y in zip(X, Y):
# step1:使用模型预测
y_pred = model(x)
# step2:根据预测结果计算损失
loss = loss_fn(y, y_pred)
# step3:把变量梯度清0
opt.zero_grad()
# step4:求解梯度
loss.backward()
# step5:优化模型参数
opt.step()
# 输出结果
print(model.weight, model.bias)
这里利用nn.Linear()函数进行计算。nn.Linear()是用于设置网络中的全连接层。
import torch
in_features = torch.tensor([1, 2, 3, 4], dtype=torch.float32)
weight_matrix = torch.tensor([
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6]
], dtype=torch.float32)
out_features = weight_matrix.matmul(in_features)
print(out_features)
这就是Linear层的工作原理。使用权重矩阵(相当于上面代码的weight_matrix二维矩阵)将一个in_feature空间映射到一个out_feature空间。
下面来看看如何创建一个Pytorch的Linear层来完成上述相同的操作。
fc = nn.Linear(in_features = 4, out_features = 3, bias = False)
上面代码定义了一个线性层,它接受4个输入特征并把它们转化成3个输出特征。即从四维空间转换到三维空间。 PyTorch LinearLayer类使用传递给构造函数的数字4和3创建了一个3 x 4的权重矩阵。
def __init__(self, in_features: int, out_features: int, bias: bool = True,
device=None, dtype=None) -> None:
factory_kwargs = {'device': device, 'dtype': dtype}
super(Linear, self).__init__()
self.in_features = in_features
self.out_features = out_features
self.weight = Parameter(torch.empty((out_features, in_features), **factory_kwargs))
if bias:
self.bias = Parameter(torch.empty(out_features, **factory_kwargs))
else:
self.register_parameter('bias', None)
self.reset_parameters()
当用一个3×4矩阵乘以一个4×1矩阵时,结果是一个3×1矩阵。这就是PyTorch以这种方式构建权重矩阵的原因。
Parameter(torch.empty((out_features, in_features), **factory_kwargs))生成out_features × in_features大小的矩阵。并将值放入parameter中。
model = torch.nn.Linear(1, 1)
for x,y in zip(X,Y):
ans = model1(x) # 相当于ans = x * model.weight + model.bias
# 这里的 model.weight 和 model.bias 是随机生成的
创建损失函数nn.MSELoss()平均方差
loss = loss_fn(y, y_pred) 即
l
o
s
s
=
(
y
−
y
p
r
e
d
)
2
loss = (y-y_{pred})^2
loss=(y−ypred)2
其它损失函数:
L1Loss 取预测值和真实值的绝对误差的平均数,用于回归。l o s s = 1 N ∑ n N ∣ x n − y n ∣ loss = \frac{1}{N} \sum_n^N |x_n-y_n| loss=N1n∑N∣xn−yn∣
SmoothL1Loss 也叫作 Huber Loss,误差在 (-1,1) 上是平方损失,其他情况是 L1 损失,应用于回归。optimizer.zero_grad()函数会遍历模型的所有参数,通过p.grad.detach_()方法截断反向传播的梯度流,再通过p.grad.zero_()函数将每个参数的梯度值设为0,即上一次的梯度记录被清空。
即本来上一次梯度求导为n,当前为m,如果不设置梯度清0,则这一次的值会与之前的累加,即n+m。
torch.tensor是autograd包的基础类,如果你设置tensor的requires_grads为True,就会开始跟踪这个tensor上面的所有运算,如果你做完运算后使用tensor.backward(),所有的梯度就会自动运算,tensor的梯度将会累加到它的.grad属性里面去。
具体的说,损失函数loss是由模型的所有权重w经过一系列运算得到的,若某个w的requires_grads为True,则w的所有上层参数的.grad_fn属性中保存了对应的运算,在使用loss.backward()后,会一层层的反向传播计算每个w的梯度值,并保存到该w的.grad属性中。
如果没有进行tensor.backward()的话,梯度值将会是None,因此loss.backward()要写在optimizer.step()之前。
step()函数的作用是执行一次优化步骤,通过梯度下降法来更新参数(w,b)的值,因为梯度下降是基于梯度的。所以在执行opt.step()函数前应该先执行loss.backward()函数来计算梯度。