【深度学习-图像处理】使用pytorch搭建LeNet图像分类网络

1、Pytorch 安装

Pytorch 安装
进入官网:: Pytorch.官网链接.

选择系统的版本、按装的方式,生成相应的安装代码命令。

示例:pip 安装

在这里插入图片描述

生成的安装命令:

pip3 install torch==1.8.1+cu102 torchvision==0.9.1+cu102 torchaudio===0.8.1 -f https://download.pytorch.org/whl/torch_stable.html

在电脑端打开终端:(cmd打开窗口)

输入复制生成的指令:

在这里插入图片描述

conda 方式安装

在这里插入图片描述

生成的安装命令:

conda install pytorch torchvision torchaudio cudatoolkit=10.2 -c pytorch

在电脑端打开终端:(cmd打开窗口)

输入复制生成的指令:
在这里插入图片描述

pytorch 官方网址的分类器demo

在官网上可找到:训练一个分类器

: 官方网页链接.

在这里插入图片描述

2、使用LeNet网络进习搭建分类器网络

LeNet 的网络架构

LeNet 的网络架构:

在这里插入图片描述
注意通道 的参数顺序:

在这里插入图片描述

需要的代码:

包含三个.py文件

在这里插入图片描述

① model.py文件


# model.py
import torch.nn as nn
import torch.nn.functional as F


class LeNet(nn.Module):                # 定义一个类,继承自nn.Module
    def __init__(self):                # 定义从初始化函数
        super(LeNet, self).__init__()  # super()继承父类的构造函数,就是调用基类的构造函数
        self.conv1 = nn.Conv2d(3, 16, 5)  # 参数依次为:通道数,卷积核个数,卷积核的尺寸
        self.pool1 = nn.MaxPool2d(2, 2)  # 第一下采样层采用的最大特征采样
        self.conv2 = nn.Conv2d(16, 32, 5)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))    # input(3, 32, 32) output(16, 28, 28)   # 16就是卷积核的个数,28 按照公式 (32-5+2*0)/ 1 + 1 = 28  
        x = self.pool1(x)            # output(16, 14, 14)
        x = F.relu(self.conv2(x))    # output(32, 10, 10)
        x = self.pool2(x)            # output(32, 5, 5)
        x = x.view(-1, 32*5*5)       # output(32*5*5)
        x = F.relu(self.fc1(x))      # output(120)
        x = F.relu(self.fc2(x))      # output(84)
        x = self.fc3(x)              # output(10)
        return x

# # 添加调试代码  可以查看卷积过程的变量数据变化
# import torch
# input1 = torch.rand([32, 3, 32, 32])
# model = LeNet()
# print(model)
# output = model(input1)


② train.py 文件


# train.py 
import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms

# 如果想看加载数据集图片效果,需要导入的库
import numpy as np
import matplotlib.pyplot as plt


