图像分类篇-1:LeNet

目录

前言

 pytorch官方demo

 1. 环境

2. LeNet介绍  

3. 具体demo搭建

3.1 demo下载

3.2 demo的流程

3.3 demo-model.py

3.4 demo-train.py

3.5 demo-predict.py


前言

这个是按照B站up主的教程学习这方面知识的时候自己做的的笔记和总结,可能有点乱,主要是按照我自己的记录习惯

参考内容来自:

  • up主的b站链接:https://space.bilibili.com/18161609/channel/series
  • up主将代码和ppt都放在了github:https://github.com/WZMIAOMIAO
  • up主的csdn博客:https://blog.csdn.net/qq_37541097/article/details/103482003

 pytorch官方demo

 1. 环境

                图像分类篇-1:LeNet_第1张图片


2. LeNet介绍  

图像分类篇-1:LeNet_第2张图片


3. 具体demo搭建

3.1 demo下载

  1. 下载来源:下载来源

3.2 demo的流程

  1. model.py——定义LeNet模型
  2. train.py——加载数据集并训练,训练集计算loss,测试集计算accuracy,保存训练好的网络参数
  3. predict.py——得到训练好的网络参数后,用自己找的图像进行分类测试(调用模型进行预测的一个脚本)

3.3 demo-model.py

import torch.nn as nn
import torch.nn.functional as F

'''
模型
1.定义一个类
·   1.初始化函数:搭建中需要使用的网络层结构
    2.forward函数:定义正向传播的过程
'''
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        # 使用super函数:因为在定义类的过程中继承了nn.Module这个类
        # super():解决在多层继承中调用父类方法中可能出现的一系列问题——继承父类的构造函数
        self.conv1 = nn.Conv2d(3, 16, 5)
        # 定义卷积层 3:输入特征的深度// 16:16个卷积核// 5:卷积核尺度5*5
        self.pool1 = nn.MaxPool2d(2, 2)
        # 池化层大小2*2 步距也为2的池化操作(池化层只改变特征矩阵的高和宽,不影响深度)
        self.conv2 = nn.Conv2d(16, 32, 5)
        # 第二个卷积层 输入深度为16,因为第一个输出深度为16
        # 此处为32个卷积核,卷积核的尺度为5*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) # 此处的10需要根据训练集进行修改,这里使用的cifar-10是具有10个类别的分类任务,所以这里是10

    def forward(self, x):
        x = F.relu(self.conv1(x))    # input(3, 32, 32) output(16, 28, 28)
        # 经过卷积后的矩阵尺寸大小计算公式:N = (W - F + 2P)/S + 1
        # 输入图片大小W*W// Filter大小为F*F// 步长S// padding的像素数P
        # W = 32// 故此处计算(32- 5 + 2*0)/1 + 1 = 28 因为上面设置了conv1为16的卷积核所以是(16, 28, 28)
        x = self.pool1(x)            # output(16, 14, 14) # 经过下采样,宽度和高度缩减一半
        x = F.relu(self.conv2(x))    # output(32, 10, 10)
        # 输入为14*14 (14 - 5 + 2*0)/1 + 1 = 10 输出10*10
        x = self.pool2(x)            # output(32, 5, 5) # 输出高度和宽度再缩减为原来的一半
        x = x.view(-1, 32*5*5)       # output(32*5*5)
        # 数据通过view函数展成一维向量// -1:代表第一个维度进行自动推理// 第二个维度就是展平后的节点个数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


对该模型进行测试:

再model.py后端加上测试代码

import torch
input1 = torch.rand([32, 3, 32, 32])
model = LeNet()
print(model)
output = model(input1)
  1. 在 forwar函数处打上断点进行debug图像分类篇-1:LeNet_第3张图片
  2. 可以看见x的大小图像分类篇-1:LeNet_第4张图片
  3.  切换到console图像分类篇-1:LeNet_第5张图片
  4.  继续调试,step over

  5. 发现x变化图像分类篇-1:LeNet_第6张图片
  6. 后续可以继续step over查看后续变化,调试完需要将调试代码删除(即测试代码)

 3.4 demo-train.py

  1. train.py代码所有代码注释掉,回到最上面,只导入上面的包图像分类篇-1:LeNet_第7张图片
  2. 导入数据集 :download更改为True,运行程序进行下载;下载完成后更改为False(此处transforme为对图像进行预处理的函数,此处使用了两个预处理方法ToTensor 和 Normalize)图像分类篇-1:LeNet_第8张图片
  3.  下载好的数据集​​​​​​

图像分类篇-1:LeNet_第9张图片

 进行测试看是否能通过imshow显示图片

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=True, transform=transform)
    train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
                                               shuffle=True, num_workers=0)  # 将数据集导入进来一个批次一个批次的,每一批随机拿出36张图片进行训练// shuffle是否打乱
    # num_workers:windows系统下只能设置为0,其他系统可以自己设置(现在windows可以不设置为0了,只要不超过支持的线程个数即可)

    # 10000张验证图片
    # 第一次使用时要将download设置为True才会自动去下载数据集
    testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                           download=False, transform=transform) # train为false为测试集
    testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                             shuffle=False, num_workers=0)
    test_data_iter = iter(testloader) # 将testloader转换成可迭代的迭代器
    test_image, test_label = next(test_data_iter) # 迭代器通过next这个方法获得数据(图像和图像对应的标签值)

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

    def imshow(img):
        img = img / 2 + 0.5  # unnormalize 对图像进行反标准化处理
        nping = img.numpy()
        plt.imshow(np.transpose(nping, (1, 2, 0))) # 通过transpose函数将shape转换为原来的通道排序[batch, channel, height, width]
        plt.show()
    # print labels
    print(''.join('%5s' % classes[test_label[j]] for j in range(4)))
    # show images
    imshow(torchvision.utils.make_grid(test_image))

