LeNet



1.视频教程:
B站、网易云课堂、腾讯课堂
2.代码地址:
Gitee
Github
3.存储地址:
Google云
百度云:
提取码:

《Gradient-based learning applied to document recognition》-1998
—LeNet
作者:
单位:
发表会议及时间:IEEE 1998
Submission history

Paper:https://ieeexplore.ieee.org/abstract/document/726791


  • Abstract

Multilayer neural networks trained with the back-propagation algorithm constitute the best example of a successful gradient based learning technique. Given an appropriate network architecture, gradient-based learning algorithms can be used to synthesize a complex decision surface that can classify high-dimensional patterns, such as handwritten characters, with minimal preprocessing. This paper reviews various methods applied to handwritten character recognition and compares them on a standard handwritten digit recognition task. Convolutional neural networks, which are specifically designed to deal with the variability of 2D shapes, are shown to outperform all other techniques. Real-life document recognition systems are composed of multiple modules including field extraction, segmentation recognition, and language modeling. A new learning paradigm, called graph transformer networks (GTN), allows such multimodule systems to be trained globally using gradient-based methods so as to minimize an overall performance measure. Two systems for online handwriting recognition are described. Experiments demonstrate the advantage of global training, and the flexibility of graph transformer networks. A graph transformer network for reading a bank cheque is also described. It uses convolutional neural network character recognizers combined with global training techniques to provide record accuracy on business and personal cheques. It is deployed commercially and reads several million cheques per day.


一 原理解析

LeNet5诞生于1994年,是最早的卷积神经网络之一,并且推动了深度学习领域的发展。自从1988年开始,在多年的研究和许多次成功的迭代后,这项由Yann LeCun完成的开拓性成果被命名为LeNet5。

1989年,Yann LeCun等人在贝尔实验室的研究首次将反向传播算法进行了实际应用,并且认为学习网络泛化的能力可以通过提供来自任务域的约束来大大增强。

他将使用反向传播算法训练的卷积神经网络结合到读取“手写”数字上,并成功应用于识别美国邮政服务提供的手写邮政编码数字。这即是后来被称为LeNet的卷积神经网络的雏形。

同年,Yann LeCun在发表的另一篇论文中描述了一个小的手写数字识别问题,并且表明即使该问题是线性可分的,单层网络也表现出较差的泛化能力。而当在多层的、有约束的网络上使用有位移不变性的特征检测器(shift invariant feature detectors)时,该模型可以在此任务上表现得非常好。

他认为这些结果证明了将神经网络中的自由参数数量最小化可以增强神经网络的泛化能力。

1990年他们发表的论文再次描述了反向传播网络在手写数字识别中的应用,他们仅对数据进行了最小限度的预处理,而模型则是针对这项任务精心设计的,并且对其进行了高度约束。

输入数据由图像组成,每张图像上包含一个数字,在美国邮政服务提供的邮政编码数字数据上的测试结果显示该模型的错误率仅有1%,拒绝率约为9%。

其后8年他们的研究一直继续,直到1998年,Yann LeCun,Leon Bottou,Yoshua Bengio和Patrick Haffner在发表的论文中回顾了应用于手写字符识别的各种方法,并用标准手写数字识别基准任务对这些模型进行了比较,结果显示卷积神经网络的表现超过了其他所有模型。

他们同时还提供了许多神经网络实际应用的例子,如两种用于在线识别手写字符的系统和能每天读取数百万张支票的模型。

他们的研究取得了巨大的成功,并且激起了大量学者对神经网络的研究的兴趣。

在今天向过去回首,目前性能最好的神经网络的架构已与LeNet不尽相同,但这个网络是大量神经网络架构的起点,并且也给这个领域带来了许多灵感。

在这里插入图片描述

二 代码实现

2.1 模型构建

