从零开始教你用Pytorch搭建CNN模型

目录

CNN搭建(PyTorch)

一、前期准备

1、PyTorch环境配置

1、创建虚拟环境

2、配置GitHub上下载的项目

3、jupyter的使用

2、加载数据初认识

1、Dataset

2、Dataloader

3、使用Python Console查看类的属性

4、Tensorboard

5、Transforms

6、torchvision中数据集的使用

二、模型搭建

7、nn.Module

7.1模型size

7.2模型conv

7.2.1卷积过程

7.2.2卷积效果:

7.3模型pooling

7.4模型非线性激活

7.5模型全连接

三、搭建小实战

8、CIFAR10模型

9、Lose Function

9.1基本使用

9.2实际作用

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

10、vgg16

10.1模型的保存与读取

五、完整的模型训练套路

11、模型训练

11.1train

11.2model

12、模型测试

12.1test


pytorch官网:torchvision — Torchvision master documentation (pytorch.org)

一、前期准备

1、PyTorch环境配置

1、创建虚拟环境

1、conda create -n pycyy python=3.7 -c 镜像地址

以下是一些常见的conda镜像地址:

  • 清华大学https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
  • 阿里云https://mirrors.aliyun.com/anaconda/pkgs/main/
  • 北京外国语大学https://mirrors.bfsu.edu.cn/anaconda/pkgs/main/

2、conda activate pycyy

3、在pytorch官网复制上述python版本对应的pytorch配置代码,如:conda install pytorch torchvision torchaudio cpuonly -c pytorch

[i]

2、配置GitHub上下载的项目

1、利用PyCharm打开项目:File->Open

2、配置相应的虚拟环境:File->Setting->Project->Python解释器。然后选择对应的虚拟环境

3、直接运行代码,右键->run

4、如果提示某些包没有发现,可以用

  1. conda activate pycyy

  2. conda install 包名

  3. pip insatll 包名

利用搜索引擎找原因 - 包名不对、通道不对,或者其他原因

5、可选 - 最好把requirements.txt文件内容当做参考,有选择性使用

3、jupyter的使用
  1. conda activate pycyy

  2. jupyter notebook D:

[i]

Jupyter Notebook(pycyy)属性:

[i]

目标:D:\software\anaconda\Ana\python.exe D:\software\anaconda\Ana\cwp.py D:\software\anaconda\Ana\envs\pycyy D:\software\anaconda\Ana\envs\pycyy\python.exe D:\software\anaconda\Ana\envs\pycyy\Scripts\jupyter-notebook-script.py "%USERPROFILE%/"

将"%USERPROFILE%/"改为"D:\"即打开Jupyter Notebook(pycyy)直接进入D盘,不用在每次都执行jupyter notebook D:命令

2、加载数据初认识

1、Dataset

提供一种方式获取数据及其label

  1. 如何获取每一个数据及其label

  2. 告诉我们总共有多少个数据

2、Dataloader

为网络提供不同的数据形式(如压缩形式)

3、使用Python Console查看类的属性

可以看到图片的宽、高等相关属性信息

4、Tensorboard

from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs")
# writer.add_image()
for i in range(100):
    writer.add_scalar("y=x",i,i)
writer.close()

tensorboard --logdir=logs --port=6007,指定端口打开tensorboard 页面

tensorboard 页面如下:

绘制了一个y=x的函数

5、Transforms

