基于pytorch的深度学习使用

发布于:2022-12-20 ⋅ 阅读:(609) ⋅ 点赞:(0)

引言

  我们将通过pytorch提供的模型学习深度学习,在此之前,我们需要配置pytorch环境以及jupyter环境(没有搭建的可以看我上一篇文章),我也是初学者,谨以此记录一下学习的笔记,通过接下来的学习,我可能会不断的补充笔记。

一、 基础知识

1. 神经网络的基本骨架----nn.Module的使用

1)pytorch中模型的介绍

  首先,我们要先进入到pytorch的官网,再点击导航栏中Docs下的PyTorch,如下图所示:
  然后,在右侧的导航栏中,选中我们要用的nn(Neural network)框架,如下图所示:
在这里插入图片描述

  • containers          骨架
  • Convolution Layers      卷积层
  • Pooling layers         池化层
  • Padding layers        填充层
  • Non-linear Activations(weighted sum, nonlinearity)  非线性激活
  • Non-linear Activations(other)  非线性激活
  • Normalization Layers      正则化层

  我们先进入到containers这里面,会看到有对其里面内容的基本介绍:
在这里插入图片描述
  我们在选中其中的第一个属性Module,进入到里面,我们可以看到这个的代码与介绍:
在这里插入图片描述
  这里看代码就需要对继承等编程知识有一定的基础(在此,继承我就不多讲了,默认有一定的编程能力)。在新的类中,我们重写了一些方法,这里是重写了初始化与前向传播的方法。通过前向传播方法,我们可以看到模型的运行流程是输入->卷积(conv1)->非线性(relu)->卷积(conv2)->非线性(relu)->输出。
  前向传播:就是给模型一个输入,通过一个关系表达式或函数表达式的运算,最终得到一个输出。

2)代码实现

  打开pycharm,创建一个工程,创建一个python文件,输入以下代码:

import torch
from torch import nn

class DeepMd(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self,input):
        output = input + 1
        return output
# 创建一个对象
deepMd = DeepMd()
x = torch.tensor(1.0)
output = deepMd(x)
print(output)

  我们可以得到结果tensor(2.),如下图所示:
在这里插入图片描述
  到此,我们框架就有了大概的了解。

2. 卷积操作

1)pytorch中的卷积

  我们还是在nn(Neural network)框架中点击 Convolution Layers跳转到卷积层中的一些信息。
在这里插入图片描述
  我们主要是用的是nn.Conv2d这个卷积。因此,我们需要借用torch.nn.functional这里面的nn.Conv2d。其实你不必太过于纠结他们两个之间的关系,你只要明白我们是通过 torch.nn.functional来理解torch.nn。
  在调用torch.nn.functional.conv2d()这个方法的时候,我们需要输入一些参数:
在这里插入图片描述

2)卷积的理解

  在卷积的时候,我们需要一个输入,一个卷积核,才能得到我们的卷积结果,如下图所示:
在这里插入图片描述
  当我们进行卷积的时候,我们从输入图像的最左上方开始框出一个与卷积核大小相等的区域,将其与卷积核的对应位置相乘,再相加,我们会得到结果的的一个输出。如下图所示,简单的理解就是一个(5X5)的输入,卷积核是(3X3),那么我们的输出就是(3X3)的结果。(这里我们默认卷积步是1,此时你不需要懂结果为什么是(3X3)。你要理解的是每卷积一次,输出结果对应在(3X3)的位置上)。那么我将从输入图像的最左上方开始框出一个(3X3)的区域与卷积核进行对应位置相乘,再相加,最后的计算结果会放在输出(3X3)结果的“a11”位置。
在这里插入图片描述
在这里插入图片描述
  接下来,我们理解一下stride这个参数。当我们设置stride为一个整数时(向上面那样),那么我们就是理解成每卷积完一次后,首先要向有走整数个单位后,进行卷积。当我们移动到右边的边缘时(也就是卷积核的右侧与输入图像的右侧重合),我们需要回到这一行的起始位置,另其向下移动整数个单位进行卷积。依次重复,直到卷积核移动到输入图像的右下角,卷积结束。
  上面是最简单的理解,我们也可以参考pytorch官网的参数理解,可以自由设置左移的距离和下移的距离,即(sH,sW)。不管你设置的stride是多少,都要遵循先左移,再下移,以此循环往复。
  再接下来,我们看一下padding的理解,padding的是指可以理解为在我们的输入图像的外面添加多宽的边距,通过下面这张图可以更好地理解padding。
