当前位置:   article > 正文

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

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

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

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

tensorboard 页面如下:

绘制了一个y=x的函数

5、Transforms

  1. from PIL import Image
  2. from torchvision import transforms
  3. image_path = "data/train/ants_image/6743948_2b8c096dda.jpg"
  4. img = Image.open(image_path)
  5. tensor_trans = transforms.ToTensor()
  6. tensor_img = tensor_trans(img)
  7. 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中数据集的使用

  1. import torchvision
  2. from torch.utils.tensorboard import SummaryWriter
  3. dataset_transform = torchvision.transforms.Compose([
  4.    torchvision.transforms.ToTensor()
  5. ])
  6. train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=dataset_transform, download=True)
  7. test_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=dataset_transform, download=True)
  8. # img, target = test_set[0]
  9. # print(img)
  10. # print(target)
  11. # img.show()
  12. writer = SummaryWriter("p10")
  13. for i in range(10):
  14.    img,target = test_set[i]
  15.    writer.add_image("test_set", img, i)
  16.    writer.close()

[i]

二、模型搭建

7、nn.Module

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

  1. import torch
  2. from torch import nn
  3. class Jzh(nn.Module):
  4.    # 初始化
  5.    def __int__(self):
  6.        super(Jzh, self).__int__()
  7.    # 功能函数
  8.    def forward(self, input):
  9.        output = input + 1
  10.        return output
  11. jzh = Jzh()
  12. x = torch.tensor(1.0)
  13. output = jzh(x)
  14. print(output)
7.1模型size
  1. import torch
  2. # 输入图像
  3. input = torch.tensor([[1, 2, 0, 3, 1],
  4.                     [0, 1, 2, 3, 1],
  5.                     [1, 2, 1, 0, 0],
  6.                     [5, 2, 3, 1, 1],
  7.                     [2, 1, 0, 1, 1]])
  8. # 卷积核
  9. kernel = torch.tensor([[1, 2, 1],
  10.                       [0, 1, 0],
  11.                       [2, 1, 0]])
  12. # 换一种数据的输入方式,这样打印出来的数据会有所不同
  13. input = torch.reshape(input, (1, 1, 5, 5))
  14. kernel = torch.reshape(kernel, (1, 1, 3, 3))
  15. print(input.shape)
  16. 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卷积过程
  1. import torch
  2. import torch.nn.functional as F
  3. # 输入图像
  4. input = torch.tensor([[1, 2, 0, 3, 1],
  5.                     [0, 1, 2, 3, 1],
  6.                     [1, 2, 1, 0, 0],
  7.                     [5, 2, 3, 1, 1],
  8.                     [2, 1, 0, 1, 1]])
  9. # 卷积核
  10. kernel = torch.tensor([[1, 2, 1],
  11.                       [0, 1, 0],
  12.                       [2, 1, 0]])
  13. # 换一种数据的输入方式,这样打印出来的数据会有所不同
  14. input = torch.reshape(input, (1, 1, 5, 5))
  15. kernel = torch.reshape(kernel, (1, 1, 3, 3))
  16. print(input.shape)
  17. print(kernel.shape)
  18. output = F.conv2d(input, kernel, stride=1)
  19. 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

