pytorch模型构建、训练、测试及预测

一  神经网络的典型处理流程

1. 定义可学习参数的网络结构(堆叠各层和层的设计);继承 nn.Module 模块,改写 forward 方法。
2. 数据集输入;
3. 对输入进行处理(由定义的网络层进行处理),主要体现在网络的前向传播;
4. 计算loss ,由Loss层计算;
5. 反向传播求梯度;
6. 根据梯度改变参数值,最简单的实现方式(SGD)为: weight = weight - learning_rate * gradient

其中,torch.nn是用来构建神经网络每个层的,例如卷积层,全连接层等。torch.nn.functional用以引用各种数学函数,例如激活函数等。torch.optim是各种优化方法,例如SGD,ADAM等。

二 简单例子

1 定义网络

构建一个简单的CNN网络,两个 (卷积+激活+池化)的模块,接两层全连接层,然后是输出层。然后实例化对象net。

import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

#实例化对象
net = Net()

2 定义损失函数和优化器

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

3 网络训练

为方便理解,只设置2个epoch,在每个 epoch 的循环中会遍历所有 trainloader 的数据。

每一次遍历时:

  1. 先通过 optimizer.zero_grad() 把梯度清理干净,防止受之前遗留梯度的影响。
  2. outputs = net(inputs), 前向传播,等价于net.forward(inputs) ,把输入数据输入到网络,得到预测结果。
  3. loss = criterion(outputs, labels), 计算当前 batch 的损失值。
  4. loss.backward(),执行链式求导,计算梯度。
  5. optimizer.step(),通过4中计算出来的梯度,更新每个可训练权重。
# 定义训练函数
def train(trainloader, model, criterion, optimizer):

    loss, current, n = 0.0, 0.0, 0
        for batch, data in enumerate(trainloader, 0):
            # get the inputs; data is a list of [inputs, labels]
            inputs, labels = data

            # zero the parameter gradients
            optimizer.zero_grad()

            # forward + backward + optimize
            outputs = net(inputs)
            cur_loss = criterion(outputs, labels)
            _, pred = torch.max(output, axis=1)
            cur_acc = torch.sum(labels==pred)/output.shape[0]
            
            loss.backward()
            optimizer.step()

            loss += cur_loss.item()
            current += cur_acc.item()
            n = n+1

        train_loss = loss / n      n个batch的平均?
        tran_acc = current /n
      

# 定义验证函数
def val(valloader, model, criterion):
    
    # 将模型转为验证模型
    model.eval()
    loss, current, n = 0.0, 0.0, 0
    with torch.no_grad():
        for batch, data in enumerate(valloader):
            inputs, labels = data
            output = net(inputs)
            cur_loss = crietrion(output, y)
            _, pred = torch.max(output, axis=1)
            cur_acc = torch.sum(y == pred) / output.shape[0]
            loss += cur_loss.item()
            current += cur_acc.item()
            n = n+1
 
        val_loss = loss / n
        val_acc = current / n

#开始训练

# 开始训练
loss_train = []
acc_train = []
loss_val = []
acc_val = []
 

min_acc = 0
for t in range(2):
    lr_scheduler.step()
    
    print(f"epoch{t+1}\n--------------")
    train_loss, train_acc = train(train_dataloader, model, crietrion, optimizer)
    val_loss, val_acc = val(val_dataloader, model, crietrion)
 
    loss_train.append(train_loss)
    acc_train.append(train_acc)
    loss_val.append(val_loss)
    acc_val.append(val_acc)
 
    # 保存最好的模型权重文件
    if val_acc > min_acc:
        folder = 'save_model'
        if not os.path.exists(folder):
            os.mkdir('save_model')
        min_acc = val_acc
        print(f'save best model,第{t+1}轮')
        torch.save(model.state_dict(), 'save_model/best_model.pth')
    # 保存最后的权重模型文件
    if t == epoch - 1:
        #使用 torch.save 将模型序列化保存在指定地址,方便后续调用,调用时使用torch.load
        torch.save(model.state_dict(), 'save_model/last_model.pth')
print('Done!')

其中,几个主要函数

optimizer.zero_grad():

该函数会遍历模型的所有参数,通过p.grad.detach_()方法截断反向传播的梯度流,再通过p.grad.zero_()函数将每个参数的梯度值设为0,即上一次的梯度记录被清空。因为训练的过程通常使用mini-batch方法,所以如果不将梯度清零的话,梯度会与上一个batch的数据相关,因此该函数要写在反向传播和梯度下降之前。

loss.backward():

PyTorch的反向传播(即tensor.backward())是通过autograd包来实现的,autograd包会根据tensor进行过的数学运算来自动计算其对应的梯度。具体来说,torch.tensor是autograd包的基础类,如果你设置tensor的requires_grads为True,就会开始跟踪这个tensor上面的所有运算,如果你做完运算后使用tensor.backward(),所有的梯度就会自动运算,tensor的梯度将会累加到它的.grad属性里面去。更具体地说,损失函数loss是由模型的所有权重w经过一系列运算得到的,若某个w的requires_grads为True,则w的所有上层参数(后面层的权重w)的.grad_fn属性中就保存了对应的运算,然后在使用loss.backward()后,会一层层的反向传播计算每个w的梯度值,并保存到该w的.grad属性中。如果没有进行tensor.backward()的话,梯度值将会是None,因此loss.backward()要写在optimizer.step()之前。

optimizer.step():

step()函数的作用是执行一次优化步骤,通过梯度下降法来更新参数的值。其使用的是参数空间(param_groups)中的grad,也就是当前参数空间对应的梯度,这也就解释了为什么optimzier使用之前需要zero清零一下,因为如果不清零,那么使用的这个grad就得同上一个mini-batch有关。因为梯度下降是基于梯度的,所以在执行optimizer.step()函数前应先执行loss.backward()函数来计算梯度。注意:optimizer只负责通过梯度下降进行优化,而不负责产生梯度,梯度是tensor.backward()方法产生的。

optimizer.step()放在每一个batch训练中,而不是epoch训练中,这是因为现在的mini-batch训练模式是假定每一个训练集就只有mini-batch这样大,因此实际上可以将每一次mini-batch看做是一次训练,一次训练更新一次参数空间,因而optimizer.step()放在这里。

scheduler.step()按照Pytorch的定义是用来更新优化器的学习率的,一般是按照epoch为单位进行更换,即多少个epoch后更换一次学习率,因而scheduler.step()放在epoch这个大循环下。

注意:1 验证集使用时,使用model.eval转换为验证模型,验证集不用来更新权重和梯度;2 验证集的作用:可以用在训练的过程中,经过几个epoch后,跑一次验证集看一下效果,可以及时发现模型或者参数的问题,主要用来调整超参数(如人为设定的初始学习率、层数、权值衰减系数、训练次数等);2 每个batch训练后更新梯度和权重。

4  预测

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值