def main():
    transform = transforms.Compose(
        [transforms.ToTensor(),    # 数据张量化
            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])  # 数据标准化

    #     # 50000张训练图片
    #     # 第一次使用时要将download设置为True才会自动去下载数据集
    train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
                                                download=False, transform=transform)
    train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,           # 分批次进行 训练: 每一次随机拿出36张图片进行训练
                                                shuffle=True, num_workers=0)        # shuffle 是否将数据集打乱   num_workers  理解为载入数据的线程数,Linux下可以为非零参数,Windows下为0

    #     # 10000张验证图片
    #     # 第一次使用时要将download设置为True才会自动去下载数据集
    val_set = torchvision.datasets.CIFAR10(root='./data', train=False,     
                                            download=False, transform=transform)                 # 在导入图片的时候进行了处理
    val_loader = torch.utils.data.DataLoader(val_set, batch_size=10000,
                                                shuffle=False, num_workers=0)
    val_data_iter = iter(val_loader) # 设置迭代器
    val_image, val_label = val_data_iter.next()
        
    classes = ('plane', 'car', 'bird', 'cat',
            'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

    # # 利用官方的代码加载查看数据集

    # # functions to show an image


    # def imshow(img):
    #     img = img / 2 + 0.5     # unnormalize 反标准化
    #     npimg = img.numpy()
    #     plt.imshow(np.transpose(npimg, (1, 2, 0)))
    #     plt.show()

    # # print labels
    # print(' '.join('%5s' % classes[val_label[j]] for j in range(4)))
    # # show images
    # imshow(torchvision.utils.make_grid(val_image))



    net = LeNet()   # 实例化网络
    loss_function = nn.CrossEntropyLoss()     # 定义损失函数,其中已经包含了 logSoftmax 和 NLLLose 函数

    optimizer = optim.Adam(net.parameters(), lr=0.001)     # 传入训练的参数  , 学习率

    # 训练过程
    for epoch in range(5):  # loop over the dataset multiple times  将训练集迭代5次

        running_loss = 0.0  # 累加训练过程中的损失
        for step, data in enumerate(train_loader, start=0):  # 循环遍历训练集样本
            # get the inputs; data is a list of [inputs, labels]
            inputs, labels = data

            # zero the parameter gradients
            optimizer.zero_grad()  # 清除历史梯度,如果不清除掉历史梯度,就会对计算的历史梯度进行累加(通过这个特性你可以变相的实现一个很大batch数值的训练)
            # forward + backward + optimize
            outputs = net(inputs)   # 图片输入网络,得到输出
            loss = loss_function(outputs, labels) # 通过损失函数计算损失 outputs 对应网络预测的值, labels 对应输入图片的真实标签
            loss.backward()  # 将得到的损失进行反向传播
            optimizer.step()  # 通过优化器函数进行参数的更新

            # print statistics
            running_loss += loss.item()  # 累加损失变量
            if step % 500 == 499:    # print every 500 mini-batches
                with torch.no_grad():                                # with是一个上下文管理器,相对重要 /作用:在接下来的计算中,不会去计算每个节点的误差损失梯度    节省算力/内存资源
                    outputs = net(val_image)  # [batch, 10]          # 正向传播
                    predict_y = torch.max(outputs, dim=1)[1]         # 网络预测最可能是哪个类别  ---> 找到对应的下标索引
                    accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)   # 预测的标签类别与真实标签类别进行比较  相同的返回1/true,不同的返回0/false
                                                                                                # 且求和得到本次预测过程中,准确预测的样本比例 ,最终结果形式是  张量

                    print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %
                            (epoch + 1, step + 1, running_loss / 500, accuracy))   # 打印出 迭代次数,某次迭代具体的哪一步,每500步的平均训练误差,测试样本的准确率
                    running_loss = 0.0   # 将训练误差归零,进行下500步的迭代过程

    print('Finished Training')

#   保存模型
save_path = './Lenet.pth'
torch.save(net.state_dict(), save_path)


if __name__ == '__main__':
    main()


③ predict.py 文件

预测代码: predict.py 文件


# predict.py 

import torch
import torchvision.transforms as transforms
from PIL import Image

from model import LeNet


# def main():
transform = transforms.Compose(
    [transforms.Resize((32, 32)),     # 测试图片转换为 32 * 32 大小
     transforms.ToTensor(),             # 张量化处理 
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])   # 标准化处理