from PIL import Image
​
from torchvision import transforms
image_path = "data/train/ants_image/6743948_2b8c096dda.jpg"
img = Image.open(image_path)
tensor_trans = transforms.ToTensor()
tensor_img = tensor_trans(img)
print(tensor_img)
​
D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\Transforms.py 
tensor([[[0.6549, 0.5451, 0.5765,  ..., 0.7451, 0.7451, 0.7647],
         [0.4078, 0.4471, 0.5373,  ..., 0.8118, 0.8431, 0.8627],
         [0.3529, 0.5804, 0.7490,  ..., 0.6824, 0.8314, 0.8824],
         ...,
         [0.5490, 0.5412, 0.4353,  ..., 0.6510, 0.6706, 0.6275],
         [0.8824, 0.5020, 0.8353,  ..., 0.6745, 0.6706, 0.5608],
         [0.6235, 0.3961, 0.7765,  ..., 0.7765, 0.6784, 0.6196]],

        [[0.6235, 0.5020, 0.5294,  ..., 0.6706, 0.6353, 0.6353],
         [0.3529, 0.3922, 0.4824,  ..., 0.7451, 0.7608, 0.7569],
         [0.2863, 0.5098, 0.6863,  ..., 0.6196, 0.7725, 0.8157],
         ...,
         [0.4824, 0.4667, 0.3529,  ..., 0.5569, 0.5843, 0.5529],
         [0.8314, 0.4392, 0.7608,  ..., 0.5843, 0.5882, 0.4863],
         [0.5922, 0.3490, 0.7059,  ..., 0.6902, 0.5961, 0.5490]],

        [[0.5412, 0.4157, 0.4353,  ..., 0.6118, 0.5804, 0.5686],
         [0.3529, 0.3804, 0.4471,  ..., 0.6431, 0.6471, 0.6392],
         [0.3569, 0.5569, 0.6902,  ..., 0.4784, 0.6118, 0.6431],
         ...,
         [0.4039, 0.4000, 0.2863,  ..., 0.5098, 0.5294, 0.4941],
         [0.7569, 0.3765, 0.7059,  ..., 0.5216, 0.5216, 0.4196],
         [0.5176, 0.2863, 0.6588,  ..., 0.6078, 0.5137, 0.4627]]])

Process finished with exit code 0

6、torchvision中数据集的使用

import torchvision
from torch.utils.tensorboard import SummaryWriter
​
dataset_transform = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
])
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=dataset_transform, download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=dataset_transform, download=True)
# img, target = test_set[0]
# print(img)
# print(target)
# img.show()
​
writer = SummaryWriter("p10")
for i in range(10):
    img,target = test_set[i]
    writer.add_image("test_set", img, i)
    writer.close()

[i]

二、模型搭建

7、nn.Module

Neural network简称nn,nn.Module为神经网络的基本骨架的搭建

import torch
from torch import nn
​
class Jzh(nn.Module):
    # 初始化
    def __int__(self):
        super(Jzh, self).__int__()
​
    # 功能函数
    def forward(self, input):
        output = input + 1
        return output
​
jzh = Jzh()
x = torch.tensor(1.0)
output = jzh(x)
print(output)

7.1模型size
import torch
​
# 输入图像
input = torch.tensor([[1, 2, 0, 3, 1],
                      [0, 1, 2, 3, 1],
                      [1, 2, 1, 0, 0],
                      [5, 2, 3, 1, 1],
                      [2, 1, 0, 1, 1]])
# 卷积核
kernel = torch.tensor([[1, 2, 1],
                       [0, 1, 0],
                       [2, 1, 0]])
​
# 换一种数据的输入方式,这样打印出来的数据会有所不同
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))
​
print(input.shape)
print(kernel.shape)

如下是不做torch.reshape()操作输出的数据尺寸:

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_conv.py 
torch.Size([5, 5])
torch.Size([3, 3])

Process finished with exit code 0

如下是经过orch.reshape()操作输出的数据尺寸:

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_conv.py 
torch.Size([1, 1, 5, 5])
torch.Size([1, 1, 3, 3])

Process finished with exit code 0

7.2模型conv
7.2.1卷积过程
import torch
import torch.nn.functional as F
​
# 输入图像
input = torch.tensor([[1, 2, 0, 3, 1],
                      [0, 1, 2, 3, 1],
                      [1, 2, 1, 0, 0],
                      [5, 2, 3, 1, 1],
                      [2, 1, 0, 1, 1]])
# 卷积核
kernel = torch.tensor([[1, 2, 1],
                       [0, 1, 0],
                       [2, 1, 0]])
​
# 换一种数据的输入方式,这样打印出来的数据会有所不同
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))
​
print(input.shape)
print(kernel.shape)
​
output = F.conv2d(input, kernel, stride=1)
print(output)

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_conv.py 
torch.Size([1, 1, 5, 5])
torch.Size([1, 1, 3, 3])
tensor([[[[10, 12, 12],
          [18, 16, 16],
          [13,  9,  3]]]])

Process finished with exit code 0

若再补充如下代码:

output2 = F.conv2d(input, kernel, stride=1, padding=1)
print(output2)