#     net = LeNet()
#     loss_function = nn.CrossEntropyLoss()
#     optimizer = optim.Adam(net.parameters(), lr=0.001)
#
#     for epoch in range(5):  # loop over the dataset multiple times
#
#         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()
#             # forward + backward + optimize
#             outputs = net(inputs)
#             loss = loss_function(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():
#                     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)
#
#                     print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %
#                           (epoch + 1, step + 1, running_loss / 500, accuracy))
#                     running_loss = 0.0
#
#     print('Finished Training')
#
#     save_path = './Lenet.pth'
#     torch.save(net.state_dict(), save_path)


if __name__ == '__main__':
    main()

 测试结果如下:图像分类篇-1:LeNet_第10张图片

        4.最终进行训练的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=True, transform=transform)
    train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
                                               shuffle=True, num_workers=0)  # 将数据集导入进来一个批次一个批次的,每一批随机拿出36张图片进行训练// shuffle是否打乱
    # num_workers:windows系统下只能设置为0,其他系统可以自己设置(现在windows可以不设置为0了,只要不超过支持的线程个数即可)

    # 10000张验证图片
    # 第一次使用时要将download设置为True才会自动去下载数据集
    testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                           download=False, transform=transform) # train为false为测试集
    testloader = torch.utils.data.DataLoader(testset, batch_size=10000,
                                             shuffle=False, num_workers=0)
    test_data_iter = iter(testloader) # 将testloader转换成可迭代的迭代器
    test_image, test_label = next(test_data_iter) # 迭代器通过next这个方法获得数据(图像和图像对应的标签值)

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

    # 这一段是测试用的
    # def imshow(img):
    #     img = img / 2 + 0.5  # unnormalize 对图像进行反标准化处理
    #     nping = img.numpy()
    #     plt.imshow(np.transpose(nping, (1, 2, 0))) # 通过transpose函数将shape转换为原来的通道排序[batch, channel, height, width]
    #     plt.show()
    # # print labels
    # print(''.join('%5s' % classes[test_label[j]] for j in range(4)))
    # # show images
    # imshow(torchvision.utils.make_grid(test_image))

    net = LeNet()
    loss_function = nn.CrossEntropyLoss() #该函数包含了softmax
    optimizer = optim.Adam(net.parameters(), lr=0.001)  # 优化器Adam,将LeNet可训练的参数都进行训练,学习率为0.001

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

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

            # zero the parameter gradients
            optimizer.zero_grad() # 将历史梯度清零
            '''
            为什么每计算一个batch,就需要调用一次optimizer.zero_grad()
            因为如果不清除历史梯度,就会对计算的历史梯度进行累加'''
            # forward + backward + optimize
            outputs = net(inputs)
            loss = loss_function(outputs, labels) # 计算损失
            loss.backward() # 反向传播
            optimizer.step() # 参数更新

            # print statistics 打印数据的一个过程
            running_loss += loss.item()
            if step % 500 == 499:    # print every 500 mini-batches 每隔500步打印一次
                with torch.no_grad(): # 不用计算每个节点的误差损失梯度
                    outputs = net(test_image)  # [batch, 10]
                    predict_y = torch.max(outputs, dim=1)[1] # 维度1上寻找输出最大的,即10个节点中去寻找最大值
                    accuracy = torch.eq(predict_y, test_label).sum().item() / test_label.size(0)
                    # torch.eq(predict_y, test_label)预测标签类别和真实标签类别进行比较,相同返回true,在求和得到accuracy

                    print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %
                          (epoch + 1, step + 1, running_loss / 500, accuracy))
                    # epoch+1迭代到了第几轮,step+1多少步,runningloss训练中累加误差(因为每500步打印一次所以除以500,得到500步中训练的平均误差, accuracy测试样本的准确率)
                    running_loss = 0.0 # 清零 ,进行下一次500的迭代过程

    print('Finished Training')

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


if __name__ == '__main__':
    main()

 训练结果如下图像分类篇-1:LeNet_第11张图片

并且文件夹中生成Lenet.pth(训练完后生成的模型权重文件)

图像分类篇-1:LeNet_第12张图片


3.5 demo-predict.py

在网上随便下一张飞机的图片,像这样图像分类篇-1:LeNet_第13张图片

 放在路径下

图像分类篇-1:LeNet_第14张图片

 预测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)),
         transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # Resize((32, 32)),对图片大小进行预处理

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

    net = LeNet() # 实例化LeNet
    net.load_state_dict(torch.load('Lenet.pth')) # 通过net.load_state_dict载入刚刚保存的权重文件

    im = Image.open('1.jpeg') # 通过Image载入图像
    # 通过PIL或者numpy一般导入的图像shape都是 高度,宽度,深度channel
    im = transform(im)  # [C, H, W] 预处理得到 channel,高度,宽度的格式
    # pytorch tensor的通道排序要求是[batch, channel, height, width],所以需要再加一个维度
    im = torch.unsqueeze(im, dim=0)  # [N, C, H, W] 增加一个新的维度,最前面dim = 0增加一个新的维度

    with torch.no_grad(): # 不需要求损失梯度
        outputs = net(im)
        predict = torch.max(outputs, dim=1)[1].numpy() # 寻找输出中对应的最大的index
    print(classes[int(predict)]) # 将index传入classes


if __name__ == '__main__':
    main()

预测运行结果如下

图像分类篇-1:LeNet_第15张图片

尝试使用softmax,更改一下预测脚本最后部分,可以得到运行结果图像分类篇-1:LeNet_第16张图片 

你可能感兴趣的:(深度学习-图像分类,pytorch,深度学习,计算机视觉)