若再补充如下代码:

  1. output2 = F.conv2d(input, kernel, stride=1, padding=1)
  2. 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卷积效果:
  1. import torch
  2. import torchvision
  3. from torch import nn
  4. from torch.nn import Conv2d
  5. from torch.utils.data import DataLoader
  6. from torch.utils.tensorboard import SummaryWriter
  7. dataset = torchvision.datasets.CIFAR10("./data_conv2d", train=False, transform=torchvision.transforms.ToTensor(),
  8.                                       download=True)
  9. dataloader = DataLoader(dataset, batch_size=64)
  10. class Jzh(nn.Module):
  11.    def __init__(self):
  12.        super(Jzh, self).__init__()
  13.        # out_channels实际上就是卷积核的数量,有多少个卷积之后就输出几幅图像
  14.        self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
  15.    def forward(self, x):
  16.        x = self.conv1(x)
  17.        return x
  18. jzh = Jzh()
  19. print(jzh)
  20. writer = SummaryWriter("./logs")
  21. step = 0
  22. for data in dataloader:
  23.    imgs, targets = data
  24.    output = jzh(imgs)
  25.    print(imgs.shape)
  26.    print(output.shape)
  27.    # 输入时的大小:torch.Size([64, 3, 32, 32])
  28.    writer.add_images("input", imgs, step)
  29.    # torch.Size([64, 6, 30, 30]) -> [xxx, 3, 30, 30]
  30.    # 3保证输出的仍是三维的数据与输入时一致,这样就能正常显示输出图像
  31.    output = torch.reshape(output, (-1, 3, 30, 30))
  32.    # 输出时的大小:
  33.    writer.add_images("output", output, step)
  34.    step = step + 1
  35. writer.close()

[i]

7.3模型pooling
  1. import torch
  2. from torch import nn
  3. from torch.nn import MaxPool2d
  4. input = torch.tensor([[1, 2, 0, 3, 1],
  5.                     [0, 1, 2, 3, 1],
  6.                     [1, 2, 1, 0, 0],
  7.                     [5, 2, 3, 1, 1],
  8.                     [2, 1, 0, 1, 1]], dtype=torch.float32)
  9. # -1表示自动计算batch_size;1表示一个channel;5、5表示5*5的输入图像
  10. # batch size(批量大小)是一个超参数,它指的是在进行模型训练或评估时,每次迭代所使用的数据样本的数量。
  11. input = torch.reshape(input, (-1, 1, 5, 5))
  12. print(input.shape)
  13. class Jzh(nn.Module):
  14.    def __init__(self):
  15.        super(Jzh, self).__init__()
  16.        # ceil_mode表示不足三列或者三行的按照三行然后去池中的最大值
  17.        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
  18.    def forward(self, input):
  19.        output = self.maxpool1(input)
  20.        return output
  21. jzh = Jzh()
  22. output = jzh(input)
  23. 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数据集进行池化操作:

  1. import torch
  2. import torchvision
  3. from torch import nn
  4. from torch.nn import MaxPool2d
  5. from torch.utils.data import DataLoader
  6. from torch.utils.tensorboard import SummaryWriter
  7. dataset = torchvision.datasets.CIFAR10("./data_pool", train=False, download=True,
  8.                                       transform=torchvision.transforms.ToTensor())
  9. dataloader = DataLoader(dataset, batch_size=64)
  10. # 用于创建一个数据加载器(DataLoader),它将数据集(dataset)分成大小为64的批次。
  11. class Jzh(nn.Module):
  12.    def __init__(self):
  13.        super(Jzh, self).__init__()
  14.        # ceil_mode表示不足三列或者三行的按照三行然后去池中的最大值
  15.        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
  16.    def forward(self, input):
  17.        output = self.maxpool1(input)
  18.        return output
  19. jzh = Jzh()
  20. writer = SummaryWriter("logs_maxpool")
  21. step = 0
  22. for data in dataloader:
  23.    imgs, targets = data
  24.    writer.add_images("input", imgs, step)
  25.    output = jzh(imgs)
  26.    writer.add_images("output", output, step)
  27.    step = step+1
  28. writer.close()

[i]