则输出为:

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_conv.py 
torch.Size([1, 1, 5, 5])
torch.Size([1, 1, 3, 3])
tensor([[[[10, 12, 12],
          [18, 16, 16],
          [13,  9,  3]]]])
tensor([[[[ 1,  3,  4, 10,  8],
          [ 5, 10, 12, 12,  6],
          [ 7, 18, 16, 16,  8],
          [11, 13,  9,  3,  4],
          [14, 13,  9,  7,  4]]]])

Process finished with exit code 0

7.2.2卷积效果:
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
​
dataset = torchvision.datasets.CIFAR10("./data_conv2d", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)
dataloader = DataLoader(dataset, batch_size=64)
​
class Jzh(nn.Module):
    def __init__(self):
        super(Jzh, self).__init__()
        # out_channels实际上就是卷积核的数量,有多少个卷积之后就输出几幅图像
        self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
​
    def forward(self, x):
        x = self.conv1(x)
        return x
​
jzh = Jzh()
print(jzh)
​
writer = SummaryWriter("./logs")
step = 0
for data in dataloader:
    imgs, targets = data
    output = jzh(imgs)
    print(imgs.shape)
    print(output.shape)
    # 输入时的大小:torch.Size([64, 3, 32, 32])
    writer.add_images("input", imgs, step)
​
    # torch.Size([64, 6, 30, 30]) -> [xxx, 3, 30, 30]
    # 3保证输出的仍是三维的数据与输入时一致,这样就能正常显示输出图像
    output = torch.reshape(output, (-1, 3, 30, 30))
    # 输出时的大小:
    writer.add_images("output", output, step)
    step = step + 1
writer.close()

[i]

7.3模型pooling
import torch
from torch import nn
from torch.nn import MaxPool2d
​
input = torch.tensor([[1, 2, 0, 3, 1],
                     [0, 1, 2, 3, 1],
                     [1, 2, 1, 0, 0],
                     [5, 2, 3, 1, 1],
                     [2, 1, 0, 1, 1]], dtype=torch.float32)
​
# -1表示自动计算batch_size;1表示一个channel;5、5表示5*5的输入图像
# batch size(批量大小)是一个超参数,它指的是在进行模型训练或评估时,每次迭代所使用的数据样本的数量。
input = torch.reshape(input, (-1, 1, 5, 5))
print(input.shape)
​
class Jzh(nn.Module):
    def __init__(self):
        super(Jzh, self).__init__()
        # ceil_mode表示不足三列或者三行的按照三行然后去池中的最大值
        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
​
    def forward(self, input):
        output = self.maxpool1(input)
        return output
​
jzh = Jzh()
output = jzh(input)
print(output)

经池化后输出为:

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_maxpool.py 
torch.Size([1, 1, 5, 5])
tensor([[[[2., 3.],
          [5., 1.]]]])

Process finished with exit code 0

加载CIRF10数据集进行池化操作:

import torch
import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
​
dataset = torchvision.datasets.CIFAR10("./data_pool", train=False, download=True,
                                       transform=torchvision.transforms.ToTensor())
​
dataloader = DataLoader(dataset, batch_size=64)
# 用于创建一个数据加载器(DataLoader),它将数据集(dataset)分成大小为64的批次。
​
class Jzh(nn.Module):
    def __init__(self):
        super(Jzh, self).__init__()
        # ceil_mode表示不足三列或者三行的按照三行然后去池中的最大值
        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
​
    def forward(self, input):
        output = self.maxpool1(input)
        return output
​
jzh = Jzh()
​
writer = SummaryWriter("logs_maxpool")
step = 0
for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, step)
    output = jzh(imgs)
    writer.add_images("output", output, step)
    step = step+1
​
writer.close()

[i]

7.4模型非线性激活
import torch
import torchvision
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
​
input = torch.tensor([[1, -0.5],
                      [-1, 3]])
input = torch.reshape(input, (-1, 1, 2, 2))
print(input.shape)
​
dataset = torchvision.datasets.CIFAR10("./data_pool", train=False, download=True,
                                       transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=64)
class Jzh(nn.Module):
    def __init__(self):
        super().__init__()
        self.relu1 = ReLU()
        self.sigmoid1 = Sigmoid()
​
​
    def forward(self, input):
        # 将输入的实数值映射到介于0和1之间的输出
        output = self.sigmoid1(input)
        return output
