跟着小土堆学习pytorch(三)

跟着小土堆学习pytorch(三)

继续学习。

现有网络模型的使用及修改

本节内容主要跟着up主学习有关模型的使用和修改,以分类问题为主,模型使用VGG,数据集仍为 CIFAR10 数据集(主要用于分类)。

数据集 ImageNet

注意:必须要先有 package scipy

在 Terminal 里输入

pip list

寻找是否有 scipy,若没有的话输入

pip install scipy

但在教学中由于该数据集并未公开访问,需要别的路径下载(可以直接搜索ImageNet下载),并且由于该数据集过于庞大,因此课程并没有继续使用。
以下是下载该数据集的一些参数:
在这里插入图片描述
相关代码:

import torchvision

train_data = torchvision.datasets.ImageNet("./data_image_net", split='train', download=True, transform=torchvision.transforms.ToTensor)

这里并没有下载成功,而是报错了,原因上面讲过,并且课程后续并未使用。

VGG16 模型

VGG 11/13/16/19 常用16和19。
参数:Pretrained
在这里插入图片描述

import torchvision
from torch import nn
vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True) # 需要进行下载

可以和up主一样打断点来查看相关的权重。

总结:

  • 设置为 False 的情况,相当于网络模型中的参数都是初始化的、默认
  • 设置为 True 时,网络模型中的参数在数据集上是训练好的,能达到比较好的效果

接下来可以查看下vgg16的网络架构:print(vgg16_true ),如下所示:

VGG(
  (features): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU(inplace=True)
    (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (3): ReLU(inplace=True)
    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (6): ReLU(inplace=True)
    (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (8): ReLU(inplace=True)
    (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU(inplace=True)
    (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (13): ReLU(inplace=True)
    (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (15): ReLU(inplace=True)
    (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (18): ReLU(inplace=True)
    (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (20): ReLU(inplace=True)
    (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (22): ReLU(inplace=True)
    (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (25): ReLU(inplace=True)
    (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (27): ReLU(inplace=True)
    (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (29): ReLU(inplace=True)
    (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (avgpool): AdaptiveAvgPool2d(output_size=(7, 7))
  (classifier): Sequential(
    (0): Linear(in_features=25088, out_features=4096, bias=True)
    (1): ReLU(inplace=True)
    (2): Dropout(p=0.5, inplace=False)
    (3): Linear(in_features=4096, out_features=4096, bias=True)
    (4): ReLU(inplace=True)
    (5): Dropout(p=0.5, inplace=False)
    (6): Linear(in_features=4096, out_features=1000, bias=True)
  )
)
  • 在其结构上进行添加层,可以选择不同的位置进行添加(总体结构或者classifier中):
# 在总体中添加
# vgg16_true.add_module("add_linear", module=nn.Linear(1000, 10))
# 在classifier中添加:
vgg16_true.classifier.add_module("add_linear", module=nn.Linear(1000, 10))

在网络最后添加:
在这里插入图片描述

在classifier中添加:
在这里插入图片描述

  • 修改已有的网络
# 修改现有的网络:
vgg16_false.classifier[6] = nn.Linear(4096, 10)
print(vgg16_false)

结果如下:在这里插入图片描述

网络模型的保存与读取

网络模型的保存有两种方式

  • 方法1:不仅保存了模型的结构,还保存了模型的参数

    torch.save(vgg16, "vgg16_method1.pth")
    
  • 保存方式2: 模型参数(官方推荐): 保存为字典模式

    torch.save(vgg16.state_dict(), "vgg16_method2.pth")
    

运行完代码之后就会有两个这样的文件:在这里插入图片描述

模型的读取

  • 模型保存方式1的读取方式:直接使用 torch.load("path")
vgg_model1 = torch.load("vgg16_method1.pth")
print(vgg_model1)
  • 模型保存方式2的读取方式:
vgg_model2 = torch.load("vgg16_method2.pth")
print((vgg_model2))

# 将字典模式的参数加载到模型中:
# 先定义模型
vgg16 = torchvision.models.vgg16(pretrained=False)
# 使用load_state_dict 来加载
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
print(vgg16)

使用以上的方式就可以读取模型。

  • 使用方式1可能出现的问题(陷阱)
    如下代码所示,定义一个模型,然后,将该模型使用方式1进行保存
# 陷阱:
class Test(nn.Module):
    def __init__(self):
        super(Test, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3)

    def forward(self, x):
        output = self.conv1(x)
        return output

test = Test()

torch.save(test, "test_model1.pth")

在另一个文件夹进行读取文件的时候,可能会报错,代码如下:

## 陷阱
class Test(nn.Module):
    def __init__(self):
        super(Test, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3)

    def forward(self, x):
        output = self.conv1(x)
        return output
model = torch.load("test_model1.pth")
print(model)
# 报错: Can't get attribute 'Test' on <module '__main__' from 'D:/learning/pytorch/learn_pytorch/model_load.py'>
# 解决:需要将模型再写一遍 & 或者 将模型引入from model_save import * (真实项目中不会把模型移来移去)

完整的模型训练套路(CIFAR10数据集)

创建model.py,搭建神经网络模型

如下所示:

# 搭建神经网络
import torch
from torch import nn

class Test(nn.Module):

    def __init__(self) :
        super(Test, 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

# main函数
if __name__ == '__main__':
    test = Test()
    # 验证网络模型的正确性,创造一个输入尺寸,判断输出尺寸是不是我们想要的
    input = torch.ones((64, 3, 32, 32))  # batchsize=64,channel=3,尺寸32*32
    output = test(input)
    print(output.shape) # 得到torch.Size([64, 10])  ---> 验证完成

创建train.py,用于训练和测试

1. 准备数据集

train_data = torchvision.datasets.CIFAR10('./CIFAR10', train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
test_data = torchvision.datasets.CIFAR10('./CIFAR10', train=False, transform=torchvision.transforms.ToTensor(),
                                         download=True)
                                         
# 可以通过len() 得到数据集的长度

2. 利用dataloader来加载数据

# 利用dataloader加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

3. 创建网络模型

test = Test()

4. 创建损失函数

loss_function = nn.CrossEntropyLoss()

5. 创建优化器

# learning_rate = 0.01
learning_rate = 1e-2 # 1e-2 = 10^(-2)
optimizer = torch.optim.SGD(test.parameters(), lr=learning_rate)

6. 设置训练网络的一些参数

# 记录训练的次数:
total_train_step = 0
# 记录测试的次数:
total_test_step = 0
# 记录训练的轮数:
epoch = 10

**7. 设置训练轮数、开始训练 **

for i in range(epoch):
    print("---------第 {} 轮训练开始--------".format(i + 1))
    # 训练开始:
    test.train() # 将网络设置为训练模式,只对某些特定的网络有作用,比如当网络中有Dropout、BatchNorm层等的时候
    for data in train_dataloader:
        imgs, targets = data
        outputs = test(imgs)
        # 记录损失
        loss = loss_function(outputs, targets)
        # 优化器优化模型
        optimizer.zero_grad() # 梯度清零
        loss.backward() # 梯度下降
        optimizer.step() # 对每个梯度进行优化

        total_train_step = total_train_step + 1
        # item():将tensor类型转为实际的值
        if (total_train_step % 100 == 0):
            print("训练次数: {}, 损失值为: {}".format(total_train_step, loss.item()))
            writer.add_scalar("train_loss", loss.item(),  total_train_step)

8. 测试模型

 # 如何知道模型有没有训练好---------进行测试,在测试集上跑一遍,用测试数据集上的损失或正确率来评估模型有没有训练好
    # 测试步骤:
    test.eval() # 将网络模式设置为测试状态,也是只对特定的网络有作用,比如当网络中有Dropout、BatchNorm层等的时候
    total_test_loss = 0
    total_accuracy = 0
    test.eval()
    with torch.no_grad():# 将网络模型中的梯度消失,只需要测试,不需要对梯度进行调整,也不需要利用梯度来优化
        for data in test_dataloader:
            imgs, targets = data
            outputs = test(imgs)
            loss = loss_function(outputs, targets)
            total_test_loss += loss
            # 正确率
            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, total_test_step)
    total_test_step += 1
  • argmax() 的理解
    例如,这里有一个二分类问题,有两个输入(2*input),其中targets = [[0],[1]],输入模型Model(2分类),然后得到输出output = [[0.1, 0.2], [0.3, 0.4]],预测值为preds =[[1],[1]],我们可以得到:(preds == input target).sum() = 1,可以写成[false, true].sum =1,在这其中argmax() 就是用来算preds

    import torch
    
    outputs = torch.tensor([[0.1, 0.2],
                            [0.05, 0.4]])
    # print(outputs.argmax(0)) # 竖着看:tensor([0, 1]) 比较0.1 和0.05、0.2和0.4
    print(outputs.argmax(1)) # 横着看:tensor([1, 1]) 比较0.1和0.2、0.05和0.4
    preds = outputs.argmax(1)
    targets = torch.tensor([0, 1])
    print((preds == targets).sum()) # --> 得到 1
    

因此,在本文举例的分类问题中,求的是batch内的每个图片对应的10个输出中的最大值所在的位置,最大值代表该图片属于哪个类概率较大,位置编号代表类别,所以要得到位置。

模型最终输出是64*10维,所以argmax(1)就相当于 求每一行最大值对应的位置,得到64 * 1个编号,targets也是64*1的,

经过(outputs.argmax(1) == targets).sum()可以得到正确的个数。

最终使用将每一步正确的个数求和,再使用total_accuracy / test_data_size求得正确率。

9. 保存模型

# 一般情况下是保存每一轮训练的模型结果
    torch.save(test, "test_{}.pth".format(i))
    # torch.save(test.state_dict(), "test_{}.pth".format(i))
    print("模型已保存")

总体代码如下:

import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *

# 1.准备数据集
train_data = torchvision.datasets.CIFAR10('./CIFAR10', train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)

test_data = torchvision.datasets.CIFAR10('./CIFAR10', train=False, transform=torchvision.transforms.ToTensor(),
                                         download=True)

# 获得数据集的长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 格式化字符串的方式
print("训练数据集的长度为: {}".format(train_data_size)) # 50000
print("测试数据集的长度为: {}".format(test_data_size)) # 10000

# 2.利用dataloader加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

# 3.创建网络模型
test = Test()

# 4.创建损失函数
loss_function = nn.CrossEntropyLoss()

# 5.创建优化器
# learning_rate = 0.01
learning_rate = 1e-2 # 1e-2 = 10^(-2)
optimizer = torch.optim.SGD(test.parameters(), lr=learning_rate)

# 6.设置训练网络的一些参数
# 记录训练的次数:
total_train_step = 0
# 记录测试的次数:
total_test_step = 0

# 记录训练的轮数:
epoch = 10

# 使用tensorboard
writer = SummaryWriter("./logs_train")

# 7.设置训练轮数、开始训练
for i in range(epoch):
    print("---------第 {} 轮训练开始--------".format(i + 1))
    # 训练开始:
    test.train() # 将网络设置为训练模式,只对某些特定的网络有作用,比如当网络中有Dropout、BatchNorm层等的时候
    for data in train_dataloader:
        imgs, targets = data
        outputs = test(imgs)
        # 记录损失
        loss = loss_function(outputs, targets)
        # 优化器优化模型
        optimizer.zero_grad() # 梯度清零
        loss.backward() # 梯度下降
        optimizer.step() # 对每个梯度进行优化

        total_train_step = total_train_step + 1
        # item():将tensor类型转为实际的值
        if (total_train_step % 100 == 0):
            print("训练次数: {}, 损失值为: {}".format(total_train_step, loss.item()))
            writer.add_scalar("train_loss", loss.item(),  total_train_step)

    # 8. 测试模型
    # 如何知道模型有没有训练好---------进行测试,在测试集上跑一遍,用测试数据集上的损失或正确率来评估模型有没有训练好
    # 测试步骤:
    test.eval() # 将网络模式设置为测试状态,也是只对特定的网络有作用,比如当网络中有Dropout、BatchNorm层等的时候
    total_test_loss = 0
    total_accuracy = 0
    test.eval()
    with torch.no_grad():# 将网络模型中的梯度消失,只需要测试,不需要对梯度进行调整,也不需要利用梯度来优化
        for data in test_dataloader:
            imgs, targets = data
            outputs = test(imgs)
            loss = loss_function(outputs, targets)
            total_test_loss += loss
            # 正确率
            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, total_test_step)
    total_test_step += 1

    # 9. 保存模型
    # 一般情况下是保存每一轮训练的模型结果
    torch.save(test, "test_{}.pth".format(i))
    # torch.save(test.state_dict(), "test_{}.pth".format(i))
    print("模型已保存")

利用GPU训练

利用GPU训练方式1

找到网络模型数据(输入、标注)、损失函数;在后面添加.cuda(),例如:

test = Test()
if torch.cuda.is_available(): # 这样写也可以
    test = test.cuda()

注意:数据是指在训练网络进行读取数据的时候设置的。

for data in train_data_loader:
	imgs, targets = data
        if torch.cuda.is_available():
            imgs = imgs.cuda()
            targets = targets.cuda()
....

完整代码如下:

import time

import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# from model import *

# 1.准备数据集
train_data = torchvision.datasets.CIFAR10('./CIFAR10', train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)

test_data = torchvision.datasets.CIFAR10('./CIFAR10', train=False, transform=torchvision.transforms.ToTensor(),
                                         download=True)

# 获得数据集的长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 格式化字符串的方式
print("训练数据集的长度为: {}".format(train_data_size)) # 50000
print("测试数据集的长度为: {}".format(test_data_size)) # 10000

# 2.利用dataloader加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

# 3.创建网络模型
class Test(nn.Module):
    def __init__(self):
        super(Test, 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
test = Test()
if torch.cuda.is_available():
    test = test.cuda()

# 4.创建损失函数
loss_function = nn.CrossEntropyLoss()
if torch.cuda.is_available():
    loss_function = loss_function.cuda()

# 5.创建优化器
# learning_rate = 0.01
learning_rate = 1e-2 # 1e-2 = 10^(-2)
optimizer = torch.optim.SGD(test.parameters(), lr=learning_rate)

# 6.设置训练网络的一些参数
# 记录训练的次数:
total_train_step = 0
# 记录测试的次数:
total_test_step = 0

# 记录训练的轮数:
epoch = 10

# 使用tensorboard
writer = SummaryWriter("./logs_train")
start_time = time.time()
# 7.设置训练轮数、开始训练
for i in range(epoch):
    print("---------第 {} 轮训练开始--------".format(i + 1))
    # 训练开始:
    test.train() # 将网络设置为训练模式,只对某些特定的网络有作用,比如当网络中有Dropout、BatchNorm层等的时候
    for data in train_dataloader:
        imgs, targets = data
        if torch.cuda.is_available():
            imgs = imgs.cuda()
            targets = targets.cuda()
        outputs = test(imgs)
        # 记录损失
        loss = loss_function(outputs, targets)
        # 优化器优化模型
        optimizer.zero_grad() # 梯度清零
        loss.backward() # 梯度下降
        optimizer.step() # 对每个梯度进行优化

        total_train_step = total_train_step + 1
        # item():将tensor类型转为实际的值
        if (total_train_step % 100 == 0):
            end_time = time.time()
            print(end_time - start_time)
            print("训练次数: {}, 损失值为: {}".format(total_train_step, loss.item()))
            writer.add_scalar("train_loss", loss.item(),  total_train_step)

    # 8. 测试模型
    # 如何知道模型有没有训练好---------进行测试,在测试集上跑一遍,用测试数据集上的损失或正确率来评估模型有没有训练好
    # 测试步骤:
    test.eval() # 将网络模式设置为测试状态,也是只对特定的网络有作用,比如当网络中有Dropout、BatchNorm层等的时候
    total_test_loss = 0
    total_accuracy = 0
    test.eval()
    with torch.no_grad():# 将网络模型中的梯度消失,只需要测试,不需要对梯度进行调整,也不需要利用梯度来优化
        for data in test_dataloader:
            imgs, targets = data
            if torch.cuda.is_available():
                imgs = imgs.cuda()
                targets = targets.cuda()
            outputs = test(imgs)
            loss = loss_function(outputs, targets)
            total_test_loss += loss
            # 正确率
            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, total_test_step)
    total_test_step += 1

    # 9. 保存模型
    # 一般情况下是保存每一轮训练的模型结果
    torch.save(test, "test_{}.pth".format(i))
    # torch.save(test.state_dict(), "test_{}.pth".format(i))
    print("模型已保存")

writer.close()

cpu上的运行结果:
在这里插入图片描述

由于我电脑不支持gpu的使用,因此我将代码复制到了kaggle上进行了一个运行,可以看到运行时间比在cpu上的运行时间提高了6-7倍。
在这里插入图片描述
使用博主推荐的colal上进行运行,可得到如下的运行结果,也是比较快的。。
在这里插入图片描述

利用GPU训练的另一种方式

使用.to(device), 其中device = torch.device("cpu")。例如:

torch.device("cuda")
torch.device("cuda:0") # 第一张显卡
torch.device("cuda:1") # 第二张显卡

修改代码为:

import time

import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# from model import *

# 定义训练的设备
device = torch.device("cuda")
# 1.准备数据集
train_data = torchvision.datasets.CIFAR10('./CIFAR10', train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)

test_data = torchvision.datasets.CIFAR10('./CIFAR10', train=False, transform=torchvision.transforms.ToTensor(),
                                         download=True)

# 获得数据集的长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 格式化字符串的方式
print("训练数据集的长度为: {}".format(train_data_size)) # 50000
print("测试数据集的长度为: {}".format(test_data_size)) # 10000

# 2.利用dataloader加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

# 3.创建网络模型
class Test(nn.Module):
    def __init__(self):
        super(Test, 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
test = Test()
# if torch.cuda.is_available():
#     test = test.cuda()
test.to(device)

# 4.创建损失函数
loss_function = nn.CrossEntropyLoss()
# if torch.cuda.is_available():
#     loss_function = loss_function.cuda()
loss_function.to(device)

# 5.创建优化器
# learning_rate = 0.01
learning_rate = 1e-2 # 1e-2 = 10^(-2)
optimizer = torch.optim.SGD(test.parameters(), lr=learning_rate)

# 6.设置训练网络的一些参数
# 记录训练的次数:
total_train_step = 0
# 记录测试的次数:
total_test_step = 0

# 记录训练的轮数:
epoch = 10

# 使用tensorboard
writer = SummaryWriter("./logs_train")
start_time = time.time()
# 7.设置训练轮数、开始训练
for i in range(epoch):
    print("---------第 {} 轮训练开始--------".format(i + 1))
    # 训练开始:
    test.train() # 将网络设置为训练模式,只对某些特定的网络有作用,比如当网络中有Dropout、BatchNorm层等的时候
    for data in train_dataloader:
        imgs, targets = data
        # if torch.cuda.is_available():
        #     imgs = imgs.cuda()
        #     targets = targets.cuda()
        imgs = imgs.to(device)
        targets = targets.to(device)
        outputs = test(imgs)
        # 记录损失
        loss = loss_function(outputs, targets)
        # 优化器优化模型
        optimizer.zero_grad() # 梯度清零
        loss.backward() # 梯度下降
        optimizer.step() # 对每个梯度进行优化

        total_train_step = total_train_step + 1
        # item():将tensor类型转为实际的值
        if (total_train_step % 100 == 0):
            end_time = time.time()
            print(end_time - start_time)
            print("训练次数: {}, 损失值为: {}".format(total_train_step, loss.item()))
            writer.add_scalar("train_loss", loss.item(),  total_train_step)

    # 8. 测试模型
    # 如何知道模型有没有训练好---------进行测试,在测试集上跑一遍,用测试数据集上的损失或正确率来评估模型有没有训练好
    # 测试步骤:
    test.eval() # 将网络模式设置为测试状态,也是只对特定的网络有作用,比如当网络中有Dropout、BatchNorm层等的时候
    total_test_loss = 0
    total_accuracy = 0
    test.eval()
    with torch.no_grad():# 将网络模型中的梯度消失,只需要测试,不需要对梯度进行调整,也不需要利用梯度来优化
        for data in test_dataloader:
            imgs, targets = data
            # if torch.cuda.is_available():
            #     imgs = imgs.cuda()
            #     targets = targets.cuda()
            imgs = imgs.to(device)
            targets = targets.to(device)
            outputs = test(imgs)
            loss = loss_function(outputs, targets)
            total_test_loss += loss
            # 正确率
            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, total_test_step)
    total_test_step += 1

    # 9. 保存模型
    # 一般情况下是保存每一轮训练的模型结果
    torch.save(test, "test_{}.pth".format(i))
    # torch.save(test.state_dict(), "test_{}.pth".format(i))
    print("模型已保存")
writer.close()

结果如下:
在这里插入图片描述

注意:模型、损失函数不需要另外赋值,即model.to(device)即可,而数据(输入、标注)需要赋值。方便记忆:可以都进行赋值。

对于单显卡,以下两种写法没有区别:

device = torch.device("cuda")
devide = torch.device("cuda:0")

也有人这样写:

devide = torch.device("cuda" if torch.cuda.is_avaliable() else "cpu")

完整的模型验证套路

核心:利用已经训练好的模型,然后给它提供输入。
完整代码:

import torch
import torchvision
from PIL import Image
from torch import nn

image_path = "./images/airplane.png"

image = Image.open(image_path)

print(image)

image = image.convert('RGB')
# png 格式是四个通道,除了RGB三通道外,还有一个透明通道,所以我们调用image = image.convert('RGB'),保留其颜色通道;
# 当然,如果图片本来就是三个颜色通道,经过此操作不变
# 加上这一步之后,可以适应png、jpg等各种格式的图片

tranform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
                                           torchvision.transforms.ToTensor()])

image = tranform(image)
print(image.shape) # torch.Size([3, 32, 32])

class Test(nn.Module):

    def __init__(self):
        super(Test, 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

# 直接加载会报错: Attempting to deserialize object on a CUDA device but torch.cuda.is_available() is False. If you are running on a CPU-only machine, please use torch.load with
# 需要添加参数:map_location=torch.device('cpu')
model = torch.load("test_9_gpu.pth", map_location=torch.device('cpu'))
print(model)
# Expected 4-dimensional input for 4-dimensional weight [32, 3, 5, 5], but got 3-dimensional input of size [3, 32, 32] instead
# 解决:
image = torch.reshape(image, (1, 3, 32, 32))
model.eval()
with torch.no_grad():
    output = model(image)
print(output)
# 结果:
print(output.argmax(1))

我使用了一张飞机的图片,结果如下所示:
在这里插入图片描述

kaggle&colab gpu使用

kaggle为每位用户提供每周30h的免费GPU使用时间。目前我电脑没有gpu,因此,想先用着这上面的先进行学习吧。

【最后】总结

可以尝试看一些开源项目。平时学习的时候可以多看官方文档,多多练习,目前感觉算是入门啦,非常感谢up主。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值