7.4模型非线性激活
  1. import torch
  2. import torchvision
  3. from torch import nn
  4. from torch.nn import ReLU, Sigmoid
  5. from torch.utils.data import DataLoader
  6. from torch.utils.tensorboard import SummaryWriter
  7. input = torch.tensor([[1, -0.5],
  8.                     [-1, 3]])
  9. input = torch.reshape(input, (-1, 1, 2, 2))
  10. print(input.shape)
  11. dataset = torchvision.datasets.CIFAR10("./data_pool", train=False, download=True,
  12.                                       transform=torchvision.transforms.ToTensor())
  13. dataloader = DataLoader(dataset, batch_size=64)
  14. class Jzh(nn.Module):
  15.    def __init__(self):
  16.        super().__init__()
  17.        self.relu1 = ReLU()
  18.        self.sigmoid1 = Sigmoid()
  19.    def forward(self, input):
  20.        # 将输入的实数值映射到介于0和1之间的输出
  21.        output = self.sigmoid1(input)
  22.        return output
  23. jzh=Jzh()
  24. writer = SummaryWriter("logs_sigmoid1")
  25. step = 0
  26. for data in dataloader:
  27.    imgs, targets = data
  28.    writer.add_images("input", imgs, global_step=step)
  29.    output = jzh(imgs)
  30.    writer.add_images("output", output, step)
  31.    step = step+1
  32. writer.close()

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

[i]