classes = ('plane', 'car', 'bird', 'cat',
            'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

net = LeNet()
net.load_state_dict(torch.load('Lenet.pth'))

im = Image.open('text.jpg')    
im = transform(im)  # 预处理之后的图片通道[C, H, W]        # pytorch tensor 的通道排序是[batch, channel, height, width]
im = torch.unsqueeze(im, dim=0)  # [N, C, H, W]    # 使用函数,增加一个维度batch

with torch.no_grad():
    outputs = net(im)
    predict = torch.max(outputs, dim=1)[1].data.numpy()
#     predict = torch.softmax(outputs, dim=1)    # 显示张量的测试结果(每一个分类对应的测试概率)

# print(predict)
print(classes[int(predict)])


# if __name__ == '__main__':
#     main()


3、代码解析

model.py文件的解析

在编辑器中打开:

在这里插入图片描述

查看内置函数的详细定义

点击内置函数,按住Ctrl,点击鼠标右键,可以进入内置函数的详细定义

如查看: init() 初始化函数的定义:

在这里插入图片描述

另外,函数定义也可以在pytorch 的官网文档中查找说明:

如:Conv2d

在这里插入图片描述

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

pytorch 的官方文档使用

在这里插入图片描述

神经网络卷积与池化过程代码解析

神经网络第一层卷积和池化

在这里插入图片描述

在这里插入图片描述

具体数据计算:

16就是卷积核的个数,28 按照公式 (32-5+2*0)/ 1 + 1 = 28
这里w=32, F = 5, P = 0, S=1,
注意:这里的代码中 卷积参数 batch 没有 表示出来(一次输入多少张图片)

经过第一层的池化,把特征矩阵降维到原来的一半,卷积深度不变

在这里插入图片描述

神经网络的第二层卷积和池化

在这里插入图片描述

第二个下采样层与 第一个相同,用 22 ,步距为2 的池化核 ,将特征维度再次缩减为原来的一半。得到深度为32,55的特征矩阵。

全连接层

接下来是全连接层。将得到的特征矩阵进行矩阵的展平,成为一个一维向量

网络节点参数:

先理解一点:通常,一般无法分析计算人工神经网络中每层使用的层数或节点数,以解决特定的实际预测建模问题。
每层中的层数和节点数是必须指定的模型超参数。
你可能是第一个尝试使用神经网络解决自己的特定问题的人。在你之前没有人解决过它。
需要使用系统的实验来发现对特定数据集最有效的方法。

在这里插入图片描述

注意这里传入的最后一个参数: 10
官网中的数据集是:CIFAR10,一共是分了10个类别

在这里插入图片描述

在这里插入图片描述

正向传播过程

定义函数:

def forward(self, x):   # x 代表输入的数据
        x = F.relu(self.conv1(x))    # input(3, 32, 32) output(16, 28, 28)   # 16就是卷积核的个数,28 按照公式 (32-5+2*0)/ 1 + 1 = 28  
        x = self.pool1(x)            # output(16, 14, 14)
        x = F.relu(self.conv2(x))    # output(32, 10, 10)
        x = self.pool2(x)            # output(32, 5, 5)
        x = x.view(-1, 32*5*5)       # output(32*5*5)
        x = F.relu(self.fc1(x))      # output(120)
        x = F.relu(self.fc2(x))      # output(84)
        x = self.fc3(x)              # output(10)
        return x


具体数据含义:

x 代表输入的数据
· F.relu()是激活函数
· F.view()函数将得到的特征矩阵展开为一维向量, -1 代表第一个 维度(batch),32×5×5是展平特征向量后的节点个数
self.fc1(x) 表示全连接层作用

之后,将得到的一维向量分别通过下面的全连接1层以及激活函数的处理;全连接2层以及激活函数的处理,最终通过全连接层3得到最终的输出。

train.py文件解析

下载数据:

更改train.py文件进行训练数据集的下载,可以先将全部代码注释掉,只留下下载的代码运行:


transform = transforms.Compose(
    [transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # 50000张训练图片
    # 第一次使用时要将download设置为True才会自动去下载数据集
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
                                            download=True, transform=transform)

下载完以后,记得再把True 改为 False

下载的时候可以查看使用的具体函数定义:

张量化函数 : transforms.ToTensor()
ctrl + 鼠标右击查看(有兴趣深入的,其他函数同理可以自己看)

内置函数说明
这里说明了对图片输入后转化为张量处理
在这里插入图片描述

内置函数说明
标准化函数 : transforms.Normalize()

在这里插入图片描述

同时: Pytorch 官网储备有很多其他的数据集可以下载

可以 通过 torchvision.datasets.() 查看:

以上述相同的方式下载。

在这里插入图片描述

利用官方的代码加载查看数据集

相比官方的代码,修改了部分变量名 ,修改了batch_size的大小 10000 & 4


# functions to show an image

# 需要手动添加导入的库
# import numpy as np
# import matplotlib.pyplot as plt


def imshow(img):
    img = img / 2 + 0.5     # unnormalize 反标准化
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

# print labels
print(' '.join('%5s' % classes[val_label[j]] for j in range(4)))
# show images
imshow(torchvision.utils.make_grid(val_image))

数据集图片 显示效果:随机载入4张图片

在这里插入图片描述

实例化网络

定义损失函数

loss_function = nn.CrossEntropyLoss()     # 定义损失函数,其中已经包含了 logSoftmax 和 NLLLose 函数

内置函数说明
在这里插入图片描述

定义优化器
optimizer = optim.Adam(net.parameters(), lr=0.001)     # 传入训练的参数  , 学习率

为什么每计算一个batch, 就要调用以一次optimizer.zero_grad() 函数:

optimizer.zero_grad() 梯度清零函数

函数作用:清除历史梯度,如果不清除掉历史梯度,就会对计算的历史梯度进行累加
(通过这个特性你可以变相的实现一个很大batch数值的训练)

补充理解:

目前,我们在训练神经网络模型时,一般采用批梯度训练,大量实验表明,超参数batch size会影响模型收敛速度(训练时间)和模型效果。
通常,batch size越小,模型的收敛速度越慢;batch size越大,模型收敛速度越快,性能一般也会好一些。但是受限于设备的显存,我们不可能一直增大batch size。

所以,大多数情况下,在一个batch中先累加多个mini_batch计算的梯度,再对这个梯度进行反向传播。

就是将整个dataset分成多个batch,分别将每个batch分成多个mini_batch,将每个mini_batch喂给神经网络,计算loss,计算梯度,并将梯度保存下来,先不进行反向传播。再对一个batch中的所有mini_batch得到的梯度进行累加,并进行反向传播。等同于将一个batch喂给神经网络,计算loss,计算梯度,再进行反向传播。这样就可以根据显存大小,实现任何batch_size 大小的批梯度训练。

batch_size 的范围是 [1, len(dataset)]。
如果你有更多的显存,就把mini_batch_size 设大一点,显存不足则将mini_batch_size设小一点。

这个思想在 Tensorflow 和 Pytorch 中的都有体现

with torch.no_grad():
with是一个上下文管理器,

这个函数相对比较重要 ,作用:在这个函数接下来的计算中不会去计算每个节点的误差损失梯度 ,可以 节省算力/减少内存资源的占用

在测试,预测环节都应当使用这个函数。

训练结果

保存训练模型权重文件 Lenet.pth 文件

在这里插入图片描述

这里的训练结果并没有进行学习率的调整,而且考虑到该网络提出时间在1998年,所以,精确度还是相对不错的。

predect.py文件解析

另外找张10个类别中的图片,通过predect.py文件加载训练好的模型进行测试,就可以得到预测的结果和准确率。

text.jpg

在这里插入图片描述

预测结果:
在这里插入图片描述

不同形式的显示结果:
softmax() 函数返回的是测试的概率

在这里插入图片描述

4、总结

涉及的内容:

pytorch的安装

LeNet网络结构

通过pytorch搭建LeNet网络模型
卷积层、池化层、全连接层的代码构建,定义了损失函数、优化器、正向传播过程等

下载,显示了 pytorch官方CIFAR10数据集并导入网络模型

进行了网络训练,并将训练好的模型进行保存,通过预测的脚本进行训练模型效果预测

5、后续

本文并未使用GPU加速进行训练,而且官方给出了使用GPU加速的指导说明:

在这里插入图片描述

在这里插入图片描述

或者多线程GPU训练:

在这里插入图片描述

GPU 加速训练是必须掌握的技能,后续会补上更新的训练代码。

Reference

文章

https://blog.csdn.net/lxy_2011/article/details/98209706

https://blog.csdn.net/weixin_41560402/article/details/106930463

https://blog.csdn.net/Kefenggewu_/article/details/108294039

视频:
https://www.bilibili.com/video/BV187411T7Ye

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值