​
jzh=Jzh()
writer = SummaryWriter("logs_sigmoid1")
step = 0
for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, global_step=step)
    output = jzh(imgs)
    writer.add_images("output", output, step)
    step = step+1
​
writer.close()

激活函数向图像引入非线性特征:

[i]

7.5模型全连接
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
​
dataset = torchvision.datasets.CIFAR10("./data_linear", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)
dataloader = DataLoader(dataset, batch_size=64, drop_last=True)
​
class Jzh(nn.Module):
    def __init__(self):
        super().__init__()
        # 将图片展平成torch.Size([1, 1, 1, 196608])格式
        self.linear1 = Linear(196608, 10)
​
    def forward(self, input):
        output = self.linear1(input)
        return output
​
jzh = Jzh()
​
for data in dataloader:
    imgs, target = data
    print(imgs.shape)
    # output = torch.reshape(imgs, (1, 1, 1, -1))
    # 上述代码可以由下一行代码替换,并直接输出成torch.Size([196608])
    output =torch.flatten(imgs)
    print(output.shape)
    output = jzh(output)
    print(output.shape)

三、搭建小实战

8、CIFAR10模型

Inputs -> 第一层Feature maps 层的计算可由如下公式得出:

[i]

计算过程为:

[ 32 + 2padding - 1(5-1) - 1 ] / stride + 1 = 32 , 得出padding=2,stride=1

对应代码为:self.conv1 = Conv2d(3, 32, 5, padding=2)

import torch
from torch import nn
from torch.nn import MaxPool2d, Flatten, Linear, Conv2d, Sequential
from torch.utils.tensorboard import SummaryWriter
​
​
class Jzh(nn.Module):
    def __init__(self):
        super().__init__()
        # 第一种写法
        # self.conv1 = Conv2d(3, 32, 5, padding=2)
        # self.maxpool1 = MaxPool2d(2)
        # self.conv2 = Conv2d(32, 32, 5, padding=2)
        # self.maxpool2 = MaxPool2d(2)
        # self.conv3 = Conv2d(32, 64, 5, padding=2)
        # self.maxpool3 = MaxPool2d(2)
        # self.flatten = Flatten()
        # self.linear1 = Linear(1024, 64)
        # self.linear2 = Linear(64, 10)
​
        # 第二种写法
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )
​
    def forward(self, x):
        # 第一种写法
        # x = self.conv1(x)
        # x = self.maxpool1(x)
        # x = self.conv2(x)
        # x = self.maxpool2(x)
        # x = self.conv3(x)
        # x = self.maxpool3(x)
        # x = self.flatten(x)
        # x = self.linear1(x)
        # x = self.linear2(x)
​
        # 第二种写法
        x = self.model1(x)
​
        return x
​
jzh = Jzh()
print(jzh)
# torch.ones((64, 3, 32, 32))里的参数注意和Conv2d(32, 32, 5, padding=2)作区分,这里的64指batch_size
input = torch.ones((64, 3, 32, 32))
output = jzh(input)
# 检验输出结果是否为torch.Size([64, 10]),因为这是一个是分类的模型,若是则证明self.linear2 = Linear(64, 10)正常执行
print(output.shape)
​
​
writer = SummaryWriter("./logs_seq")
# jzh 是一个 PyTorch 模型,input 是输入数据,这个函数会将模型的计算图添加到 TensorBoard,以便在可视化时查看模型的结构
writer.add_graph(jzh, input)
writer.close()
​

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_CIFAR10.py 
Jzh(
  (model1): Sequential(
    (0): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (4): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Flatten(start_dim=1, end_dim=-1)
    (7): Linear(in_features=1024, out_features=64, bias=True)
    (8): Linear(in_features=64, out_features=10, bias=True)
  )
)
torch.Size([64, 10])

Process finished with exit code 0

模型结构图:

[i]

9、Lose Function

9.1基本使用

LogSoftmax用于计算log-softmax激活。它的作用是将输入张量中的每个元素进行指数运算后归一化,然后取对数。这样可以将原始输出转换为概率分布,便于在分类任务中使用

计算公式如下:

注意,深度学习中的log默认是以e为底的即ln