7.5模型全连接
  1. import torch
  2. import torchvision
  3. from torch import nn
  4. from torch.nn import Linear
  5. from torch.utils.data import DataLoader
  6. dataset = torchvision.datasets.CIFAR10("./data_linear", train=False, transform=torchvision.transforms.ToTensor(),
  7.                                       download=True)
  8. dataloader = DataLoader(dataset, batch_size=64, drop_last=True)
  9. class Jzh(nn.Module):
  10.    def __init__(self):
  11.        super().__init__()
  12.        # 将图片展平成torch.Size([1, 1, 1, 196608])格式
  13.        self.linear1 = Linear(196608, 10)
  14.    def forward(self, input):
  15.        output = self.linear1(input)
  16.        return output
  17. jzh = Jzh()
  18. for data in dataloader:
  19.    imgs, target = data
  20.    print(imgs.shape)
  21.    # output = torch.reshape(imgs, (1, 1, 1, -1))
  22.    # 上述代码可以由下一行代码替换,并直接输出成torch.Size([196608])
  23.    output =torch.flatten(imgs)
  24.    print(output.shape)
  25.    output = jzh(output)
  26.    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)

  1. import torch
  2. from torch import nn
  3. from torch.nn import MaxPool2d, Flatten, Linear, Conv2d, Sequential
  4. from torch.utils.tensorboard import SummaryWriter
  5. class Jzh(nn.Module):
  6.    def __init__(self):
  7.        super().__init__()
  8.        # 第一种写法
  9.        # self.conv1 = Conv2d(3, 32, 5, padding=2)
  10.        # self.maxpool1 = MaxPool2d(2)
  11.        # self.conv2 = Conv2d(32, 32, 5, padding=2)
  12.        # self.maxpool2 = MaxPool2d(2)
  13.        # self.conv3 = Conv2d(32, 64, 5, padding=2)
  14.        # self.maxpool3 = MaxPool2d(2)
  15.        # self.flatten = Flatten()
  16.        # self.linear1 = Linear(1024, 64)
  17.        # self.linear2 = Linear(64, 10)
  18.        # 第二种写法
  19.        self.model1 = Sequential(
  20.            Conv2d(3, 32, 5, padding=2),
  21.            MaxPool2d(2),
  22.            Conv2d(32, 32, 5, padding=2),
  23.            MaxPool2d(2),
  24.            Conv2d(32, 64, 5, padding=2),
  25.            MaxPool2d(2),
  26.            Flatten(),
  27.            Linear(1024, 64),
  28.            Linear(64, 10)
  29.       )
  30.    def forward(self, x):
  31.        # 第一种写法
  32.        # x = self.conv1(x)
  33.        # x = self.maxpool1(x)
  34.        # x = self.conv2(x)
  35.        # x = self.maxpool2(x)
  36.        # x = self.conv3(x)
  37.        # x = self.maxpool3(x)
  38.        # x = self.flatten(x)
  39.        # x = self.linear1(x)
  40.        # x = self.linear2(x)
  41.        # 第二种写法
  42.        x = self.model1(x)
  43.        return x
  44. jzh = Jzh()
  45. print(jzh)
  46. # torch.ones((64, 3, 32, 32))里的参数注意和Conv2d(32, 32, 5, padding=2)作区分,这里的64指batch_size
  47. input = torch.ones((64, 3, 32, 32))
  48. output = jzh(input)
  49. # 检验输出结果是否为torch.Size([64, 10]),因为这是一个是分类的模型,若是则证明self.linear2 = Linear(64, 10)正常执行
  50. print(output.shape)
  51. writer = SummaryWriter("./logs_seq")
  52. # jzh 是一个 PyTorch 模型,input 是输入数据,这个函数会将模型的计算图添加到 TensorBoard,以便在可视化时查看模型的结构
  53. writer.add_graph(jzh, input)
  54. 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

  1. import torch
  2. from torch import nn
  3. from torch.nn import L1Loss, MSELoss
  4. inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
  5. targets = torch.tensor([1, 2, 5], dtype=torch.float32)
  6. inputs = torch.reshape(inputs, (1, 1, 1, 3))
  7. targets = torch.reshape(targets, (1, 1, 1, 3))
  8. loss = L1Loss()
  9. # result=(0+0+2)/3
  10. result = loss(inputs, targets)
  11. loss_mse = MSELoss()
  12. result_mse = loss_mse(inputs, targets)
  13. print(result)
  14. print(result_mse)
  15. x = torch.tensor([0.1, 0.2, 0.3])
  16. y = torch.tensor([1])
  17. x = torch.reshape(x, (1, 3))
  18. loss_cross = nn.CrossEntropyLoss()
  19. result_cross = loss_cross(x, y)
  20. 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. 计算实际输出和目标之间的差距

    1. import torch
    2. import torchvision
    3. from torch import nn
    4. from torch.nn import MaxPool2d, Flatten, Linear, Conv2d, Sequential
    5. from torch.utils.data import DataLoader
    6. from torch.utils.tensorboard import SummaryWriter
    7. dataset = torchvision.datasets.CIFAR10("./data_linear", train=False, transform=torchvision.transforms.ToTensor(),
    8.                                       download=True)
    9. dataloader = DataLoader(dataset, batch_size=1)
    10. class Jzh(nn.Module):
    11.    def __init__(self):
    12.        super().__init__()
    13.        # 第二种写法
    14.        self.model1 = Sequential(
    15.            Conv2d(3, 32, 5, padding=2),
    16.            MaxPool2d(2),
    17.            Conv2d(32, 32, 5, padding=2),
    18.            MaxPool2d(2),
    19.            Conv2d(32, 64, 5, padding=2),
    20.            MaxPool2d(2),
    21.            Flatten(),
    22.            Linear(1024, 64),
    23.            Linear(64, 10)
    24.       )
    25.    def forward(self, x):
    26.        # 第二种写法
    27.        x = self.model1(x)
    28.        return x
    29. loss = nn.CrossEntropyLoss()
    30. jzh = Jzh()
    31. for data in dataloader:
    32.    imgs, targets = data
    33.    outputs = jzh(imgs)
    34.    result_loss = loss(outputs, targets)
    35.    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优化器代码:

    1. loss = nn.CrossEntropyLoss()
    2. jzh = Jzh()
    3. # lr为学习速率,一般模型训练开始时设置较大,模型训练后期设置较小
    4. optim = torch.optim.SGD(jzh.parameters(), 0.01)
    5. for epoch in range(20):
    6.    running_loss = 0.0
    7.    # 下面这个for仅代表循环一次
    8.    for data in dataloader:
    9.        imgs, targets = data
    10.        outputs = jzh(imgs)
    11.        result_loss = loss(outputs, targets)
    12.        # 清零梯度
    13.        optim.zero_grad()
    14.        # 计算每个节点参数的梯度,方便后续用优化器对其优化
    15.        result_loss.backward()
    16.        optim.step()
    17.        # 这一轮整体误差总和
    18.        running_loss = running_loss + result_loss
    19.    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

  1. import torchvision
  2. from torchvision.models import VGG16_Weights
  3. from torch import nn
  4. # train_data = torchvision.datasets.ImageNet("./data_imagenet", split='train', download=True,
  5. #                                           transform=torchvision.transforms.ToTensor())
  6. # vgg16_false = torchvision.models.vgg16(pretrained=False)
  7. # 设置成true则下载的是一个已经预训练过的模型
  8. # vgg16_true = torchvision.models.vgg16(pretrained=True)
  9. # 下列语句为新版本模型加载代码,新版本默认没有预训练,如需预训练则需加上weights=VGG16_Weights.DEFAULT
  10. vgg16_true = torchvision.models.vgg16(weights=VGG16_Weights.DEFAULT)
  11. print(vgg16_true)
  12. train_data = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(),
  13.                                       download=True)
  14. # 对模型进行修改,从1000分类修改成10分类
  15. vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
  16. 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

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