import torch.nn as nn
import torch.nn.functional as F
import torch
'''
LeNet网络模型构建
'''
class LeNet(nn.Module):
    # 初始化类别数,全连接接受层维数(由输入图像大小决定)
    def __init__(self, num_classes ,num_linear=44944):
        super(LeNet, self).__init__()
        # 定义卷积层1(输入通道数、输出通道数、卷积核大小)
        self.conv1 = nn.Conv2d(3, 6, 5)
        # 定义卷积层2(输入通道数、输出通道数、卷积核大小)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # 线性层(全连接层)1
        self.fc1 = nn.Linear(num_linear, 120)  # 44944,这里nn.Linear的第一个参数随输入数据大小改变而改变
        # 线性层2(输入特征图个数,输出特征图个数)
        self.fc2 = nn.Linear(120, 84)
        # 线性层3
        self.fc3 = nn.Linear(84, num_classes)

    # 构建前向传播(使用init中创建的各网络层)
    def forward(self, x):
        # 卷积层1+relu激活
        out = F.relu(self.conv1(x))
        # 最大池化
        out = F.max_pool2d(out, 2)
        # 卷积层2+relu激活
        out = F.relu(self.conv2(out))
        # 最大池化
        out = F.max_pool2d(out, 2)
        # flatten,扁平向量化
        out = out.view(out.size(0), -1)
        # 线性层1+relu激活
        out = F.relu(self.fc1(out))
        # 线性层2+relu激活
        out = F.relu(self.fc2(out))
        # 线性层relu3
        out = self.fc3(out)
        # 输出
        return out



if __name__ == '__main__':
    # 随机生成输入数据
    rgb = torch.randn(1, 3, 224, 224)
    # 定义网络
    # # num_linear的设置是为了,随着输入图片数据大小的改变,使线性层的神经元数量可以匹配成功
    # 默认输入图片数据大小为224*224
    net = LeNet(num_classes=8, num_linear=44944)
    # 前向传播
    out = net(rgb)
    print('-----' * 5)
    # 打印输出大小
    print(out.shape)
    print('-----' * 5)

2.2 模型训练

# ============================ 导入工具包包 ============================
import numpy as np  # numpy
import torch  # torch
import torch.nn as nn  ## 网络模块
from torch.utils.data import DataLoader  # 数据加载器
import torchvision.transforms as transforms  # 预处理
import torch.optim as optim  # 优化器
import torch.nn.functional as F  # 激活函数
from PIL import Image
import random
from torch.utils.data import Dataset
import os

# ============================ step 0/5 参数设置 ============================

# 标签和名称字典
dict_label_name = {}
# 设备
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')

MAX_EPOCH = 100  # 训练轮数
BATCH_SIZE = 4  # 训练批次大小
LR = 0.001  # 学习率
log_interval = 10  # 打印训练信息,日志记录间隔(记录到本地txt)
val_interval = 3  # 评估间隔(训练几个Epoch再验证一次)
norm_mean = [0.485, 0.456, 0.406]  # 均值
norm_std = [0.229, 0.224, 0.225]  # 方差
resize = (224, 224)  # 图像固定大小
train_dataset_path = r"C:\Users\11716\Desktop\CatDogDet\train"  # 训练集路径
val_dataset_path = r"C:\Users\11716\Desktop\CatDogDet\val"  # 验证集路径
num_classes = 2  # 类别数
num_linear = 44944  # 对应图像大小的接入线性层大小
saved_best_model_path = r"best.pt"  # 指标最优模型保存地址
saved_last_model_path = r"last.pt"  # 指标最新模型保存地址


# ============================ 辅助函数 ============================
# 设置随机种子(用于还原模型训练指标(效果))
def set_seed(seed=1):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)


# 获取目录下所有文件夹路径
def get_dirs_path(dir_path):
    files_dirs_path = os.listdir(dir_path)
    dirs_path = []
    for i in files_dirs_path:
        if(os.path.isdir(os.path.join(dir_path,i))):
            dir_p = os.path.join(dir_path,i)
            dirs_path.append(dir_p)
    return dirs_path

# 获取目录下所有文件路径
def get_files_path(dir_path):
    files_path = os.listdir(dir_path)
    files_path_list = []
    for i in files_path:
        if (os.path.isfile(os.path.join(dir_path,i))):
            filw_p = os.path.join(dir_path,i)
            files_path_list.append(filw_p)
    return files_path_list