import torch
from torch import nn
from torch.nn import L1Loss, MSELoss
​
inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
​
inputs = torch.reshape(inputs, (1, 1, 1, 3))
targets = torch.reshape(targets, (1, 1, 1, 3))
​
loss = L1Loss()
# result=(0+0+2)/3
result = loss(inputs, targets)
​
loss_mse = MSELoss()
result_mse = loss_mse(inputs, targets)
​
print(result)
print(result_mse)
​
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x, (1, 3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x, y)
print(result_cross)

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_loss.py 
tensor(0.6667)
tensor(1.3333)
tensor(1.1019)

Process finished with exit code 0

9.2实际作用
  1. 计算实际输出和目标之间的差距

    import torch
    import torchvision
    from torch import nn
    from torch.nn import MaxPool2d, Flatten, Linear, Conv2d, Sequential
    from torch.utils.data import DataLoader
    from torch.utils.tensorboard import SummaryWriter
    ​
    dataset = torchvision.datasets.CIFAR10("./data_linear", train=False, transform=torchvision.transforms.ToTensor(),
                                           download=True)
    dataloader = DataLoader(dataset, batch_size=1)
    ​
    ​
    class Jzh(nn.Module):
        def __init__(self):
            super().__init__()
    ​
            # 第二种写法
            self.model1 = Sequential(
                Conv2d(3, 32, 5, padding=2),
                MaxPool2d(2),
                Conv2d(32, 32, 5, padding=2),
                MaxPool2d(2),
                Conv2d(32, 64, 5, padding=2),
                MaxPool2d(2),
                Flatten(),
                Linear(1024, 64),
                Linear(64, 10)
            )
    ​
        def forward(self, x):
    ​
            # 第二种写法
            x = self.model1(x)
            return x
    ​
    loss = nn.CrossEntropyLoss()
    jzh = Jzh()
    for data in dataloader:
        imgs, targets = data
        outputs = jzh(imgs)
        result_loss = loss(outputs, targets)
        print(result_loss)
    
    

    部分输出结果:

    D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_loss_network.py 
    Files already downloaded and verified
    tensor(2.3428, grad_fn=<NllLossBackward0>)
    tensor(2.3684, grad_fn=<NllLossBackward0>)
    tensor(2.3722, grad_fn=<NllLossBackward0>)
    tensor(2.3924, grad_fn=<NllLossBackward0>)
    tensor(2.3649, grad_fn=<NllLossBackward0>)
    tensor(2.3878, grad_fn=<NllLossBackward0>)
    tensor(2.1113, grad_fn=<NllLossBackward0>)

  2. 为我们更新输出提供一定的依据:反向传播and优化器

    主体部分不变,在上述代码中增加如下sgd优化器代码:

    loss = nn.CrossEntropyLoss()
    jzh = Jzh()
    # lr为学习速率,一般模型训练开始时设置较大,模型训练后期设置较小
    optim = torch.optim.SGD(jzh.parameters(), 0.01)
    for epoch in range(20):
        running_loss = 0.0
        # 下面这个for仅代表循环一次
        for data in dataloader:
            imgs, targets = data
            outputs = jzh(imgs)
            result_loss = loss(outputs, targets)
            # 清零梯度
            optim.zero_grad()
            # 计算每个节点参数的梯度,方便后续用优化器对其优化
            result_loss.backward()
            optim.step()
            # 这一轮整体误差总和
            running_loss = running_loss + result_loss
        print(running_loss)
    ​
    

    部分运行结果:

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\nn_optim.py 
Files already downloaded and verified
tensor(18591.2637, grad_fn=<AddBackward0>)
tensor(16061.7539, grad_fn=<AddBackward0>)
tensor(15454.4775, grad_fn=<AddBackward0>)
tensor(15985.7441, grad_fn=<AddBackward0>)
tensor(18142.6211, grad_fn=<AddBackward0>)
tensor(20303.0449, grad_fn=<AddBackward0>)
tensor(22268.1523, grad_fn=<AddBackward0>)
tensor(23856.8047, grad_fn=<AddBackward0>)
tensor(25047.0723, grad_fn=<AddBackward0>)

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

10、vgg16