model_load

  1. import torch
  2. import torchvision
  3. from model_save import *
  4. from torch import nn
  5. # 对应方式1,加载模型
  6. model = torch.load("vgg16_method1.pth")
  7. # print(model)
  8. # 对应方式2,加载模型
  9. vgg16 = torchvision.models.vgg16(weights=None)
  10. vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
  11. # model = torch.load("vgg16_method2.pth")
  12. print(vgg16)
  13. # 方式一陷阱,自己创建的模型需要在这里定义一下,但是from model_save import *下面代码就可以不要了,
  14. # model_save文件定义过了,直接加载模型打印
  15. # class Jzh(nn.Module):
  16. # def __init__(self):
  17. # super(Jzh, self).__init__()
  18. # self.conv1 = nn.Conv2d(3, 64, kernel_size=3)
  19. #
  20. # def forward(self, x):
  21. # x = self.conv1(x)
  22. # return x
  23. model = torch.load('jzh_method1.pth')
  24. print(model)

五、完整的模型训练套路

11、模型训练

11.1train
  1. import torch
  2. import torchvision
  3. from torch.utils.data import DataLoader
  4. from torch.utils.tensorboard import SummaryWriter
  5. from model import *
  6. # 准备数据集
  7. train_data = torchvision.datasets.CIFAR10(root="./data_taolu", train=True, transform=torchvision.transforms.ToTensor(),
  8.                                          download=True)
  9. test_data = torchvision.datasets.CIFAR10(root="./data_taolu", train=False, transform=torchvision.transforms.ToTensor(),
  10.                                          download=True)
  11. train_data_size = len(train_data)
  12. test_data_size = len(test_data)
  13. print("训练数据集的长度为:{}".format(train_data_size))
  14. print("测试数据集的长度为:{}".format(test_data_size))
  15. # 利用DataLoader来加载数据集
  16. train_dataloader = DataLoader(train_data, batch_size=64)
  17. test_dataloader = DataLoader(test_data, batch_size=64)
  18. # 创建网络模型
  19. jzh = Jzh()
  20. # 创建损失函数
  21. loss_fn = nn.CrossEntropyLoss()
  22. # 优化器
  23. # learning_rate = 0.01
  24. # 1e-2=1 x (10)^(-2) = 1/100 = 0.01
  25. learning_rate = 1e-2
  26. optimizer = torch.optim.SGD(jzh.parameters(), lr=learning_rate)
  27. # 设置网络的一些参数
  28. # 记录训练次数
  29. total_train_step = 0
  30. # 记录测试次数
  31. total_test_step = 0
  32. # 记录训练的轮数
  33. epoch = 10
  34. # 添加tensorboard
  35. writer = SummaryWriter("./logs_train")
  36. for i in range(epoch):
  37.    print("------第 {} 轮训练开始------".format(i+1))
  38.    # 训练步骤开始
  39.    jzh.train()
  40.    for data in train_dataloader:
  41.        imgs, targets = data
  42.        outputs = jzh(imgs)
  43.        loss = loss_fn(outputs, targets)
  44.        # 优化器调优
  45.        optimizer.zero_grad()
  46.        loss.backward()
  47.        optimizer.step()
  48.        total_train_step = total_train_step + 1
  49.        if total_train_step % 100 == 0:
  50.            print("训练次数: {}, loss: {}".format(total_train_step, loss.item()))
  51.            writer.add_scalar("train_loss", loss.item(), total_train_step)
  52.    # 测试步骤开始
  53.    jzh.eval()
  54.    total_test_loss = 0
  55.    total_accuracy = 0
  56.    with torch.no_grad():
  57.        for data in test_dataloader:
  58.            imgs, targets = data
  59.            outputs = jzh(imgs)
  60.            loss = loss_fn(outputs, targets)
  61.            total_test_loss = total_test_loss + loss.item()
  62.            accuracy = (outputs.argmax(1) == targets).sum()
  63.            total_accuracy = total_accuracy + accuracy
  64.    print("整体测试集上的loss: {}".format(total_test_loss))
  65.    print("整体测试集上的正确率: {}".format(total_accuracy/test_data_size))
  66.    writer.add_scalar("test_loss", total_test_loss, total_test_step)
  67.    writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step)
  68.    total_test_step = total_test_step + 1
  69.    # 保存模型
  70.    torch.save(jzh, "jzh_{}.pth".format(i))
  71.    print("模型已保存")
  72. writer.close()