在这里插入图片描述

3)代码实现

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]])
print(input.shape)
print(kernel.shape)
# 结果为:
# torch.Size([5, 5])
# torch.Size([3, 3])
# 根据pytorch的文档,这并不符合conv2d的卷积输入的格式,我们需要进行修改
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)

  通过运行,我们能得到最终的卷积结果,与我们上面理解里面的运算结果是一致的。
在这里插入图片描述
  你可以修改stride的值,看看与你自己的计算结果是否一致。

下面代码是padding = 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]])
print(input.shape)
print(kernel.shape)
# 结果为:
# torch.Size([5, 5])
# torch.Size([3, 3])
# 根据pytorch的文档,这并不符合输入的格式,我们需要进行修改
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)

output1 = F.conv2d(input, kernel, stride=2)
print(output1)

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

4)利用数据集进行卷积

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("../data", tran=False, transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset, batch_size=64)

# 搭建简单神经网络
class DeepMd(nn.Module):
    def __init__(self):
        super(DeepMd, self).__init__()
        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
deepMd = DeepMd()
writer = SummaryWriter("../logs")

step = 0
for data in dataloader:
    imgs, targets = data
    output = deepMd(imgs)
    # print(output.shape)
    # 输入的大小
    print(imgs.shape)
    print(output.shape)
    # torch.Size([64, 3, 32, 32])
    writer.add_images("input", imgs, step)
    # torch.Size([64, 6, 30, 30])

    output = torch.reshape(output, (-1, 3, 30, 30))
    writer.add_images("output", output, step)

    step = step + 1

  运行完此程序后,我们可以通过pycharm打开我们的终端,进入到我们所在的pytorch环境下,输入如下命令,则会给我们一个地址,我们进入到此地址会看到我们的图片。

tensorboard --logsdir=logs

在这里插入图片描述

3. 池化

1)pytorch中的池化

  我们还是在nn(Neural network)框架中点击 Padding Layers跳转到池化层中的一些信息。
在这里插入图片描述
  我们主要是用的是nn.MaxPool2d这个池化。在调用torch.nn.MaxPool2d()这个方法的时候,我们需要输入一些参数:
在这里插入图片描述

2)代码实现

普通实现:

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)
input = torch.reshape(input, (-1, 1, 5, 5))
print(input)

class DeepMd(nn.Module):
    def __init__(self):
        super(DeepMd, self).__init__()
        # ceil_mode = True表示边缘取最大值
        # ceil_mode = False表示边缘取值使舍去,不取
        # self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=False)

    def forward(self, input):
        output = self.maxpool1(input)
        return output

deepMd = DeepMd()
output = deepMd(input)
print(output)

利用数据集实现:

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", train=False, download=True, transform=torchvision.transforms.ToTensor())

dataloader = DataLoader(dataset, batch_size=64)


class DeepMd(nn.Module):
    def __init__(self):
        super(DeepMd, self).__init__()
        # ceil_mode = True表示边缘取最大值
        # ceil_mode = False表示边缘取值使舍去,不取
        # self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)
        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=False)

    def forward(self, input):
        output = self.maxpool1(input)
        return output


deepMd = DeepMd()

writer = SummaryWriter("../logs_maxpool")
step = 0

for data in dataloader:
    imgs, targets = data
    print(imgs.shape)
    writer.add_images("input", imgs, step)
    output = deepMd(imgs)
    writer.add_images("output", output, step)
    step = step + 1

writer.close()

  运行完此程序后,我们还是通过pycharm打开我们的终端,进入到我们所在的pytorch环境下,输入如下命令,则会给我们一个地址,我们进入到此地址会看到我们的图片。

tensorboard --logsdir=logs_maxpool

4.非线性激活

