初步了解torch.nn
pytorch的神经网络层利用torch.nn实现,我们通过一个例子来熟悉其前向传播、反向传播的链路。
首先,我们给出要优化的函数——它是一个包含2000个样本的正弦函数:
import math
# Create Tensors to hold input and outputs.
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)
我们用一个包含三个神经元的线性输入层去逼近真实的y,输出层是一个神经元,Flatten是继承自torch.flatten方法的,它表示将数据展成一维(与真实y维度对应):
torch.nn.Flatten(start_dim=0, end_dim=1)
p = torch.tensor([1, 2, 3])
xx = x.unsqueeze(-1).pow(p) # tensor (x, x^2, x^3)
model = torch.nn.Sequential(
torch.nn.Linear(3, 1),
torch.nn.Flatten(0, 1)
)
定义损失函数,利用梯度下降法去优化参数:
#定义损失函数
loss_fn = torch.nn.MSELoss(reduction='sum')
learning_rate = 1e-6
for t in range(2000):
y_pred = model(xx)
loss = loss_fn(y_pred, y)
if t % 100 == 0:
print(t, loss.item())
# Zero the gradients before running the backward pass.
model.zero_grad()
loss.backward()
with torch.no_grad(): #停止对从跟踪历史中的requires_grad=True的张量自动求导
for param in model.parameters():
param -= learning_rate * param.grad
model.parameters()可以获取神经网络层所有参数,这里一共4个参数,三个神经元对应的系数项w以及输出一个神经元对应的偏移项b。在第一讲的时候我们说过,pytorch计算梯度是累加的,所以下一次迭代执行反向传播前,需要执行梯度清零操作——moel.zero_grad()。然后,上面代码就剩下最后的torch.no_grad()要理解,要理解这个,首先要知道pytorch的原地操作符——in-place operation。
pytorch的原地操作符是指改变一个tensor的值的时候,不经过复制操作,而是直接在原来的内存上改变它的值,比如带下划线后缀的操作,“+=”操作:
x.add_(y)
x += 1
x *= 2
而pytorch规定有两种情况不能使用原地操作符:
对于requires_grad=True的叶子张量
求梯度阶段用到的张量
举个例子,下面对张量w的归一化会报错:
第二种情况就是上面的例子:
param -= learning_rate * param.grad
这里对模型参数进行原地操作符,它在计算w1后会更新w1,再次计算w2是会使用更新后w1的值,这显然就不对了。所以,为了避免这种情况,就要使用torch.no_grad()来停止对张量即刻更新,等待程序跳出with torch.no_grad()后再更新。
我们来看看模型的结果:
优化器torch.optim
上面的优化是执行了常规的梯度下降,梯度下降还有很多变种,pytorch也封装了各种优化器,直接调用就行。
传送门:梯度下降法
继续上一节的例子,我们来看看pytorch如何利用优化器完成整个训练过程:
learning_rate = 1e-3
optimizer = torch.optim.RMSprop(model.parameters(), lr=learning_rate)
for t in range(2000):
# Forward pass: compute predicted y by passing x to the model.
y_pred = model(xx)
# Compute and print loss.
loss = loss_fn(y_pred, y)
if t % 500 == 0:
print(t, loss.item())
optimizer.zero_grad()
loss.backward()
optimizer.step()
这里使用RMSprop梯度下降法,用torch.optim传入模型参数、学习率即可;然后执行代码optimizer.step() 进行参数更新
nn.Module
类似与keras,pytorch实现神经网络的高级API是模块torch.nn.Module,我们通过一个稍微复杂的案例来理解它的过程:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 5x5 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
随便定义一些样本,通过优化器来训练模型:
input = torch.randn(1, 1, 32, 32)
target = torch.randn(10) # a dummy target, for example
target = target.view(1, -1) # make it the same shape as output
net = Net()
criterion = nn.MSELoss()
optimizer = optim.SGD(net.parameters(), lr=0.01)
for t in range(20):
output = net(input)
loss = criterion(output, target)
print(t,loss.item())
optimizer.zero_grad() # zero the gradient buffers
loss.backward()
optimizer.step() # Does the update
参考资料:
https://pytorch.org/tutorials/beginner/pytorch_with_examples.html#pytorch-nn