11.2model
  1. import torch
  2. from torch import nn
  3. # 搭建神经网络
  4. class Jzh(nn.Module):
  5.    def __init__(self):
  6.        super(Jzh, self).__init__()
  7.        self.model = nn.Sequential(
  8.            nn.Conv2d(3, 32, 5, 1,  padding=2),
  9.            nn.MaxPool2d(2),
  10.            nn.Conv2d(32, 32, 5, 1, padding=2),
  11.            nn.MaxPool2d(2),
  12.            nn.Conv2d(32, 64, 5, 1, padding=2),
  13.            nn.MaxPool2d(2),
  14.            nn.Flatten(),
  15.            nn.Linear(1024, 64),
  16.            nn.Linear(64, 10)
  17.       )
  18.    def forward(self, x):
  19.        x = self.model(x)
  20.        return x
  21. if __name__ == '__main__':
  22.    jzh = Jzh()
  23.    input = torch.ones((64, 3, 32, 32))
  24.    output = jzh(input)
  25.    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
  1. import torch
  2. import torchvision
  3. from PIL import Image
  4. from torch import nn
  5. image_path = "./imgs/airplan.png"
  6. image = Image.open(image_path)
  7. # 保留颜色通道,加上这一步后可以适应png、jpg各种格式图片
  8. # image = image.convert('RGB')
  9. print(image)
  10. transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
  11.                                            torchvision.transforms.ToTensor()])
  12. image = transform(image)
  13. print(image.shape)
  14. class Jzh(nn.Module):
  15.    def __init__(self):
  16.        super(Jzh, self).__init__()
  17.        self.model = nn.Sequential(
  18.            nn.Conv2d(3, 32, 5, 1,  padding=2),
  19.            nn.MaxPool2d(2),
  20.            nn.Conv2d(32, 32, 5, 1, padding=2),
  21.            nn.MaxPool2d(2),
  22.            nn.Conv2d(32, 64, 5, 1, padding=2),
  23.            nn.MaxPool2d(2),
  24.            nn.Flatten(),
  25.            nn.Linear(1024, 64),
  26.            nn.Linear(64, 10)
  27.       )
  28.    def forward(self, x):
  29.        x = self.model(x)
  30.        return x
  31. model = torch.load("./jzh_9.pth")
  32. # 若使用GPU训练的模型,一定要使用下述语句将其映射到CPU上
  33. # model = torch.load("jzh_gpu.pth" ,map_location=torch.device('cpu'))
  34. print(model)
  35. # 数据要和模型一致
  36. image = torch.reshape(image, (1, 3, 32, 32))
  37. model.eval()
  38. with torch.no_grad():
  39.    output = model(image)
  40. 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##

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/816680
推荐阅读
相关标签
  

闽ICP备14008679号