1)pytorch中的非线性激活

  我们还是在nn(Neural network)框架中点击Non-linear Activations (weighted sum, nonlinearity)跳转到非线性激活中的一些信息。
在这里插入图片描述

2)代码实现

普通实现:
  我们选用ReLU进行代码实验。

import torch
from torch import nn
from torch.nn import ReLU

input = torch.tensor([[1, -0.5],
                      [-1, 3]])
input = torch.reshape(input, (-1, 1, 2, 2))
print(input.shape)


class DeepMd(nn.Module):
    def __init__(self):
        super().__init__()
        # inplace=False表示对原来的位置不改变
        self.relu1 = ReLU()

    def forward(self, input):
        output = self.relu1(input)
        return output


deepMd = DeepMd()
output = deepMd(input)
print(output)

利用数据集实现:
  由于ReLU对图像的处理不是很明显,因此我们选用Sigmoid激活函数。

import torch
import torchvision.datasets
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("../data", train=False, download=True, transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=64)


class DeepMd(nn.Module):
    def __init__(self):
        super().__init__()
        # inplace=False表示对原来的位置不改变
        self.relu1 = ReLU()
        self.sigmoid1 = Sigmoid()

    def forward(self, input):
        output = self.sigmoid1(input)
        return output


deepMd = DeepMd()
writer = SummaryWriter("../logs_relu")
step = 0
for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, global_step=step)
    output = deepMd(imgs)
    writer.add_images("output", output, global_step=step)
    step = step + 1

writer.close()

  运行完此程序后,我们还是通过pycharm打开我们的终端,进入到我们所在的pytorch环境下,输入如下命令,则会给我们一个地址,我们进入到此地址会看到我们的图片。

tensorboard --logsdir=logs_relu

5.线性层

1)pytorch中的线性层

  我们还是在nn(Neural network)框架中点击Linear Layers跳转到线性层中的一些信息。
在这里插入图片描述
  线性层要做的事情其实如下图所示,将一个(5X5)的输入变成一个(1X25),再变成(1X3)。
在这里插入图片描述

2)代码实现

利用数据集实现:

import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("../data", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64)


class DeepMd(nn.Module):

    def __init__(self):
        super().__init__()
        self.linear1 = Linear(196608, 10)

    def forward(self, input):
        output = self.linear1(input)
        return output


deepMd = DeepMd()

for data in dataloader:
    imgs, targets = data
    print(imgs.shape)  # torch.Size([64, 3, 32, 32])
    # 这一行output = torch.reshape(imgs, (1, 1, 1, -1)) 生成torch.Size([1, 1, 1, 196608])的结果,
    # 其作用个下面这一行的作用是一样的
    output = torch.flatten(imgs)  # torch.Size([196608])
    print(output.shape)
    output = deepMd(output)
    print(output.shape)  # torch.Size([1, 1, 1, 10])

5. CIFAR10的网络结构

  这是一个简单的CIFAR10的网络结构,我们需要通过代码搭建这么一个结构。
在这里插入图片描述
  其中,我们需要通过pytorch官网中卷积所提供的的公式,计算出stride和padding的数目。其中,dilation默认为1。
在这里插入图片描述
这是我们按照前面所说的方式一步步写的模型,并没有用到Sequential。

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential


class DeepMd(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = Conv2d(3, 32, 5, stride=1, padding=2)
        self.maxPool1 = MaxPool2d(2)
        self.conv2 = Conv2d(32, 32, 5, stride=1, padding=2)
        self.maxPool2 = MaxPool2d(2)
        self.conv3 = Conv2d(32, 64, 5, stride=1, padding=2)
        self.maxPool3 = MaxPool2d(2)
        self.flatten = Flatten()
        self.linear1 = Linear(1024, 64)
        self.linear2 = 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)
        return x


deepMd = DeepMd()
print(deepMd)

# 检查网络正确性
input = torch.ones(64, 3, 32, 32)
output = deepMd(input)
print(output.shape)

当我们使用Sequential的时候,会发现简化了很多。

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential

class DeepMd(nn.Module):
    def __init__(self):
        super().__init__()
        self.module1 = Sequential(
            Conv2d(3, 32, 5, stride=1, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, stride=1, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, stride=1, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.module1(x)
        return x


deepMd = DeepMd()
print(deepMd)

# 检查网络正确性
input = torch.ones((64, 3, 32, 32))
output = deepMd(input)
print(output.shape)

6. 损失函数和反向传播

1)pytorch中的损失函数

  我们还是在nn(Neural network)框架中点击Loss Functions跳转到损失函数中的一些信息。
在这里插入图片描述
  损失函数的作用有两个,一个就是计算实际输出和目标之间的差距,另一个是为我们更新输出提供一定的依据(反向传播),这个依据就是梯度

2)代码实现

这是几个不同的损失函数计算结果的代码。

import torch
from torch import nn
from torch.nn import L1Loss, MSELoss

# dtype=torch.float32表示浮点数
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(reduction='sum')
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)

这里我们通过搭建损失函数网络。

import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("../data", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=1)


class DeepMd(nn.Module):
    def __init__(self):
        super().__init__()
        self.module1 = Sequential(
            Conv2d(3, 32, 5, stride=1, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, stride=1, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, stride=1, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.module1(x)
        return x


loss = nn.CrossEntropyLoss()
deepMd = DeepMd()
for data in dataloader:
    imgs, targets = data
    outputs = deepMd(imgs)
    result_loss = loss(outputs, targets)
    # result_loss.backward()

  其实,上面代码就是一个有自己搭建的网络以及自己设定的损失函数,最后一行代码是经过损失函数后要再进行反向传播,我们可以通过断点调试的方法,可以看到当我们没有反向传播的时候,梯度grad是没有的,一旦我们经过反向传播,我们会获得梯度的一些值。如下图所示:
查找路径是自己的网络deepMd–>module1–>Protected Attributes–>_modules–>‘0’–>weight–>grad

没有经过反向传播的grad
没有经过反向传播

经过反向传播后的grad
在这里插入图片描述

7.优化器

1)pytorch中的优化器

  我们的优化器是在torch.optim当中。
在这里插入图片描述

2)代码实现

import torch.optim
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("../data", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)
dataloader = DataLoader(dataset, batch_size=1)


class DeepMd(nn.Module):
    def __init__(self):
        super().__init__()
        self.module1 = Sequential(
            Conv2d(3, 32, 5, stride=1, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, stride=1, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, stride=1, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.module1(x)
        return x


# 损失函数
loss = nn.CrossEntropyLoss()
deepMd = DeepMd()
# 优化器
optim = torch.optim.SGD(deepMd.parameters(), lr=0.01)
for epoc in range(20):
    running_loss = 0.0
    # 这是对这个网络进行一层的学习
    for data in dataloader:
        imgs, targets = data
        outputs = deepMd(imgs)
        result_loss = loss(outputs, targets)
        # 将梯度清零
        optim.zero_grad()
        # 反向传播
        result_loss.backward()
        # 对参数调优
        optim.step()
        running_loss = running_loss + result_loss
    print(running_loss)

这段代码其实就算一个相对完整的代码,有前向传播,后向传播,参数优化等。

二、 网络模型

1. 现有网络的修改及使用

1)现有模型

  我们通过模型vgg16进行修改。

import torchvision.datasets

vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)

  我们可以看到输出:
在这里插入图片描述在这里插入图片描述

2)在现有网络上添加

  最后,经过线性层的输出是1000个分类,如果我们想要对其进行修改,让其最后的输出是10。(我们此处是对vgg_true进行的修改。)

from torch import nn
import torchvision.datasets

vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)

# 因为vgg16的输出是1000,我们需要添加一个线性层使其输出为10
# add_linear是我们添加的名字,nn.Linear是我们添加的线性层
vgg16_true.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)

在这里插入图片描述在这里插入图片描述

  这里可以看出,我们添加的线性层是在整个vgg16里面添加的,如果我们想要添加到Sequential里面的话,需要进行如下操作:

from torch import nn
import torchvision.datasets

vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)

# 因为vgg16的输出是1000,我们需要添加一个线性层使其输出为10
# add_linear是我们添加的名字,nn.Linear是我们添加的线性层
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)