# ============================ 网络模型构建 ============================
'''
LeNet网络模型构建
'''
class LeNet(nn.Module):
    # 初始化类别数,全连接接受层维数(由输入图像大小决定)
    def __init__(self, num_classes ,num_linear=44944):
        super(LeNet, self).__init__()
        # 定义卷积层1(输入通道数、输出通道数、卷积核大小)
        self.conv1 = nn.Conv2d(3, 6, 5)
        # 定义卷积层2(输入通道数、输出通道数、卷积核大小)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # 线性层(全连接层)1
        self.fc1 = nn.Linear(num_linear, 120)  # 44944,这里nn.Linear的第一个参数随输入数据大小改变而改变
        # 线性层2(输入特征图个数,输出特征图个数)
        self.fc2 = nn.Linear(120, 84)
        # 线性层3
        self.fc3 = nn.Linear(84, num_classes)

    # 构建前向传播(使用init中创建的各网络层)
    def forward(self, x):
        # 卷积层1+relu激活
        out = F.relu(self.conv1(x))
        # 最大池化
        out = F.max_pool2d(out, 2)
        # 卷积层2+relu激活
        out = F.relu(self.conv2(out))
        # 最大池化
        out = F.max_pool2d(out, 2)
        # flatten,扁平向量化
        out = out.view(out.size(0), -1)
        # 线性层1+relu激活
        out = F.relu(self.fc1(out))
        # 线性层2+relu激活
        out = F.relu(self.fc2(out))
        # 线性层relu3
        out = self.fc3(out)
        # 输出
        return out

# ============================ 数据集加载器 ============================
# 获取数据集所有的图像和标签,并将图像和标签合并,再进行返回
def get_img_label(data_dir):
    # 初始化返回图像标签列表
    imgs_labels_list = []
    # 获取目录下所有类别文件夹
    list_dirs = get_dirs_path(data_dir)
    # 对文件夹路径进行排序(保持标签稳定)
    list_dirs.sort()
    # 遍历每个类别文件夹,获取图像路径和对应标签
    for index_label, single_class in enumerate(list_dirs):
        # 获取类别
        className = os.path.basename(single_class)
        dict_label_name[index_label] = className
        # 遍历所有文件夹中所有文件
        all_files = get_files_path(single_class)
        # 赋值返回图像标签列表
        for item in all_files:
            imgs_labels_list.append((item,index_label))
    return imgs_labels_list


class LoadDataset(Dataset):
    # 构建数据集(输入路径,和预处理)
    def __init__(self, data_dir, transform=None):
        self.imgs_labels_list = get_img_label(data_dir)  # data_info存储所有图片路径和标签
        self.transform = transform

    # 反馈图像和标签(预处理后的)
    def __getitem__(self, index):
        # 根据索引Index获取图像和标签
        path_img, label = self.imgs_labels_list[index]
        # 读取图像,并转为RGB
        img = Image.open(path_img).convert('RGB')     # 0~255
        # 如果有预处理,则进行预处理
        if self.transform is not None:
            img = self.transform(img)   # 在这里做transform,转为tensor等等
        return img, label

    # 确定数据集个数(默认所有数据集个数)
    def __len__(self):
        return len(self.imgs_labels_list)