import torchvision
from torchvision.models import VGG16_Weights
from torch import nn
​
# train_data = torchvision.datasets.ImageNet("./data_imagenet", split='train', download=True,
#                                            transform=torchvision.transforms.ToTensor())
​
# vgg16_false = torchvision.models.vgg16(pretrained=False)
# 设置成true则下载的是一个已经预训练过的模型
# vgg16_true = torchvision.models.vgg16(pretrained=True)
# 下列语句为新版本模型加载代码,新版本默认没有预训练,如需预训练则需加上weights=VGG16_Weights.DEFAULT
vgg16_true = torchvision.models.vgg16(weights=VGG16_Weights.DEFAULT)
print(vgg16_true)
​
train_data = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(),
                                       download=True)
# 对模型进行修改,从1000分类修改成10分类
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)

VGG1000分类和10分类输出对比:

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)
  )
)
改成10分类则在(6): Linear(in_features=4096, out_features=1000, bias=True)之后多添加了一层线性层:
 (add_linear): Linear(in_features=1000, out_features=10, bias=True)

10.1模型的保存与读取

model_save

import torch
import torchvision
from torch import nn
​
vgg16 = torchvision.models.vgg16(weights=None)
# 第一种保存方式,模型结构+模型参数
torch.save(vgg16, "vgg16_method1.pth")
​
# 第二种保存方式,模型参数(保存成字典形式),官方推荐,但这样输出的就不再是一个网络模型了,需要使用还要进行相关操作
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
​
# 方式一有陷阱(如果自己搭建模型的话)
class Jzh(nn.Module):
    def __init__(self):
        super(Jzh, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3)
​
    def forward(self, x):
        x = self.conv1(x)
        return x
​
jzh = Jzh()
torch.save(jzh, "jzh_method1.pth")

model_load

import torch
import torchvision
from model_save import *
from torch import nn

# 对应方式1,加载模型
model = torch.load("vgg16_method1.pth")
# print(model)

# 对应方式2,加载模型
vgg16 = torchvision.models.vgg16(weights=None)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
# model = torch.load("vgg16_method2.pth")
print(vgg16)

# 方式一陷阱,自己创建的模型需要在这里定义一下,但是from model_save import *下面代码就可以不要了,
# model_save文件定义过了,直接加载模型打印
# class Jzh(nn.Module):
#     def __init__(self):
#         super(Jzh, self).__init__()
#         self.conv1 = nn.Conv2d(3, 64, kernel_size=3)
#
#     def forward(self, x):
#         x = self.conv1(x)
#         return x

model = torch.load('jzh_method1.pth')
print(model)

五、完整的模型训练套路

11、模型训练

11.1train
import torch
​
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
​
from model import *
​
# 准备数据集
train_data = torchvision.datasets.CIFAR10(root="./data_taolu", train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
test_data = torchvision.datasets.CIFAR10(root="./data_taolu", 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))
print("测试数据集的长度为:{}".format(test_data_size))
​
# 利用DataLoader来加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
​
# 创建网络模型
jzh = Jzh()
​
# 创建损失函数
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(jzh.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))
    # 训练步骤开始
    jzh.train()
    for data in train_dataloader:
        imgs, targets = data
        outputs = jzh(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)
​
    # 测试步骤开始
    jzh.eval()
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():
        for data in test_dataloader:
            imgs, targets = data
            outputs = jzh(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(jzh, "jzh_{}.pth".format(i))
    print("模型已保存")
writer.close()
​
11.2model
import torch
from torch import nn
​
# 搭建神经网络
class Jzh(nn.Module):
    def __init__(self):
        super(Jzh, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(3, 32, 5, 1,  padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, 1, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, 1, padding=2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(1024, 64),
            nn.Linear(64, 10)
        )
​
    def forward(self, x):
        x = self.model(x)
        return x
​
​
if __name__ == '__main__':
    jzh = Jzh()
    input = torch.ones((64, 3, 32, 32))
    output = jzh(input)
    print(output.shape)

部分输出结果:

训练数据集的长度为:50000
测试数据集的长度为:10000
------第 1 轮训练开始------
训练次数: 100, loss: 2.2846009731292725
训练次数: 200, loss: 2.2844748497009277
训练次数: 300, loss: 2.2514917850494385
训练次数: 400, loss: 2.148555278778076
训练次数: 500, loss: 2.1040234565734863
训练次数: 600, loss: 2.033297061920166
训练次数: 700, loss: 1.9678012132644653
整体测试集上的loss: 320.23996794223785
整体测试集上的正确率: 0.26930001378059387
模型已保存
------第 2 轮训练开始------
训练次数: 800, loss: 1.8451868295669556
训练次数: 900, loss: 1.8664506673812866
训练次数: 1000, loss: 1.905165433883667
训练次数: 1100, loss: 1.979297161102295
训练次数: 1200, loss: 1.6892757415771484
训练次数: 1300, loss: 1.655542254447937
训练次数: 1400, loss: 1.725655436515808
训练次数: 1500, loss: 1.8056442737579346
整体测试集上的loss: 287.658221244812
整体测试集上的正确率: 0.3418999910354614
模型已保存

模型tensorboard图:

[i]

[i]

[i]

总结来说,完整的模型训练套路为:准备数据,加载数据,准备模型,设置损失函数,设置优化器,开始训练,最后验证,结果聚合展示

12、模型测试

12.1test
import torch
import torchvision
from PIL import Image
from torch import nn
​
image_path = "./imgs/airplan.png"
image = Image.open(image_path)
# 保留颜色通道,加上这一步后可以适应png、jpg各种格式图片
# image = image.convert('RGB')
print(image)
​
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
                                            torchvision.transforms.ToTensor()])