在这里插入图片描述在这里插入图片描述

3)在现有网络上修改

import torchvision.datasets

vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_false)

在这里插入图片描述在这里插入图片描述
  我们想要将其classifier中的第六层的out_features修改成10,则代码如下:

from torch import nn
import torchvision.datasets

vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_false)

# 要注意这个classifier[?]里面的数要与模型中的数对应
vgg16_false.classifier[6] = nn.Linear(4096, 10)
print(vgg16_false)

在这里插入图片描述在这里插入图片描述

2. 模型的保存和读取

1)方法一

保存

  这种方式不仅将模型保存下来,还将模型参数进行了保存。

import torch
import torchvision

# pretrained=False表示我们没有对模型进行训练,而是使用了他原来训练的一些参数
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1
# 这种方式不仅将模型保存下来,还将模型参数进行了保存
torch.save(vgg16, "vgg16_method1.pth")

在这里插入图片描述
加载

import torch
import torchvision.models

# 用保存的方式一的形式加载模型
model = torch.load("vgg16_method1.pth")
print(model)

2)方法二(推荐)

保存

  这种方式保存的不再是模型的结构,而是模型的参数。

import torch
import torchvision

# pretrained=False表示我们没有对模型进行训练,而是使用了他原来训练的一些参数
vgg16 = torchvision.models.vgg16(pretrained=False)

# 保存方式2(官方推荐)
# 这种方式保存的不再是模型的结构,而是模型的参数
# state_dict()是将其保存成一个字典的python形式
torch.save(vgg16.state_dict(), "vgg16_method2.pth")

在这里插入图片描述
加载

import torch
import torchvision.models

model = torch.load("vgg16_method2.pth")
print(model)

  你会发现打印出的是一个字典形式。

恢复
  当我们在这种情况下存储的是字典的形式,我们要恢复成网络模型,需要以下操作:

import torch
import torchvision.models

# model存储的是字典形式,需要load_state_dict加载字典形式的数据
model = torch.load("vgg16_method2.pth")
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(model)

print(vgg16)

3)文件大小

  我们打开该目录下的终端命令窗口,输入ls或者dir都可以(如果输入后不成功,多敲几下回车,再输入命令试试,我也不知道什么原因),查看所有问价的大小:

ls

在这里插入图片描述
  我们可以看到第二种方法的存储方式比第一种的小,此处表现得不是很明显,当我们模型特别的大时,这能帮助我们节省存储空间。

三、完整的训练模型套路

1. 分类的基础知识

  在讲完整的训练模型之前,我们先要了解一下针对分类的最后处理。
在这里插入图片描述
  我们以上面这个图片为基础,我们在每次测试的时候,经过训练后的模型得到的outputs输出都要取得分类的结果,将其与我们的目标结果进行比较,得到正确的个数。例如:上图有两个图片,经过训练模型后,我们达到输出的概率。第一张图片的结果是[0.1, 0.2],即这个模型认为他是第一类的概率是0.1,第二类的概率是0.2,所以第二类的概率相对而言比较大,因此,我们就可以认为这张图片是第二类,那么我们怎么得到他是第二类呢?,这时候我们就需要经过argmax()函数后会得到最大概率的下标,也就是得到结果1。同理,第二张图片的结果也是1。
  接下来,我们需要将这个预测的结果与实际的目标结果进行比较,是否正确。例如图中,我们目标结果是第一张图片分类是0,第二张图片分类是1,也就是[0, 1]。但是预测的是[1, 1],所以经过preds == inputs target 这个判断后,会返回[False, True]。在经过sum()函数后,会统计正确的个数。

import torch
outputs = torch.tensor([[0.1, 0.2],
                       [0.3, 0.4]])
print(outputs.argmax(1))
preds = outputs.argmax(1)
targets = torch.tensor([0, 1])
print((preds == targets).sum())

2. 完成的训练步骤

  我们完整的训练模型时,需要有非常好的规范,因此,我们需要建立两个文件,一个是 model.py 文件用来存储网络模型,另一个是 train.py 文件用来做一些其他的设置。我们要保证将这两个python文件放在同一个根目录下。
model.py

import torch
from torch import nn