def train():
    # ============================ step 1/5 数据 ============================
    # 训练数据预处理
    train_transform = transforms.Compose([
        transforms.Resize(resize),  # resize
        transforms.ToTensor(),  # 转tensor型变量
        transforms.Normalize(norm_mean, norm_std),  # 标准化
    ])
    transforms.ColorJitter(brightness=0.5)
    # 验证数据预处理
    valid_transform = transforms.Compose([
        transforms.Resize(resize),  # resize
        transforms.ToTensor(),  # 转tensor型变量
        transforms.Normalize(norm_mean, norm_std),  # 标准化
    ])

    # 构建训练集的Dataset和DataLoader
    train_dataset = LoadDataset(data_dir=train_dataset_path, transform=train_transform)
    train_loader = DataLoader(dataset=train_dataset, batch_size=BATCH_SIZE, shuffle=True)  # shuffle训练时打乱样本

    # 构建验证集的Dataset和DataLoader
    valid_dataset = LoadDataset(data_dir=val_dataset_path, transform=valid_transform)
    valid_loader = DataLoader(dataset=valid_dataset, batch_size=BATCH_SIZE)

    # ============================ step 2/5 模型 ============================
    net = LeNet(num_classes=num_classes,num_linear=num_linear)
    # 转进设备(主要为了GPU加速)
    net = net.to(device)
    # 如果存在最优模型,则直接加载最优模型
    if os.path.exists(saved_best_model_path):
        net = torch.load(saved_best_model_path,map_location=device)
    # ============================ step 3/5 损失函数 ============================
    criterion = nn.CrossEntropyLoss()                                                   # 选择损失函数

    # ============================ step 4/5 优化器 ============================
    optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9)                        # 选择优化器
    scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)     # 设置学习率下降策略

    # ============================ step 5/5 训练 ============================
    # 存储最优指标,用于Early Stopping
    best = [0]

    # 训练
    for epoch in range(MAX_EPOCH):
        '''
          初始化指标
        '''
        # 平均损失
        loss_mean = 0.
        # 预测正确的数量
        correct = 0.
        # 总预测数量
        total = 0.

        '''
           训练
        '''
        # 设置网络为训练模式
        net.train()

        # 按batchsize遍历所有数据集
        for i, data in enumerate(train_loader):
            # 获取图标和标签
            inputs, labels = data
            # 转进设备(主要为了进行GPU加速)
            inputs = inputs.to(device)
            labels = labels.to(device)
            # 前向传播,获取输出
            outputs = net(inputs)
            # 优化器梯度清零
            optimizer.zero_grad()  # 梯度置零,设置在loss之前
            # 损失函数计算损失
            loss = criterion(outputs, labels)  # 一个batch的loss
            # 反向传播
            loss.backward()  # loss反向传播
            # 更新权重
            optimizer.step()  # 更新所有的参数
            # 统计分类情况  注:参数1: 返回索引的意思
            _, predicted = torch.max(outputs.data, 1)
            # 当前训练数据总数
            total += labels.size(0)
            # GPU
            if torch.cuda.is_available():
                # 对预测值和标签进行对比,得出是否相等标志位
                flag_predict = (predicted == labels)
                # 对预测值和标签值一致的个数进行计数
                current_correct_num = flag_predict.squeeze().sum().cpu().numpy()
                # 计算一共正确的个数
                correct += current_correct_num
            # CPU
            else:
                # 当前所有正确预测数量
                correct += (predicted == labels).squeeze().sum().numpy()  # 计算一共正确的个数

            # 计算一共的loss
            loss_mean += loss.item()

            # 打印训练信息,日志记录间隔(记录到本地txt)
            if (i+1) % log_interval == 0:   # log_interval=10 表示每迭代10次,打印一次训练信息,在这里bachsize=16 迭代10次就是160张图片,即total=160
                # 取平均loss
                loss_mean = loss_mean / log_interval
                # 打印训练信息(当前训练轮数、总训练轮数、当前迭代次数,总迭代次数,损失值,准确率)
                print("Training:Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                    epoch, MAX_EPOCH, i+1, len(train_loader), loss_mean, correct / total))

                '''
                    打印验证信息到本地txt
                '''
                # 保存训练信息,即写日志
                f = open("log_training.txt", 'a')  # 若文件不存在,系统自动创建。'a'表示可连续写入到文件,保留原内容,在原
                # 内容之后写入。可修改该模式('w+','w','wb'等)
                f.write("Training:Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                    epoch, MAX_EPOCH, i+1, len(train_loader), loss_mean, correct / total))  # 将字符串写入文件中
                f.write("\n")  # 换行

                # 每次需要清0
                loss_mean = 0.

        # 更新学习率(学习率策略更新器)
        scheduler.step()

        # 保存最新模型
        torch.save(net,saved_last_model_path)

        # 验证模型(这里保存最优模型)
        if (epoch+1) % val_interval == 0:  # val_interval=1 表示每一个epoch打印一次验证信息
            '''
              初始化指标
            '''
            correct_val = 0. #  预测正确总数
            total_val = 0.  # 预测总数
            loss_val = 0.  # 所有损失

            '''
               评估
            '''
            net.eval()  # 模型保持静止,不进行更新,从而来验证
            with torch.no_grad():  # 不保存梯度,减少内存消耗,提高运行速度
                # 遍历所有验证集
                for j, data in enumerate(valid_loader):
                    # 获取图像和标签
                    inputs_val, labels_val = data
                    # 转设备,主要为了GPU加速
                    inputs_val = inputs_val.to(device)
                    labels_val = labels_val.to(device)
                    # 前向传播,获取输出
                    outputs = net(inputs_val)
                    # 计算损失
                    loss = criterion(outputs, labels_val)
                    # 统计分类情况  注:参数1: 返回索引的意思
                    _, predicted = torch.max(outputs.data, 1)
                    # 当前训练数据总数
                    total_val += labels_val.size(0)
                    # GPU
                    if torch.cuda.is_available():
                        # 对预测值和标签进行对比,得出是否相等标志位
                        flag_predict = (predicted == labels_val)
                        # 对预测值和标签值一致的个数进行计数
                        current_correct_num = flag_predict.squeeze().sum().cpu().numpy()
                        # 计算一共正确的个数
                        correct_val += current_correct_num
                    # CPU
                    else:
                        # 当前所有正确预测数量
                        correct_val += (predicted == labels_val).squeeze().sum().numpy()  # 计算一共正确的个数

                    loss_val += loss.item()

                '''
                    保存最优模型
                '''
                if max(best) <= correct_val / total_val:
                    best.append(correct_val / total_val)
                    torch.save(net, saved_best_model_path)

                # 打印验证指标信息,(当前训练轮数、总训练轮数、当前迭代次数,总迭代次数,损失值,准确率)
                print("Valid:\t Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                    epoch, MAX_EPOCH, j+1, len(valid_loader), loss_val, correct_val / total_val))

                '''
                    打印验证信息到本地txt
                '''
                f = open("log_training.txt", 'a')  # 若文件不存在,系统自动创建。'a'表示可连续写入到文件,保留原内容,在原
                # 内容之后写入。可修改该模式('w+','w','wb'等)
                f.write("Valid:\t Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                    epoch, MAX_EPOCH, j+1, len(valid_loader), loss_val, correct_val / total_val))  # 将字符串写入文件中
                f.write("\n")  # 换行



if __name__ == '__main__':
    # 设置随机种子
    set_seed(seed=1)
    # 训练
    train()

2.3 模型预测

import os
import time
from PIL import Image
import torch
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
# 标签和类别的映射关系
classes = ["airplane", "automobile", "bird", "cat", "deer","dog", "frog", "horse", "ship", "truck"]
# 设备
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
norm_mean = [0.485, 0.456, 0.406]  # 均值
norm_std = [0.229, 0.224, 0.225]  # 方差
saved_model_path = r""
num_classes = 2  # 类别数
num_linear = 44944  # 对应图像大小的接入线性层大小
BATCH_SIZE = 4  # 训练批次大小
saved_best_model_path = r""  # 指标最优模型保存地址
saved_last_model_path = r""  # 指标最新模型保存地址

# 1.model.eval()
# 2.torch.no_grad()
# 3.数据预处理保持一致
# 4.预测时间的计算

inference_transform = transforms.Compose([
    transforms.Resize((32, 32)),
    transforms.ToTensor(),
    transforms.Normalize(norm_mean, norm_std),
])

def preprocessing(img,transform = None):
    if transforms is None:
        raise Exception("无transform进行预处理")
    img_tensor = transform(img)
    return img_tensor


def detect(img_path):
    # 1. data
    img_path = img_path
    # 2. model
    net = torch.load(saved_best_model_path)
    net.to(device)
    net.eval()
    # 3.单图predict
    with torch.no_grad():
        # step 1/4 : path --> img
        img_rgb = Image.open(img_path).convert('RGB')

        # step 2/4 : img --> tensor
        img_tensor = preprocessing(img_rgb, inference_transform)
        img_tensor.unsqueeze_(0)
        img_tensor = img_tensor.to(device)

        # step 3/4 : tensor --> vector
        time_start = time.time()
        outputs = net(img_tensor)
        time_end = time.time()

        # step 4/4 : visualization
        print(outputs)
        _, pred_int = torch.max(outputs, 1)
        print(pred_int)
        pred_str = classes[int(pred_int.cuda().data.cpu().numpy())]
        print(pred_str)

    # 4.多图预测
    with torch.no_grad():
        # step 1/4 : path --> img
        path = r"D:\Classification_Demo\major_dataset_repo\split_data\test\0"
        files_list = os.listdir(path)
        file_path_list = [os.path.join(path, img) for img in files_list]

        for i in range(100):
            img_rgb = Image.open(file_path_list[i]).convert('RGB')

            # step 2/4 : img --> tensor
            img_tensor = preprocessing(img_rgb, inference_transform)
            img_tensor.unsqueeze_(0)
            img_tensor = img_tensor.to(device)

            # step 3/4 : tensor --> vector
            time_start = time.time()
            outputs = net(img_tensor)
            time_end = time.time()
            print("所耗时间:", time_end - time_start)

            # step 4/4 : visualization
            print(outputs)
            _, pred_int = torch.max(outputs, 1)
            print(pred_int)
            pred_str = classes[int(pred_int.cuda().data.cpu().numpy())]
            print(pred_str)



if __name__ == "__main__":
   # 检测
   detect()

2.4 模型验证

#  for major_test
import torch
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
from PIL import Image
from torch.utils.data import Dataset
import os
# ============================ step 0/5 参数设置 ============================
# 设备
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
saved_model_path = r""  # 模型保存地址
norm_mean = [0.485, 0.456, 0.406]  # 均值
norm_std = [0.229, 0.224, 0.225]  # 方差
val_dataset_path = r""  # 验证集路径
BATCH_SIZE = 4
saved_best_model_path = r""  # 指标最优模型保存地址
saved_last_model_path = r""  # 指标最新模型保存地址


# ============================ 辅助函数 ============================
def evaluteTop1(model, loader):
    model.eval()
    correct = 0
    total = len(loader.dataset)

    for x, y in loader:
        x, y = x.to(device), y.to(device)
        with torch.no_grad():
            logits = model(x)
            pred = logits.argmax(dim=1)
            correct += torch.eq(pred, y).sum().float().item()
        # correct += torch.eq(pred, y).sum().item()
    return correct / total


def evaluteTop5(model, loader):
    model.eval()
    correct = 0
    total = len(loader.dataset)
    for x, y in loader:
        x, y = x.to(device), y.to(device)
        with torch.no_grad():
            logits = model(x)
            maxk = max((1, 5))
            y_resize = y.view(-1, 1)
            _, pred = logits.topk(maxk, 1, True, True)
            correct += torch.eq(pred, y_resize).sum().float().item()
    return correct / total


# 获取文件夹
def get_dirs_path(dir_path):
    files_dirs_path = os.listdir(dir_path)
    dirs_path = []
    for i in files_dirs_path:
        if(os.path.isdir(os.path.join(dir_path,i))):
            dir_p = os.path.join(dir_path,i)
            dirs_path.append(dir_p)
    return dirs_path

# ============================ 数据集加载器 ============================
# 获取数据集所有的图像和标签,并将图像和标签合并,再进行返回
def get_img_label(data_dir):
    list_dirs = get_dirs_path(os.path.join(data_dir,"train"))
    data_info = list()
    for root, dirs, _ in os.walk(data_dir):
        # 遍历类别
        for sub_dir in dirs:
            img_names = os.listdir(os.path.join(root, sub_dir))
            img_names = list(filter(lambda x: x.endswith('.png'), img_names)) # 过滤,剩下.png结尾的文件名
            # 遍历图片
            for i in range(len(img_names)):
                img_name = img_names[i]
                path_img = os.path.join(root, sub_dir, img_name) # 完整图片路径
                label = dict_label[sub_dir] # 获取当前图片的标签
                data_info.append((path_img, int(label))) # 返回 [(path_img1,label1),(path_img2,label2),...]
    return data_info


class LoadDataset(Dataset):
    # 构建数据集(输入路径,和预处理)
    def __init__(self, data_dir, transform=None):
        self.imgs_labels = get_img_label(data_dir)  # data_info存储所有图片路径和标签
        self.transform = transform

    # 反馈图像和标签(预处理后的)
    def __getitem__(self, index):
        # 根据索引Index获取图像和标签
        path_img, label = self.imgs_labels[index]
        # 读取图像,并转为RGB
        img = Image.open(path_img).convert('RGB')     # 0~255
        # 如果有预处理,则进行预处理
        if self.transform is not None:
            img = self.transform(img)   # 在这里做transform,转为tensor等等
        return img, label

    # 确定数据集个数(默认所有数据集个数)
    def __len__(self):
        return len(self.data_info)


def validate():
    # 1.加载测试数据
    # 1.1 预处理
    test_transform = transforms.Compose([
        transforms.Resize((32, 32)),
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    # 1.2 数据加载
    test_data = LoadDataset(data_dir=val_dataset_path, transform=test_transform)
    test_loader = DataLoader(dataset=test_data, batch_size=BATCH_SIZE, shuffle=True)  # shuffle训练时打乱样本

    # 2.加载模型(默认加载最优模型)
    net = torch.load(saved_best_model_path)

    # 3.评测
    evaluteTop1(net,test_loader)
    evaluteTop5(net,test_loader)


if __name__ == "__main__":
    # 验证
    validate()

三 问题思索

四 改进方案

五 额外补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值