​
image = transform(image)
print(image.shape)
​
class Jzh(nn.Module):
    def __init__(self):
        super(Jzh, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(3, 32, 5, 1,  padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, 1, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, 1, padding=2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(1024, 64),
            nn.Linear(64, 10)
        )
​
    def forward(self, x):
        x = self.model(x)
        return x
​
model = torch.load("./jzh_9.pth")
# 若使用GPU训练的模型,一定要使用下述语句将其映射到CPU上
# model = torch.load("jzh_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)

如图为CIFAR10数据集对应的物体类别:

[i]

如下为预测结果:

D:\software\anaconda\Ana\envs\pycyy\python.exe D:\code\Python\pycyy\test.py 
<PIL.PngImagePlugin.PngImageFile image mode=RGB size=295x183 at 0x21FE599AC08>
torch.Size([3, 32, 32])
Jzh(
  (model): Sequential(
    (0): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (4): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Flatten(start_dim=1, end_dim=-1)
    (7): Linear(in_features=1024, out_features=64, bias=True)
    (8): Linear(in_features=64, out_features=10, bias=True)
  )
)
tensor([[10.5353,  1.8883,  1.5429, -2.5516,  0.0149, -4.7008, -5.5488, -5.0510,
          3.8907, -1.5681]])
tensor([0])

Process finished with exit code 0

其中airplan对应类别下标为00,其预测概率最大,模型预测正确

后续将继续学习github上优秀的开源项目,加油!!!

Author:####Jzh##

  • 26
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当利用PyTorch搭建卷积神经网络(CNN)时,需要使用`torch.nn`模块中的类来定义网络的结构和操作。以下是一个简单的示例,展示如何使用PyTorch搭建一个包含卷积层、池化层和全连接层的CNN。 ```python import torch import torch.nn as nn # 定义CNN模型 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() # 卷积层 self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1, padding=1) # 池化层 self.pool = nn.MaxPool2d(kernel_size=2, stride=2) # 全连接层 self.fc1 = nn.Linear(16 * 14 * 14, 10) # 输入尺寸:16个通道,14x14的特征图 def forward(self, x): # 前向传播 x = self.conv1(x) x = nn.ReLU()(x) x = self.pool(x) x = x.view(x.size(0), -1) # 展平特征图 x = self.fc1(x) return x # 创建CNN模型实例 model = CNN() # 打印模型结构 print(model) ``` 在上述代码中,我们定义了一个名为`CNN`的子类,继承自`nn.Module`。在`__init__`方法中,我们定义了卷积层`self.conv1`、池化层`self.pool`和全连接层`self.fc1`。在`forward`方法中,我们定义了模型的前向传播过程,通过调用卷积层、激活函数、池化层和全连接层来构建整个网络结构。 通过创建`CNN`类的实例,我们可以得到一个具有卷积层、池化层和全连接层的CNN模型。最后,我们使用`print(model)`打印模型的结构。 这只是一个简单的CNN模型示例,你可以根据具体任务的需求,自定义和添加更多的卷积层、池化层和全连接层来构建更复杂的网络结构。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值