一个前向传播网络框架
y=f(w, x) = Wx
y'=f'(w, x)w' = X
神经网络的训练需要使用损失函数的梯度,torch提供了一个torch.autograd的自动求导机制。
创建一个网络,并获取梯度
import torch
x = torch.ones(5) # input tensor
y = torch.zeros(3) # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
print(f"Gradient function for z = {z.grad_fn}")
print(f"Gradient function for loss = {loss.grad_fn}")
这里拆分了w和b,在一些网络的计算中,可以按W = [w, b],作为统一参数。
使用 grad() 计算梯度
对于一个网络默认只会对最后一层网络进行自动求导,即最后一层requires_grad=true
如需对全网络进行求导,需使用
loss.backward()
print(w.grad)
print(b.grad)
关闭梯度计算
在网络的应用(前向计算, 使用部分已训练网络)中,为了计算效率,可以关闭梯度的自动计算
z = torch.matmul(x, w)+b
print(z.requires_grad)
with torch.no_grad():
z = torch.matmul(x, w)+b
print(z.requires_grad)
或者
z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)
网络的正向,反向传播
在torch中网络表达为DAG(directed acyclic graph),这里考虑mini-batch的情况。
在前向计算中autograd 完成如下两件事:
- 根据单层网络计算tensor结果
- 维护网络梯度计算函数
在反向传播中,调用.backward(),完成如下事情: - 使用tensor的梯度函数 .grad_fn计算梯度
- 累计梯度结果到 .grad中
- 使用链式法则,计算每一层
Jacobin矩阵的计算
对于多维loss function, 在计算y=f(x),在backward中会自动计算Jacobian Product: v^T*J,其中v是对应的tensor。
inp = torch.eye(5, requires_grad=True)
out = (inp+1).pow(2)
out.backward(torch.ones_like(inp), retain_graph=True)
print(f"First call\n{inp.grad}")
out.backward(torch.ones_like(inp), retain_graph=True)
print(f"\nSecond call\n{inp.grad}")
inp.grad.zero_()
out.backward(torch.ones_like(inp), retain_graph=True)
print(f"\nCall after zeroing gradients\n{inp.grad}")