# 搭建神经网络
class DeepMd(nn.Module):
    def __init__(self):
        super().__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(1024, 64),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = self.model(x)
        return x


# 测试我们的网络模型
if __name__ == '__main__':
    deepMd = DeepMd()
    input = torch.ones((64, 3, 32, 32))
    output = deepMd(input)
    print(output.shape)

train.py

import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from model import *


# 准备数据集
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)
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)

# 创建网络模型
deepMd = DeepMd()

# 损失函数
loss_fn = nn.CrossEntropyLoss()

# 优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(deepMd.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))

    # 训练步骤开始
    deepMd.train()  # 可有可没有
    for data in train_dataloader:
        imgs, targets = data
        outputs = deepMd(imgs)
        loss = loss_fn(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("训练次数:{},loss:{}".format(total_train_step, loss.item()))
            writer.add_scalar("train_loss", loss.item(), total_train_step)

    # 测试步骤开始
    deepMd.eval()  # 可有可没有
    total_test_loss = 0
    total_accuracy = 0
    # 让模型的梯度消除
    with torch.no_grad():
        for data in test_dataloader:
            imgs, targets = data
            outputs = deepMd(imgs)
            loss = loss_fn(outputs, targets)
            total_test_loss = total_test_loss + loss.item()
            # 当我们进行分类问题的时候,我们就需要下面这一行代码,如果是目标检测或者自然语言处理的时候,就暂时不需要。
            # argmax(1)指的是横向对比大小,返回最大值的下标
            # argmax(0)指的是纵向对比大小,返回最大值的下标
            # == 值的是输出概率结果与目标的结果是否一致,是返回True,否则返回False
            # sum()是用来计算统计True的个数
            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(deepMd, "deepMd_{}.pth".format(i + 1))
    # torch.save(deepMd.state_dict(), "deepMd_{}.pth".format(i+1))
    print("模型已保存")

writer.close()

  这段代码可能会运行出错,查看一下是不是存储模型的时候报的错,如果是,那么就换一种存储方式。(我第一遍运行的时候没有问题,但是第三四次运行时就报“attribute lookup DeepMd on _main _failed”错误)

四、利用GPU训练

1. pytorch方法一

  当我们使用GPU进行训练和测试的时候,我们需要在原来的代码上对网络模型、数据(输入、标注)以及损失函数进行修改,判断他们是否有GPU。

import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time
# from model import *


# 准备数据集
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)
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 DeepMd(nn.Module):
    def __init__(self):
        super(DeepMd, 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(1024, 64),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = self.model(x)
        return x
deepMd = DeepMd()
if torch.cuda.is_available():
    deepMd = deepMd.cuda()

# 损失函数
loss_fn = nn.CrossEntropyLoss()
if torch.cuda.is_available():
    loss_fn = loss_fn.cuda()

# 优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(deepMd.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))

    # 训练步骤开始
    deepMd.train()  # 可有可没有
    for data in train_dataloader:
        imgs, targets = data
        if torch.cuda.is_available():
            imgs = imgs.cuda()
            targets = targets.cuda()
        outputs = deepMd(imgs)
        loss = loss_fn(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("训练次数:{},loss:{}".format(total_train_step, loss.item()))
            writer.add_scalar("train_loss", loss.item(), total_train_step)

    # 测试步骤开始
    deepMd.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 = deepMd(imgs)
            loss = loss_fn(outputs, targets)
            total_test_loss = total_test_loss + loss.item()
            # 当我们进行分类问题的时候,我们就需要下面这一行代码,如果是目标检测或者自然语言处理的时候,就暂时不需要。
            # argmax(1)指的是横向对比大小,返回最大值的下标
            # argmax(0)指的是纵向对比大小,返回最大值的下标
            # == 值的是输出概率结果与目标的结果是否一致,是返回True,否则返回False
            # sum()是用来计算统计True的个数
            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(DeepMd(), "deepMd_{}.pth".format(i+1))
    torch.save(deepMd.state_dict(), "deepMd_{}.pth".format(i+1))
    print("模型已保存")

writer.close()

  如果你电脑上没有GPU,你可以访问谷歌的Google Colab,它给你提供的GPU进行试验,一周大概可以免费试用30个小时。

2. pytorch方法二

  pytorch还给我们提供了第二种方法来利用GPU训练,主要就是通过“.to(device)”这个函数对网络模型、数据(输入,标注)以及损失函数进行修改。

import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time
# from model import *

# 定义一个设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 准备数据集
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)
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 DeepMd(nn.Module):
    def __init__(self):
        super(DeepMd, 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(1024, 64),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = self.model(x)
        return x
deepMd = DeepMd()
deepMd = deepMd.to(device)

# 损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device)

# 优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(deepMd.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))

    # 训练步骤开始
    deepMd.train()  # 可有可没有
    for data in train_dataloader:
        imgs, targets = data
        imgs = imgs.to(device)
        targets = targets.to(device)
        outputs = deepMd(imgs)
        loss = loss_fn(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("训练次数:{},loss:{}".format(total_train_step, loss.item()))
            writer.add_scalar("train_loss", loss.item(), total_train_step)

    # 测试步骤开始
    deepMd.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 = deepMd(imgs)
            loss = loss_fn(outputs, targets)
            total_test_loss = total_test_loss + loss.item()
            # 当我们进行分类问题的时候,我们就需要下面这一行代码,如果是目标检测或者自然语言处理的时候,就暂时不需要。
            # argmax(1)指的是横向对比大小,返回最大值的下标
            # argmax(0)指的是纵向对比大小,返回最大值的下标
            # == 值的是输出概率结果与目标的结果是否一致,是返回True,否则返回False
            # sum()是用来计算统计True的个数
            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(DeepMd(), "deepMd_{}.pth".format(i+1))
    torch.save(deepMd.state_dict(), "deepMd_{}.pth".format(i+1))
    print("模型已保存")

writer.close()

五、完整的模型验证套路

  前面我们讲个两种存储模型的方式,那么我们就会有两种加载的方式。我们采用一狗的照片进行测试。

1. 方式一存储,方式一加载

  不知道什么原因,我在计算机中运行存储方式一的代码总是报错(前面成功过一两次),因此我在Google Colab中运行。

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


image_path = "../imgs/dog.png"
image = Image.open(image_path)
print(image)
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)), torchvision.transforms.ToTensor()])
image = transform(image)
print(image.shape)


