Pytorch应用训练好的模型

Pytorch应用训练好的模型

  • 1.保存训练好的模型:torch.save方法
  • 2.加载之前保存的模型:torch.load方法
  • 3.对于分类问题的补充
  • 4.CPU训练完整代码
  • 5.GPU训练方法一
  • 6.GPU训练方法二
  • 7.GPU训练过程的细节优化
  • 8.验证模型
  • 1.保存训练好的模型:torch.save方法

    保存训练好的模型有两种方式,第一种保存模型结构且保存模型参数,第一种方式存在一种陷阱,也就是每次加载模型都得把类定义,或者访问类所在的包。保存方式为:

    torch.save(模型名, 以pth为后缀的文件)

    第二种保存方式只保存模型参数,不保存模型结构,这样可以面对较大的网络模型,可以节省空间,是官方推荐的保存方式,具体为:

    torch.save(模型名.state_dict(), 以pth为后缀的文件)

    第一个参数,模型名.state_dict()意为只取模型的参数,且以字典方式存储;第二个参数存储模型的地址,一般都用以pth结尾的文件。
    代码如下:

    import torch
    import torchvision
    from torch import nn
    
    vgg16 = torchvision.models.vgg16(pretrained=False)
    # 保存方式1,模型结构+模型参数
    torch.save(vgg16, "vgg16_method1.pth")
    
    # 保存方式2,模型参数(官方推荐)
    torch.save(vgg16.state_dict(), "vgg16_method2.pth")
    
    # 陷阱
    class test_Model(nn.Module):
        def __init__(self):
            super(test_Model, self).__init__()
            self.conv1 = nn.Conv2d(3, 64, kernel_size=3)
    
        def forward(self, x):
            x = self.conv1(x)
            return x
    
    test_model = test_Model()
    torch.save(test_model, "test_model_method1.pth")
    

    2.加载之前保存的模型:torch.load方法

    对应两种不同的保存模型的方式,也相应地有两种加载模型的方式,第一种方式为:

    读取出的模型名 = torch.load(之前保存的模型文件名)

    第二种方式为:

    读取出的模型名 = torchvision.models.vgg16(pretrained=False)
    读取出的模型名.load_state_dict(torch.load(之前保存的模型文件名))

    因为第二种方法没有保存模型结构,所以我们要先设计一个模型结构,本例中用的是直接下载VGG16模型结构;然后第二条语句用于将保存的参数值传入到模型结构中。代码如下:

    import torch
    from model_save import *
    import torchvision
    from torch import nn
    
    # 方式1-》保存方式1,加载模型
    model = torch.load("vgg16_method1.pth")
    # print(model)
    
    # 方式2,加载模型
    vgg16 = torchvision.models.vgg16(pretrained=False)
    vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
    # 只读取参数
    # model = torch.load("vgg16_method2.pth")
    # print(vgg16)
    
    # 方式1,陷阱
    # class test_Model(nn.Module):
    #     def __init__(self):
    #         super(test_Model, self).__init__()
    #         self.conv1 = nn.Conv2d(3, 64, kernel_size=3)
    #
    #     def forward(self, x):
    #         x = self.conv1(x)
    #         return x
    model = torch.load('test_model_method1.pth')
    print(model)
    

    3.对于分类问题的补充

    对于图像分类、目标检测或是语义分割等各种问题而言,单靠loss曲线还无法完全表现出模型的优劣,而在图像分类问题中,我们常用acc,即在测试集上的正确率来表示模型的训练情况及优劣。

    在分类问题中,例如CIFAR10中,某张图片进入模型后的输出结果是形如([0.2, 0.1, 0.4, 0.6, 0.4, 0.4, 0.7, 0.2, 0.5, 0.1]),而targets是类似(5)这样的某个整型数字,表明该图片属于第5类。那么如和将输出结果与targets之间进行计算,产生正确率呢,我们采用argmax函数,该函数会找到数组中最大值并输出最大值的序号,那么如此一来,将输出结果的最大值序号和targets相比较,如果一致则说明该图像识别正确。代码如下:

    import torch
    
    output = torch.tensor([[0.2, 0.3],
                          [0.1, 0.5]])
    # 纵向比较
    print(output.argmax(0))
    # 横向比较
    print(output.argmax(1))
    
    pred = output.argmax(1)
    targets = torch.tensor([0, 1])
    print(pred == targets)
    # 输出正确的个数
    print((pred == targets).sum())
    

    注:因在验证过程中,因为不是一张图片,而是batch_size张图片验证,那么output应是二维数组,第二维的数目是batchsize个,此例中设为2个,相应的targets应是一个一维数组,数组中的每个数表示每张图对应的正确类别是哪类。另外,argmax(0)表示传入的数据纵向进行比较,在本例中是0.2和0.1比,0.3和0.5比;而argmax(1)表示传入的数据横向进行比较,在本例中是0.2和0.3比,0.1和0.5比,毫无疑问,运用在计算准确率中,我们应该使用的是argmax(1),那么记录所有output和targets相吻合的项的和除以总项数即可获得该batch_size的准确率。

    4.CPU训练完整代码

    CPU训练就是之前介绍的搭建模型和应用模型相结合,内容都是之前所讲,完整代码如下:

    import torchvision
    from torch.utils.tensorboard import SummaryWriter
    
    from model import *
    # 准备数据集
    from torch import nn
    from torch.utils.data import DataLoader
    
    train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
                                              download=True)
    test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
                                             download=True)
    
    # length 长度
    train_data_size = len(train_data)
    test_data_size = len(test_data)
    # 如果train_data_size=10, 训练数据集的长度为:10
    print("训练数据集的长度为:{}".format(train_data_size))
    print("测试数据集的长度为:{}".format(test_data_size))
    
    
    # 利用 DataLoader 来加载数据集
    train_dataloader = DataLoader(train_data, batch_size=64)
    test_dataloader = DataLoader(test_data, batch_size=64)
    
    # 创建网络模型
    tudui = Tudui()
    
    # 损失函数
    loss_fn = nn.CrossEntropyLoss()
    
    # 优化器
    # learning_rate = 0.01
    # 1e-2=1 x (10)^(-2) = 1 /100 = 0.01
    learning_rate = 1e-2
    optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
    
    # 设置训练网络的一些参数
    # 记录训练的次数
    total_train_step = 0
    # 记录测试的次数
    total_test_step = 0
    # 训练的轮数
    epoch = 10
    
    # 添加tensorboard
    writer = SummaryWriter("../logs_train")
    
    for i in range(epoch):
        print("-------第 {} 轮训练开始-------".format(i+1))
    
        # 训练步骤开始
        # 这句话在训练开始时调用,在有Dropout层和层起作用,在本例中可不写,但建议写,较为规范
        tudui.train()
        for data in train_dataloader:
            imgs, targets = data
            outputs = tudui(imgs)
            loss = loss_fn(outputs, targets)
    
            # 优化器优化模型
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
    
            total_train_step = total_train_step + 1
            if total_train_step % 100 == 0:
                print("训练次数:{}, Loss: {}".format(total_train_step, loss.item()))
                writer.add_scalar("train_loss", loss.item(), total_train_step)
    
        # 测试步骤开始
        # 这句话在测试开始时调用,在有Dropout层和层起作用,在本例中可不写,但建议写,较为规范
        tudui.eval()
        total_test_loss = 0
        # 整体正确个数
        total_accuracy = 0
        # 设置测试模型不改变梯度
        with torch.no_grad():
            for data in test_dataloader:
                imgs, targets = data
                outputs = tudui(imgs)
                loss = loss_fn(outputs, targets)
                total_test_loss = total_test_loss + loss.item()
                # 计算正确个数
                accuracy = (outputs.argmax(1) == targets).sum()
                total_accuracy = total_accuracy + accuracy
    
        print("整体测试集上的Loss: {}".format(total_test_loss))
        print("整体测试集上的正确率: {}".format(total_accuracy/test_data_size))
        writer.add_scalar("test_loss", total_test_loss, total_test_step)
        writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step)
        total_test_step = total_test_step + 1
    
        #此处用的模型保存方式为方式一,建议使用方式二
        torch.save(tudui, "tudui_{}.pth".format(i))
        print("模型已保存")
    
    writer.close()
    

    5.GPU训练方法一

    GPU训练大部分代码与CPU训练一致,调用GPU的方法有两种,第一种方式是在网络模型、损失函数、训练输入数据、测试输入数据四处加入自己 = 自己.cuda即可,即在不同的地方加入以下四句语句:

    if torch.cuda.is_available():
        tudui = tudui.cuda()
    
    if torch.cuda.is_available():
        loss_fn = loss_fn.cuda()
    
    if torch.cuda.is_available():
    	imgs = imgs.cuda()
    	targets = targets.cuda()
    
            if torch.cuda.is_available():
                imgs = imgs.cuda()
                targets = targets.cuda()
    
            if torch.cuda.is_available():
                imgs = imgs.cuda()
                targets = targets.cuda()
    

    因此第一种GPU训练的完整代码为:

    import time
    
    import torch
    import torchvision
    from torch.utils.tensorboard import SummaryWriter
    
    # from model import *
    # 准备数据集
    from torch import nn
    from torch.utils.data import DataLoader
    
    train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
                                              download=True)
    test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
                                             download=True)
    
    # length 长度
    train_data_size = len(train_data)
    test_data_size = len(test_data)
    # 如果train_data_size=10, 训练数据集的长度为:10
    print("训练数据集的长度为:{}".format(train_data_size))
    print("测试数据集的长度为:{}".format(test_data_size))
    
    
    # 利用 DataLoader 来加载数据集
    train_dataloader = DataLoader(train_data, batch_size=64)
    test_dataloader = DataLoader(test_data, batch_size=64)
    
    # 创建网络模型
    class Tudui(nn.Module):
        def __init__(self):
            super(Tudui, self).__init__()
            self.model = nn.Sequential(
                nn.Conv2d(3, 32, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Conv2d(32, 32, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Conv2d(32, 64, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Flatten(),
                nn.Linear(64*4*4, 64),
                nn.Linear(64, 10)
            )
    
        def forward(self, x):
            x = self.model(x)
            return x
    tudui = Tudui()
    if torch.cuda.is_available():
        print("成功调用GPU")
        tudui = tudui.cuda()
    
    # 损失函数
    loss_fn = nn.CrossEntropyLoss()
    if torch.cuda.is_available():
        loss_fn = loss_fn.cuda()
    # 优化器
    # learning_rate = 0.01
    # 1e-2=1 x (10)^(-2) = 1 /100 = 0.01
    learning_rate = 1e-2
    optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
    
    # 设置训练网络的一些参数
    # 记录训练的次数
    total_train_step = 0
    # 记录测试的次数
    total_test_step = 0
    # 训练的轮数
    epoch = 10
    
    # 添加tensorboard
    writer = SummaryWriter("../logs_train")
    # 记录最初时间
    start_time = time.time()
    
    for i in range(epoch):
        print("-------第 {} 轮训练开始-------".format(i+1))
    
        # 训练步骤开始
        tudui.train()
        for data in train_dataloader:
            imgs, targets = data
            if torch.cuda.is_available():
                imgs = imgs.cuda()
                targets = targets.cuda()
            outputs = tudui(imgs)
            loss = loss_fn(outputs, targets)
    
            # 优化器优化模型
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
    
            total_train_step = total_train_step + 1
            if total_train_step % 100 == 0:
                # 记录结束时间
                end_time = time.time()
                print("每100次训练花费时间为:{}秒".format(end_time - start_time))
                # 重置开始时间
                start_time = time.time()
                print("训练次数:{}, Loss: {}".format(total_train_step, loss.item()))
                writer.add_scalar("train_loss", loss.item(), total_train_step)
    
        # 测试步骤开始
        tudui.eval()
        total_test_loss = 0
        total_accuracy = 0
        with torch.no_grad():
            for data in test_dataloader:
                imgs, targets = data
                if torch.cuda.is_available():
                    imgs = imgs.cuda()
                    targets = targets.cuda()
                outputs = tudui(imgs)
                loss = loss_fn(outputs, targets)
                total_test_loss = total_test_loss + loss.item()
                accuracy = (outputs.argmax(1) == targets).sum()
                total_accuracy = total_accuracy + accuracy
    
        print("整体测试集上的Loss: {}".format(total_test_loss))
        print("整体测试集上的正确率: {}".format(total_accuracy/test_data_size))
        writer.add_scalar("test_loss", total_test_loss, total_test_step)
        writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step)
        total_test_step = total_test_step + 1
    
        torch.save(tudui, "tudui_{}.pth".format(i))
        print("模型已保存")
    
    writer.close()
    

    6.GPU训练方法二

    GPU第二种训练方式是先定义一个训练的设备,语句为device = torch.device(“cuda”),如果有多张显卡则语句为device = torch.device(“cuda:0”)代表用第0张显卡,要调用其它显卡则将0改成其它数字,然后将训练方法一中的.cuda改为.to(device),例如:

    if torch.cuda.is_available():
        tudui = tudui.cuda()
    

    则完整代码为:

    import torch
    import torchvision
    from torch.utils.tensorboard import SummaryWriter
    
    # from model import *
    # 准备数据集
    from torch import nn
    from torch.utils.data import DataLoader
    
    # 定义训练的设备
    device = torch.device("cuda")
    
    train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
                                              download=True)
    test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
                                             download=True)
    
    # length 长度
    train_data_size = len(train_data)
    test_data_size = len(test_data)
    # 如果train_data_size=10, 训练数据集的长度为:10
    print("训练数据集的长度为:{}".format(train_data_size))
    print("测试数据集的长度为:{}".format(test_data_size))
    
    
    # 利用 DataLoader 来加载数据集
    train_dataloader = DataLoader(train_data, batch_size=64)
    test_dataloader = DataLoader(test_data, batch_size=64)
    
    # 创建网络模型
    class Tudui(nn.Module):
        def __init__(self):
            super(Tudui, self).__init__()
            self.model = nn.Sequential(
                nn.Conv2d(3, 32, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Conv2d(32, 32, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Conv2d(32, 64, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Flatten(),
                nn.Linear(64*4*4, 64),
                nn.Linear(64, 10)
            )
    
        def forward(self, x):
            x = self.model(x)
            return x
    tudui = Tudui()
    tudui = tudui.to(device)
    
    # 损失函数
    loss_fn = nn.CrossEntropyLoss()
    loss_fn = loss_fn.to(device)
    # 优化器
    # learning_rate = 0.01
    # 1e-2=1 x (10)^(-2) = 1 /100 = 0.01
    learning_rate = 1e-2
    optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
    
    # 设置训练网络的一些参数
    # 记录训练的次数
    total_train_step = 0
    # 记录测试的次数
    total_test_step = 0
    # 训练的轮数
    epoch = 10
    
    # 添加tensorboard
    writer = SummaryWriter("../logs_train")
    
    for i in range(epoch):
        print("-------第 {} 轮训练开始-------".format(i+1))
    
        # 训练步骤开始
        tudui.train()
        for data in train_dataloader:
            imgs, targets = data
            imgs = imgs.to(device)
            targets = targets.to(device)
            outputs = tudui(imgs)
            loss = loss_fn(outputs, targets)
    
            # 优化器优化模型
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
    
            total_train_step = total_train_step + 1
            if total_train_step % 100 == 0:
                print("训练次数:{}, Loss: {}".format(total_train_step, loss.item()))
                writer.add_scalar("train_loss", loss.item(), total_train_step)
    
        # 测试步骤开始
        tudui.eval()
        total_test_loss = 0
        total_accuracy = 0
        with torch.no_grad():
            for data in test_dataloader:
                imgs, targets = data
                imgs = imgs.to(device)
                targets = targets.to(device)
                outputs = tudui(imgs)
                loss = loss_fn(outputs, targets)
                total_test_loss = total_test_loss + loss.item()
                accuracy = (outputs.argmax(1) == targets).sum()
                total_accuracy = total_accuracy + accuracy
    
        print("整体测试集上的Loss: {}".format(total_test_loss))
        print("整体测试集上的正确率: {}".format(total_accuracy/test_data_size))
        writer.add_scalar("test_loss", total_test_loss, total_test_step)
        writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step)
        total_test_step = total_test_step + 1
    
        torch.save(tudui, "tudui_{}.pth".format(i))
        print("模型已保存")
    
    writer.close()
    

    7.GPU训练过程的细节优化

    首先,对于网络模型和损失函数,不需要对其重新赋值,也就是说以下代码:

    tudui = tudui.to(device)
    loss_fn = loss_fn.to(device)
    tudui = tudui.cuda()
    loss_fn = loss_fn.cuda()
    

    可以直接写为:

    tudui.to(device)
    loss_fn.to(device)
    tudui.cuda()
    loss_fn.cuda()
    

    但imgs和targets不能省略前面的赋值。

    其次,在方法二中可能出现没有GPU或是GPU未能成功调用,所以可以将代码:

    device = torch.device("cuda")
    

    改为:

    device = torch.device("cuda" if torch.cuda.isavailable() else "CPU")
    

    如此一来,如果无法调用GPU则会调用CPU训练,而不会报错。

    8.验证模型

    在训练并测试完之后,如果想要验证模型,类似与测试集的操作,可对某张特定图片或某些图片进行验证,观察其输出是否正确,代码如下:

    import torch
    import torchvision
    from PIL import Image
    from torch import nn
    
    image_path = "../imgs/airplane.png"
    image = Image.open(image_path)
    print(image)
    # 该语句是因为不是所有类型的图片都是三通道的,通过这句代码可以将所有类型的通篇都转化成3通道
    image = image.convert('RGB')
    transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
                                                torchvision.transforms.ToTensor()])
    
    image = transform(image)
    print(image.shape)
    
    class Tudui(nn.Module):
        def __init__(self):
            super(Tudui, self).__init__()
            self.model = nn.Sequential(
                nn.Conv2d(3, 32, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Conv2d(32, 32, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Conv2d(32, 64, 5, 1, 2),
                nn.MaxPool2d(2),
                nn.Flatten(),
                nn.Linear(64*4*4, 64),
                nn.Linear(64, 10)
            )
    
        def forward(self, x):
            x = self.model(x)
            return x
    
    # map_location属性是在,当保存的模型是GPU训练的,而现在我们要用CPU验证时需要添加这样一句话
    model = torch.load("tudui_29_gpu.pth", map_location=torch.device('cpu'))
    print(model)
    image = torch.reshape(image, (1, 3, 32, 32))
    model.eval()
    with torch.no_grad():
        output = model(image)
    print(output)
    
    print(output.argmax(1))
    

    注:因为训练是我们采用是第一种方式保存模型,所有加载模型时我们需要将模型的类再写一次,或者调用定义了模型类的文件;另外注意image = image.convert(‘RGB’)将各种不同的图片转化为三通道;最后注意如果保存的模型是GPU训练的,而现在我们要用CPU验证时需要添加这样一句话model = torch.load(“tudui_29_gpu.pth”, map_location=torch.device(‘cpu’))。

    来源:NNNJY

    物联沃分享整理
    物联沃-IOTWORD物联网 » Pytorch应用训练好的模型

    发表评论