# 搭建神经网络
class DeepMd(nn.Module):
    def __init__(self):
        super(DeepMd, 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(1024, 64),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = self.model(x)
        return x


# 第一种加载方式
deepMd = DeepMd()
model = torch.load("deepMd_9.pth")
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))

  原来的分类:
在这里插入图片描述
  运行的最终结果可以得到:
在这里插入图片描述
  运行的结果是第5类,所以对应的分类是dog,这个结果是正确的。

2.方式二存储,方式二加载

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

image_path = "../imgs/dog.png"
image = Image.open(image_path)
print(image)
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)), torchvision.transforms.ToTensor()])
image = transform(image)
print(image.shape)

# 搭建神经网络
class DeepMd(nn.Module):
    def __init__(self):
        super().__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(1024, 64),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = self.model(x)
        return x

# 如果在GPU训练的模型,想在CPU上测试,那么需要加入下面一行代码
# model = torch.load("deepMd_10.pth", map_location=torch.device('cpu'))
model = torch.load("deepMd_10.pth")
deepMd = DeepMd()
deepMd.load_state_dict(model)
print(model)
image = torch.reshape(image, (1, 3, 32, 32))
print(image.shape)
deepMd.eval()
with torch.no_grad():
    output = deepMd(image)
print(output)

print(output.argmax(1))

  原来的分类:
在这里插入图片描述
  运行的最终结果可以得到:
在这里插入图片描述
  运行的结果是第5类,所以对应的分类是dog,这个结果是正确的,与第一种方式对应了。

  如果你的结果验证的不对,可能与你训练的次数和学习速率有关,你可以更改这两个参数从新训练,再用训练的模型验证你的结果是否正确。

结束语

  以上我是结合着视频学习的,如果你有时间,你可以结合PyTorch深度学习的快速入门视频一起学习。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到