深度学习图像分类实战——pytorch搭建卷积神经网络(AlexNet, LeNet, ResNet50)进行场景图像分类(详细)

2023-05-16

目录

1  一、实验过程

1.1  实验目的

1.2  实验简介

1.3  数据集的介绍

1.4  一、LeNet5网络模型

1.5  二、AlexNet网络模型

1.6  三、ResNet50(残差网络)网络模型

 二、实验代码

导入实验所需要的库

 参数配置

数据预处理

重新DataSet

加载数据转为DataLoader函数

可视化一批训练数据

 构建模型

搭建训练函数

搭建测试函数

实例化模型开始训练

 开始测试

参考文献


 

1  一、实验过程

1.1  实验目的

通过这个课程项目大,期望达到以下目的:

  • 1.了解如何对深度学习的图像数据集进行预处理操作。
  • 2.熟络深度学习训练模型的步骤流程、pytorch的使用。
  • 3.学习ResNet-50网络模型思想、网络架构和代码实现。
  • 4.学习深度学习中的图像分类任务。

1.2  实验简介

这个项目名称为“搭建卷积神经网络进行图像分类”,要求针对提供的场景图片数据集进行模型的训练,并对测试集进行场景类别的预测。 这个项目提供了 6700 张场景图像,包括了67种不同的场景。数据集存在曝光度差异、分辨率不同等多种干扰,具有一定的挑战性。 实验采用经典的ResNet50模型。

1.3  数据集的介绍

数据集包括 6700 张已经分割好的场景图像。其中 67 * 80 张作为训练集,其余 67 * 20张作为测试集。场景图像包括了机场场景、办公室场景、居家场景等67种场景,数据样本丰富。要求根据场景图像,设计一种卷积神经网络模型,自动判断场景。数据集包含如下内容:

1.train.csv - 训练集,其中包括两列,第一列id是人脸图像的编号,即对应的文件名,第二列label是性别标签,0表示男性,1表示女性

2.test.csv - 训练集,其中包括两列,第一列id是人脸图像的编号,即对应的文件名,第二列label是性别标签,0表示男性,1表示女性

3.train文件夹 - 所有的训练图像,扩展名为jpg,其命名与train.csv中的id命名一致

4.test文件夹 - 所有的测试图像,扩展名为jpg,其命名与test.csv中的id命名一致

1.4  一、LeNet5网络模型

手写字体识别模型LeNet5诞生于1994年,是最早的卷积神经网络之一。LeNet5通过巧妙的设计,利用卷积、参数共享、池化等操作提取特征,避免了大量的计算成本,最后再使用全连接神经网络进行分类识别,这个网络也是最近大量神经网络架构的起点。

LeNet5 的架构基于这样的观点:(尤其是)图像的特征分布在整张图像上,以及带有可学习参数的卷积是一种用少量参数在多个位置上提取相似特征的有效方式。这和将每个像素用作一个大型多层神经网络的单独输入相反。LeNet5 阐述了那些像素不应该被使用在第一层,因为图像具有很强的空间相关性,而使用图像中独立的像素作为不同的输入特征则利用不到这些相关性。 Lenet5特征能够总结为如下几点:

  • (1)卷积神经网络使用三个层作为一个系列:卷积、池化、非线性
  • (2)使用卷积提取空间特征
  • (3)使用映射到空间均值下采样
  • (4)双曲线(tanh)或s型(sigmoid)形式的非线性
  • (5)多层神经网络(MLP)作为最后的分类器
  • (6)层与层之间的稀疏链接矩阵避免大的计算成本

模型架构图如下:

1.5  二、AlexNet网络模型

2012年,ImageNet比赛冠军的model–Alexnet,可以说是LeNet的一种更深更宽的版本。AlexNet包含了6亿3000万个连接,6000万个参数和65万个神经元,拥有5个卷积层,其中3个卷积层后面连接了最大池化层,最后还有3个全连接层。AlextNet以显著的优势赢得了ILSVRC比赛的冠军,top-5的错误率从之前的25.8%降低至16.4。

AlexNet网络结构相对简单,使用了8层卷积神经网络,前5层是卷积层,剩下的3层是全连接层。

具体如下所示:

1.6  三、ResNet50(残差网络)网络模型

深度残差网络(Deep residual network, ResNet)的提出是CNN图像史上的一件里程碑事件。ResNet的作者何恺明也因此摘得CVPR2016最佳论文奖,当然何博士的成就远不止于此,感兴趣的也可以去搜一下他后来的辉煌战绩。下面简单讲述ResNet的理论及实现。

  1. 残差学习 深度网络的退化问题至少说明深度网络不容易训练。但是我们考虑这样一个事实:现在你有一个浅层网络,你想通过向上堆积新层来建立深层网络,一个极端情况是这些增加的层什么也不学习,仅仅复制浅层网络的特征,即这样新层是恒等映射(Identity mapping)。在这种情况下,深层网络应该至少和浅层网络性能一样,也不应该出现退化现象。

这个有趣的假设让何博士灵感爆发,他提出了残差学习来解决退化问题。对于一个堆积层结构(几层堆积而成)。对于一个堆积层结构(几层堆积而成)当输入为 x时其学习到的特征记为F(x), 现在再加一条分支,直接跳到堆积层的输出,则此时最终输出H(x) = F(x) + x

如下图

这种跳跃连接就叫做shortcut connection(类似电路中的短路)。具体原理这边不展开叙说,感兴趣的可以去看原论文。上面这种两层结构的叫BasicBlock,一般适用于ResNet18和ResNet34,而ResNet50以后都使用下面这种三层的残差结构叫Bottleneck。

  1. ResNet50具体结构 可以看到,50层的网络有五个部分组成,从STAGE2开始,每层都有多个有残差块,并且每个残差块具有3个卷积层。

 二、实验代码

导入实验所需要的库

import os
import math
import random
import collections
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import torch
from torch import nn
from PIL import Image
import torch.optim as optim
from tqdm.notebook import tqdm
from torchvision import transforms
from torch.utils.data import Dataset
from torch.utils.data import DataLoader

import warnings
warnings.filterwarnings("ignore")

# cpu or gpu
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print("Using {} device".format(device))

 参数配置

#coding:utf8

class DefaultConfig(object):
    # 这里写默认初始化参数
    lr = 1e-5                # 学习率
    momentum = 0.5           # 如果是SGD优化器会用到动量
    num_classes = 67          # 类别个数
    image_size = 224         # 图像大小
    root_dir = '/kaggle/input/indoor-scenes-cvpr-2019/indoorCVPR_09/Images/' # 数据集的根目录
    batch_size = 64          # 批数据量 
    train_size = 5360       # 训练集个数
    test_size = 1340         # 测试集个数
    epoches = 10              # 训练轮数
    class_dict = {}          # 类别字典

    
    def parse(self, kwargs):
        '''
        根据字典kwargs,更新参数
        '''
        # 如果类属性不存在 kwargs 中的属性, 则向类添加该属性
        print('user config ----- not exist attribute')
        for k, v in kwargs.items():
            if not hasattr(self, k):  # 如果不存在改配置,抛出警告
                print(k, " ---> ", v)
                warn_test = "warning: opt has not attribute【{}】".format(k)
                warnings.warn(warn_test)
            setattr(self, k, v)  # 添加、更改、设置属性

        print('user config ----- already exist attribute')
        for k, v in self.__class__.__dict__.items():
            if not k.startswith('__'):
                print(k, " ---> ", getattr(self, k))  # 获取属性值
                pass
    
# 测试
opt =DefaultConfig()
config = {"data":"22-12-6", "momentum":0.1}  # 添加data属性,更改momentum属性
opt.parse(config)
opt.momentum

'''
user config ----- not exist attribute
data  --->  22-12-6
user config ----- already exist attribute
lr  --->  1e-05
momentum  --->  0.1
num_classes  --->  67
image_size  --->  224
root_dir  --->  /kaggle/input/indoor-scenes-cvpr-2019/indoorCVPR_09/Images/
batch_size  --->  64
train_size  --->  5360
test_size  --->  1340
epoches  --->  10
class_dict  --->  {}
parse  --->  <bound method DefaultConfig.parse of <__main__.DefaultConfig object at 0x7f34fab84150>>
'''

数据预处理

# 生成类别字典
def class_name_dict(root_dir):
    dir_list = os.listdir(root_dir)
    class_num = 0
    for img_dir in dir_list:
        opt.class_dict[str(img_dir)] = class_num
        class_num += 1

class_name_dict(opt.root_dir)
opt.class_dict

'''
{'meeting_room': 0,
 'grocerystore': 1,
 'trainstation': 2,
 'mall': 3,
 'bar': 4,
 'auditorium': 5,
 'laboratorywet': 6,
 'florist': 7,
 'closet': 8,
 'livingroom': 9,
 'pantry': 10,
 'airport_inside': 11,
 'prisoncell': 12,
 'locker_room': 13,
 'elevator': 14,
 'dentaloffice': 15,
 'laundromat': 16,
 'fastfood_restaurant': 17,
 'casino': 18,
 'dining_room': 19,
 'kindergarden': 20,
 'concert_hall': 21,
 'waitingroom': 22,
 'bathroom': 23,
 'corridor': 24,
 'bedroom': 25,
 'hairsalon': 26,
 'kitchen': 27,
 'tv_studio': 28,
 'artstudio': 29,
 'library': 30,
 'inside_bus': 31,
 'restaurant_kitchen': 32,
 'inside_subway': 33,
 'buffet': 34,
 'bookstore': 35,
 'museum': 36,
 'lobby': 37,
 'gameroom': 38,
 'shoeshop': 39,
 'garage': 40,
 'poolinside': 41,
 'clothingstore': 42,
 'deli': 43,
 'subway': 44,
 'jewelleryshop': 45,
 'stairscase': 46,
 'toystore': 47,
 'classroom': 48,
 'restaurant': 49,
 'nursery': 50,
 'bakery': 51,
 'bowling': 52,
 'office': 53,
 'operating_room': 54,
 'warehouse': 55,
 'studiomusic': 56,
 'church_inside': 57,
 'computerroom': 58,
 'cloister': 59,
 'greenhouse': 60,
 'winecellar': 61,
 'gym': 62,
 'videostore': 63,
 'hospitalroom': 64,
 'children_room': 65,
 'movietheater': 66}
'''

重新DataSet

class MyData(Dataset):
        
    def __init__(self, file_name, transform=None, type_data=""):
        img_list_path = []
        label_list = []
        if file_name and os.path.exists(file_name):
            try:
                with open(file_name) as f:  # 使用with读文件
                    file = f.readlines()  # 返回list列表
                    count = len(file)
                    for path in file:
#                         print(path)
                        path = path.rstrip()  # 删除换行符
                        label = path.split(r"/")[0]
#                         print(path.split(r"/"))
                        label = opt.class_dict.get(label)  # 从字典列表中匹配标签
                        img_path = os.path.join(opt.root_dir, path)  # 生成图片路径
                        img_list_path.append(img_path)
                        label_list.append(label)
#                         print(img_path) 
#                         print(label) 
                    f.close()
            except IOError as err:
                print(err)
        else:
            print("%s is not a validate file." % file_name)
        self.img_list_path = img_list_path
        self.label_list = label_list
        self.transform = transform
        self.type_data = type_data
        self.dict_count = {}   # 统计每个类别的数量,字典类型
        self.class_num()
        
    
    def __getitem__(self, idx):
        path = self.img_list_path[idx]
        label = self.label_list[idx]
        image = Image.open(path)
        if(image.mode!='RGB'):        
            image = image.convert("RGB")
        if self.transform is not None:
            image = self.transform(image)
        return image,label
    
    def __len__(self):
        return len(self.img_list_path)
    
    
    def class_num(self):
        '''统计每个类别的样本数'''
        for c in opt.class_dict.values():
            self.dict_count[c] = self.label_list.count(c)
        print(self.type_data + '总样本数:', len(self.label_list), "各类别数:", self.dict_count)
        
        
if __name__ == "__main__":
    test = MyData('/kaggle/input/indoor-scenes-cvpr-2019/TestImages.txt', None, "测试集")

加载数据转为DataLoader函数

# 训练数据的transforms
train_transform = transforms.Compose([
    transforms.Resize(opt.image_size),
    transforms.RandomVerticalFlip(),  # 随机垂直翻转
    transforms.RandomHorizontalFlip(),  # 水平转换
    transforms.ToTensor(),              # 转换为张量类型
    #将图像的像素值归一化到[-1,1]之间
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

# 测试数据的transforms
test_transform = transforms.Compose([
        transforms.Resize((opt.image_size, opt.image_size)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])


train_dataset = MyData('/kaggle/input/indoor-scenes-cvpr-2019/TrainImages.txt', test_transform, "训练集")
test_dataset = MyData('/kaggle/input/indoor-scenes-cvpr-2019/TestImages.txt', test_transform, "测试集")


train_loader = DataLoader(train_dataset, opt.batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, opt.batch_size, shuffle=True)

train_loader.__len__(), test_loader.__len__()

可视化一批训练数据

plt.figure(figsize=(16,4))
for data in train_loader:
    print(data[1])
    for i in range(data[0].shape[0]):
#         print(data[0][i].shape)
        plt.subplot(4, 16, i+1)
        plt.imshow(data[0][i].reshape((3,opt.image_size,opt.image_size))[0, :, :], cmap=plt.cm.gray)
        plt.title(data[1][i].item(), size=9)
        plt.axis("off")
        plt.subplots_adjust(wspace=0.5)
    break
data[0][0].shape

 构建模型

'''Resnet50'''

class Bottleneck(nn.Module):
    expansion = 4

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(Bottleneck, self).__init__()
        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
                               padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)
        self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, bias=False)
        self.bn3 = nn.BatchNorm2d(planes * self.expansion)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)

        if self.downsample is not None:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)
        return out

class ResNet(nn.Module):

    def __init__(self, block, layers, num_classes=1000):
        self.inplanes = 64
        super(ResNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,
                               bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
        self.avgpool = nn.AvgPool2d(kernel_size=7, stride=1, padding=0)
        self.fc = nn.Linear(512 * block.expansion, num_classes)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

    def _make_layer(self, block, planes, blocks, stride=1):
        downsample = None
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
                nn.Conv2d(self.inplanes, planes * block.expansion,
                          kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(planes * block.expansion),
            )

        layers = []
        layers.append(block(self.inplanes, planes, stride, downsample))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            layers.append(block(self.inplanes, planes))

        return nn.Sequential(*layers)

    def forward(self, x):
        record = dict()
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        record["maxpool"] = x
        x = self.maxpool(x)
        x = self.layer1(x)
        record["layer1"] = x
        x = self.layer2(x)
        record["layer2"] = x
        x = self.layer3(x)
        record["layer3"] = x
        x = self.layer4(x)
        record["layer4"] = x
        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        record["avgpool"] = x
        x = self.fc(x)
        return x


def resnet50(pretrained=False, **kwargs):
    """Constructs a ResNet-50 model.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
    """
    model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)
    return model

# 测试
model = resnet50(False, num_classes=67)
input = torch.ones((1,3,224,224))
output = model(input)
print(output)
'''AlexNet模型代码'''
class  AlexNet(nn.Module):
    def __init__(self, num_classes, times=7):
        super(AlexNet, self).__init__()
        #  卷积层
        self.conv = nn.Sequential(
            nn.Conv2d(3, 96, 11, 1),
            nn.BatchNorm2d(96),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(3, 2),
            
            # 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,
            # 且增大输出通道数
            nn.Conv2d(96, 256, 5, 1, 2),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(3, 2),
            
            # 连续3个卷积层,且使用更小的卷积窗口。除了最后的卷积层外,
            # 进一步增大了输出通道数。
            # 前两个卷积层后不使用池化层来减小输入的高和宽
            nn.Conv2d(256, 384, 3, 1, 1),
            nn.BatchNorm2d(384),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 384, 3, 1, 1),
            nn.BatchNorm2d(384),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, 3, 1, 1),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(3, 2)
        )
        
         # 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合
        self.fc = nn.Sequential(
            nn.Linear(256*(4*times-3)*(4*times-3), 2048),
            nn.ReLU(inplace=True),
            nn.Dropout(0.5),
            
            nn.Linear(2048, 256),
            nn.ReLU(inplace=True),
            nn.Dropout(0.5),
            
            nn.Linear(256, num_classes)  # 自定义的类别数
        )
        
    def forward(self, img):
        output = self.conv(img)
        # print(output.shape)  # 查看尺寸大小
        
        # 在第一个全连接层与卷积层连接的位置,需要将特征图拉成一个一维向量
        output = output.view(img.size(0), -1)
        output = self.fc(output)
        # 经过softmax处理转为概率输出
        return output
    
    
if __name__ == "__main__":
    inputs = torch.ones((1,3,224, 224))
    net = AlexNet(67)
    output = net(inputs)
    print(output)
'''LeNet5模型代码'''
class LeNet5(nn.Module):
    def __init__(self, num_classes, times=7):
        super(LeNet5, self).__init__()        
        self.convnet = nn.Sequential(
            # 输入大小为3*32*32,输出大小为28*28,输入通道为1,输出为6,卷积核为5
            nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5),
            nn.BatchNorm2d(6),
            # 使用ReLU激活函数
            nn.ReLU(inplace=True),

            # 使用平均池化层
            nn.AvgPool2d(kernel_size=2, stride=2),
            
            nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5),
            nn.BatchNorm2d(16),
            nn.ReLU(inplace=True),
            nn.AvgPool2d(kernel_size=2, stride=2),
            
            nn.Conv2d(in_channels=16, out_channels=120, kernel_size=5),
            nn.BatchNorm2d(120),
            nn.ReLU(inplace=True),
        )
        
        self.fc = nn.Sequential(
            nn.Linear(120*(8*(times-1)+1)*(8*(times-1)+1), 256),  # ***因为要修改输入图像的尺寸***
            nn.ReLU(inplace=True),
            nn.Dropout(0.5),
            nn.Linear(256, num_classes)
        )
        
    
    # 定义网络的前向运算
    def forward(self, img):
        output = self.convnet(img)
#         print(output.shape)  # 查看尺寸大小
        # 在第一个全连接层与卷积层连接的位置
        # 需要将特征图拉成一个一维向量
        output = output.view(img.size(0), -1)
        output = self.fc(output)
        return output

if __name__ == "__main__":
    inputs = torch.ones((1,3,224, 224))
    net = LeNet5(67)
    output = net(inputs)
    print(output, inputs.shape)

搭建训练函数

"""
    train() 训练模型
        *model_name: 需要训练模型名称
        *model: 需要训练的模型
        *optimizer: 优化器
        *criterion: 损失函数
        *train_loader: 训练数据集
        *epoches: 训练轮数
        
        return: 每轮的loss, acc列表
"""
def train(model_name, model, train_loader, optimizer='Adam', epochs=3):
    # optimizer优化器
    if optimizer=="Adam":
        optimizer = optim.Adam(model.parameters(), lr=opt.lr)
    elif optimizer=="SGD":
        optimizer = optim.SGD(model.parameters(), lr=opt.lr)
        
    
    # criterion损失函数
    criterion = nn.CrossEntropyLoss()
    
    train_loss = []
    train_acc = []
    best_acc = 0.0

    print("--------------------------{}-----------------------------".format(model_name))
    for epoch in tqdm(range(epochs)):
        epoch_loss = 0.0        # 记录每一轮的总损失
        epoch_accuracy_num = 0  # 记录每一轮正确预测的样本数
        epoch_sample_num = 0    # 记录每一轮的样本数
        step = 0                # 记录批次步骤
        # 训练步骤开始
        model.train()
        for imgs, targets in train_loader:
            step += 1
            if step%10==0: print('.', end='')
                
            optimizer.zero_grad()                           # 清空历史梯度
            outputs = model(imgs.to(device))                # 输入数据
            loss = criterion(outputs, targets.to(device))   # 计算损失值
            loss.backward()                                 # 神经网络反向传播
            optimizer.step()

            epoch_loss += loss.item()     # 累加损失值
            outputs_ = outputs.argmax(1)  # 输出转为最大概率的下标
            epoch_accuracy_num += (targets.to(device) == outputs_).sum()  # 累加该批次正确预测的个数
            epoch_sample_num += targets.size(0)

        epoch_acc = epoch_accuracy_num/epoch_sample_num
        train_loss.append(epoch_loss)
        train_acc.append(epoch_acc)
        
        flag = str(epoch+1) if epoch >= 9 else '0' + str(epoch+1)
#         print("第{}轮结果:训练集上:loss:{} —— acc:{} ".format(flag, '%.5f'%epoch_loss, '%.5f'%epoch_acc), end='')


        if epoch_acc > best_acc:
            best_acc = epoch_acc
            torch.save(model.state_dict(), "/kaggle/working/{}_best.pth".format(model_name))
            print("第{}轮结果:训练集上:loss:{} —— acc:{}   new best!!!".format(flag, '%.5f'%epoch_loss, '%.5f'%epoch_acc))
        else:
            print("第{}轮结果:训练集上:loss:{} —— acc:{}".format(flag, '%.5f'%epoch_loss, '%.5f'%epoch_acc))

            
        
    torch.save(model.state_dict(), "/kaggle/working/{}_last.pth".format(model_name))
    print("保存成功!!!")
    
    return train_loss, train_acc

搭建测试函数

"""
    test()函数
        *model_name: 测试的模型名称
        *model: 测试的模型
        *test_loader: 测试数据集 
        *criterion: 损失函数
        
        return: 返回测试的loss、acc
"""
def test(model_name, model, test_loader):
    # criterion损失函数
    criterion = nn.CrossEntropyLoss()
    
    best_parameter = torch.load("/kaggle/working/{}_best.pth".format(model_name))
    model.load_state_dict(best_parameter)
    
    test_loss = 0.0
    test_sample_num = 0
    test_accuracy_num = 0
    test_acc = 0.0

    # 测试步骤开始
    model.eval()
    with torch.no_grad():
        print("----------------{}测试开始------------------".format(model_name))
        test_bar = tqdm(test_loader)
        for imgs, targets in test_bar:
            outputs = model(imgs.to(device))
            loss = criterion(outputs, targets.to(device))
            test_loss += loss.item()

            outputs_ = outputs.argmax(1)
            test_accuracy_num += (outputs_ == targets.to(device)).sum()
            test_sample_num += targets.size(0)
            # break

    test_acc = test_accuracy_num/test_sample_num
    print("{}测试结果 loss:{} —— acc:{}".format(model_name, test_loss, test_acc))
    return test_loss, test_acc

实例化模型开始训练

opt.epoches = 10
opt.optimizer = "Adam"
opt.lr = 1e-4
opt.batch_size = 64

# 存储训练数据
nets_val_loss = []
nets_val_acc = []

# 实例化模型 列表
leNet5 = LeNet5(67).to(device)
alexNet = AlexNet(67).to(device)
resNet50 = resnet50(False, num_classes=67).to(device)

train_nets = [leNet5, alexNet, resNet50]
nets_name = ['leNet5', 'alexNet', 'resNet50']

print("参数明细:epoches-->{}, optimizer-->{}, criterion-->{}, lr-->{}, batchsize-->{}, img_size-->{}".format(
    opt.epoches, opt.optimizer, 'CrossEntropyLoss', opt.lr, opt.batch_size, opt.image_size))

for i in range(len(train_nets)):
    train_loss, train_acc = train(nets_name[i], train_nets[i], train_loader, optimizer=opt.optimizer, epochs=opt.epoches)
    nets_val_loss.append(train_loss)
    nets_val_acc.append(train_acc)

plt.rcParams['font.sans-serif']=['SimHei']   # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

def train_visualize(loss_data, acc_data, nets_name):
    plt.figure(figsize=(16,5))
    plt.subplot(1,2,1)
    for loss in loss_data:
        plt.plot(range(opt.epoches), loss)
        plt.title("Loss")
        plt.xlabel("epoches", fontsize=15)
        plt.ylabel("loss", fontsize=15)
    plt.legend(nets_name, fontsize=11)
        
    plt.subplot(1,2,2)
    for acc in acc_data:
        plt.plot(range(opt.epoches), acc)
        plt.title("accuracy")
        plt.xlabel("epochs", fontsize=15)
        plt.ylabel("accuracy", fontsize=15)
    plt.legend(nets_name, fontsize=11)


    
nets_val_acc_ = []
for i in nets_val_acc:
    acc = []
    for j in i:
        acc.append(j.cpu())
    nets_val_acc_.append(acc)

train_visualize(nets_val_loss, nets_val_acc_, nets_name)
# nets_val_loss, nets_val_acc

 

 

 开始测试

# 实例化模型 列表
leNet5_ = LeNet5(67).to(device)
alexNet_ = AlexNet(67).to(device)
resNet50_ = resnet50(False, num_classes=67).to(device)



# 存储测试数据
nets_test_loss = []
nets_test_acc = []

test_nets = [leNet5_, alexNet_, resNet50_]
nets_name = ['leNet5', 'alexNet', 'resNet50']
# 需要和训练时定义的相同,因为与文件路径直接关联

opt.is_lstm = 0
print("参数明细:epoches-->{}, optimizer-->{}, criterion-->{}, lr-->{}, batchsize-->{}, img_size-->{}".format(
    opt.epoches, opt.optimizer, 'CrossEntropyLoss', opt.lr, opt.batch_size, opt.image_size))
for i in range(len(test_nets)):
    if nets_name[i] == 'lstmNet':
        opt.is_lstm = 1
    test_loss, test_acc = test(nets_name[i], test_nets[i], test_loader)
    nets_test_loss.append(test_loss)
    nets_test_acc.append(test_acc)
    opt.is_lstm = 0

def test_visualize(loss_data, acc_data, nets_name):
    plt.figure(figsize=(16, 5))
    plt.subplot(1,2,1)
    plt.bar(nets_name, loss_data)    
    plt.title("test_loss", fontsize=20)
    plt.xlabel("model", fontsize=15) 
    plt.ylabel("loss", fontsize=20)
    for a,b in zip(nets_name, loss_data):   #柱子上的数字显示
        plt.text(a,b,'%.2f'%b, ha='center', fontsize=18)
    
    plt.subplot(1,2,2)
    plt.bar(nets_name, acc_data)
    plt.title("test_acc", fontsize=20)
    plt.xlabel("model", fontsize=15)
    plt.ylabel("acc", fontsize=20)
    for a,b in zip(nets_name, acc_data):   #柱子上的数字显示
        plt.text(a,b,'%.2f'%b, ha='center', fontsize=18)
    
    
nets_test_acc_ = []
for i in nets_test_acc:
    nets_test_acc_.append(i.cpu())
    
test_visualize(nets_test_loss, nets_test_acc_, nets_name)
# nets_test_loss, nets_test_acc

 

 

 结果:效果非常差,很不理想,可能是哪一步出错了,如果有懂的大佬给指正一下,谢谢!!

参考文献

Keras中那些学习率衰减策略_西檬饭的博客-CSDN博客_keras 学习率衰减

梯度消失与梯度爆炸产生原因及解决方法_frostjsy的博客-CSDN博客_梯度爆炸

 过拟合(出现的原因4种、解决方案6种)

Batch Size的理解_Altoria.的博客-CSDN博客_batch size

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深度学习图像分类实战——pytorch搭建卷积神经网络(AlexNet, LeNet, ResNet50)进行场景图像分类(详细) 的相关文章

  • 如何在 PyTorch 中的特定新维度中重复张量

    如果我有一个张量A有形状 M N 我想重复张量 K 次 以便结果B有形状 M K N 和每片B k 应该具有相同的数据A 这是没有 for 循环的最佳实践 K可能在其他维度 torch repeat interleave and tenso
  • torch.unique() 中的参数“dim”如何工作?

    我试图提取矩阵每一行中的唯一值并将它们返回到同一个矩阵中 重复值设置为 0 例如 我想转换 torch Tensor 1 2 3 4 3 3 4 1 6 3 5 3 5 4 to torch Tensor 1 2 3 4 0 0 0 1 6
  • Win10 64位上CUDA 12的PyTorch安装

    我需要在我的 PC 上安装 PyTorch 其 CUDA 版本 12 0 pytorch 2 的表 https i stack imgur com X13oS png in In 火炬网站 https pytorch org get sta
  • 无法使用 torch.Tensor 创建张量

    我试图创建一个张量 如下所示 import torch t torch tensor 2 3 我收到以下错误 类型错误回溯 最近调用 最后 在 gt 1 a torch tensor 2 3 类型错误 tensor 需要 1 个位置参数 但
  • 无法将 cuda:0 设备类型张量转换为 numpy。首先使用 Tensor.cpu() 将张量复制到主机内存

    我试图展示 GAN 网络在某些指定时期的结果 打印当前结果的功能之前是在 TF 中使用的 我需要换成pytorch def show result G net z num epoch show False save False path r
  • Pytorch 分析器显示两个不同网络的卷积平均执行时间不同

    我有两个网络 我正在对它们进行分析以查看哪些操作占用了大部分时间 我注意到CUDA time avg为了aten conv2d不同网络的操作有所不同 这也增加了一个数量级 在我的第一个网络中 它是22us 而对于第二个网络则是3ms 我的第
  • PyTorch - 参数不变

    为了了解 pytorch 的工作原理 我尝试对多元正态分布中的一些参数进行最大似然估计 然而 它似乎不适用于任何协方差相关的参数 所以我的问题是 为什么这段代码不起作用 import torch def make covariance ma
  • PoseWarping:如何矢量化此 for 循环(z 缓冲区)

    我正在尝试使用地面真实深度图 姿势信息和相机矩阵将帧从视图 1 扭曲到视图 2 我已经能够删除大部分 for 循环并将其矢量化 除了一个 for 循环 扭曲时 由于遮挡 视图 1 中的多个像素可能会映射到视图 2 中的单个位置 在这种情况下
  • 如何使用 torch.stack?

    我该如何使用torch stack将两个张量与形状堆叠a shape 2 3 4 and b shape 2 3 没有就地操作 堆叠需要相同数量的维度 一种方法是松开并堆叠 例如 a size 2 3 4 b size 2 3 b torc
  • pytorch通过易失性变量反向传播错误

    我试图通过多次向后传递迭代来运行它并在每个步骤更新输入 从而最小化相对于某个目标的一些输入 第一遍运行成功 但在第二遍时出现以下错误 RuntimeError element 0 of variables tuple is volatile
  • 使用 pytorch 获取可用 GPU 内存总量

    我正在使用 google colab 免费 Gpu 进行实验 并想知道有多少 GPU 内存可供使用 torch cuda memory allocated 返回当前占用的 GPU 内存 但我们如何使用 PyTorch 确定总可用内存 PyT
  • Model() 获得参数“nr_class”的多个值 - SpaCy 多分类模型(BERT 集成)

    您好 我正在致力于使用新的 SpaCy 模型实现多分类模型 5 类 en pytt bertbaseuncased lg 新管道的代码在这里 nlp spacy load en pytt bertbaseuncased lg textcat
  • torch.mm、torch.matmul 和 torch.mul 有什么区别?

    阅读完 pytorch 文档后 我仍然需要帮助来理解之间的区别torch mm torch matmul and torch mul 由于我不完全理解它们 所以我无法简明地解释这一点 B torch tensor 1 1207 0 3137
  • 下载变压器模型以供离线使用

    我有一个训练有素的 Transformer NER 模型 我想在未连接到互联网的机器上使用它 加载此类模型时 当前会将缓存文件下载到 cache 文件夹 要离线加载并运行模型 需要将 cache 文件夹中的文件复制到离线机器上 然而 这些文
  • PyTorch 中复数矩阵的行列式

    有没有办法在 PyTorch 中计算复矩阵的行列式 torch det未针对 ComplexFloat 实现 不幸的是 目前尚未实施 一种方法是实现您自己的版本或简单地使用np linalg det 这是一个简短的函数 它计算我使用 LU
  • 如何计算 CNN 第一个线性层的维度

    目前 我正在使用 CNN 其中附加了一个完全连接的层 并且我正在使用尺寸为 32x32 的 3 通道图像 我想知道是否有一个一致的公式可以用来计算第一个线性层的输入尺寸和最后一个卷积 最大池层的输入 我希望能够计算第一个线性层的尺寸 仅给出
  • 将 Keras (Tensorflow) 卷积神经网络转换为 PyTorch 卷积网络?

    Keras 和 PyTorch 使用不同的参数进行填充 Keras 需要输入字符串 而 PyTorch 使用数字 有什么区别 如何将一个转换为另一个 哪些代码在任一框架中获得相同的结果 PyTorch 还采用参数 in channels o
  • 如何有效地对一个数组中某个值在另一个数组中的位置出现的次数求和

    我正在寻找一种有效的 for 循环 避免解决方案来解决我遇到的数组相关问题 我想使用一个巨大的一维数组 A gt size 250 000 用于一维索引的 0 到 40 之间的值 以及用于第二维索引的具有 0 到 9995 之间的值的相同大
  • 如何使用Python计算多类分割任务的dice系数?

    我想知道如何计算多类分割的骰子系数 这是计算二元分割任务的骰子系数的脚本 如何循环每个类并计算每个类的骰子 先感谢您 import numpy def dice coeff im1 im2 empty score 1 0 im1 numpy
  • ValueError:使用火炬张量时需要解压的值太多

    对于神经网络项目 我使用 Pytorch 并使用 EMNIST 数据集 已经给出的代码加载到数据集中 train dataset dsets MNIST root data train True transform transforms T

随机推荐

  • Hadoop中HDFS的读写流程详解

    一 HDFS写流程示意图 xff1a xff08 1 xff09 客户端通过Distributed FileSystem模块向NameNode请求上传文件 xff0c NameNode检查目标文件是否已存在 xff0c 父目录是否存在 检查
  • 基于树莓派(python)的平衡小车

    目前普遍是STM32或者51来实现平衡小车 xff0c 基于树莓派的平衡小车少见 xff08 因为树莓派适合用于数据处理 xff09 但有部分同学的毕设选择了用树莓派来做平衡小车的 xff0c 接下来分享一下基于树莓派的平衡小车如何完成 驱
  • BGP双平面架构

    要求 xff1a PC1 PC3 PC5为电信面路由 xff0c PC2 PC4 PC6为联通面路由 正常情况下 xff0c 电信面路由走AS1 xff0c 联通面路由走AS2 xff0c 当R9与R1之间的线路发生故障时 xff0c 电信
  • 宿主机可以正常上网,虚拟机不能上网的问题

    宿主机可以正常上网 xff0c 虚拟机不能上网的问题 问题描述 宿主机 xff1a Windows 10 虚拟机 xff1a VMware Ubuntu 14 04 6 LTS 之前配置过一个主节点 xff0c 三个从节点 宿主机 xff0
  • Jlink调试2440进不了中断的看这里

    很多人学习2440都是从裸机开始的 刚开始的小菜LED xff0c 然后定时器 按键这些常用内部外设 xff0c 最后则是LCD Nand等复杂外设 用Jlink裸机调试2440小程序时 xff0c 中断的调试是一个重要内容 像定时器 按键
  • 在Eclipse中运行第一个MapReduce程序

    这是Hadoop学习全程记录第2篇 xff0c 在这篇里我将介绍一下如何在Eclipse下写第一个MapReduce程序 新说明一下我的开发环境 xff1a 操作系统 xff1a 在windows下使用wubi安装了ubuntu 10 10
  • 论文笔记:Learning Deep Features for Discriminative Localization

    一 这篇论文解决什么问题 原始问题 xff1a Weakly supervised object localization xff0c 研究发现 xff0c 图像分类任务上训练的CNN xff0c 可以直接用于物体定位 两个子问题 xff1
  • CSS高度塌陷问题-六种解决方案

    问题背景 子元素浮动后 xff0c 无法撑起父元素高度 xff0c 导致父元素高度丢失 xff0c 其下元素会自动上移 xff0c 导致页面布局混乱 解决方案 一 BFC 块级格式化环境 xff08 Block Formatting Con
  • MySQL设置更改root密码

    MySQL设置更改root密码 方法一 xff1a 通过MySQL console直接设置密码 首先打开MySQL console 输入原始密码后按回车键 xff08 如果没有原始密码 xff0c enter password后面为空 xf
  • Unity2D—利用UGUI纯代码实现简易摇杆

    目标 xff1a 拖动摇杆实现平面物体的移动 一 制作摇杆UI 平面物体 xff08 1 xff09 新建画布Canvas xff0c 将Canvas的UI缩放模式设置为屏幕大小播放 xff08 如不设为该模式则会因界面缩放而导致UI消失
  • Unity2D—骨骼绑定、IK系统、动画(一)

    目标 xff1a 使用素材实现特定的2D人物动画 一 导入包资源和素材 xff08 本人的Unity版本为2021 1 7 xff09 xff08 1 xff09 Unity注册表中 xff1a 2D Animation 2D PSD Im
  • Unity2D—骨骼绑定、IK系统、动画(二)

    目标 xff1a 使用素材实现特定的2D人物动画 三 IK系统简介 小知识 xff1a IK动画全名是Inverse Kinematics 意思是反向动力学 xff0c 子骨骼节点带动父骨骼节点运动 比如跳街舞的少年用手撑着身体在地上转圈
  • 手把手教你做计算机网络基础大题—报文交换

    一 题目 如图所示 xff0c 主机A要向主机B发送一个长度为300KB的报文 xff0c 发送速率为10Mbps xff0c 传输路径上要经过8个路由器 xff0c 连接路由器的链路长度为100km xff0c 信号在链路上的传播速度为2
  • 手把手教你做计算机网络基础大题—TCP头部结构

    一 题目 已知TCP头部用十六进制数表示为 xff1a 05320017 00000001 00000055 500207FF 00000000 请回答以下问题 xff1a xff08 1 xff09 源端口号是多少 xff1f xff08
  • 雅思大作文写作模版

    模版整理自Vince9120老师 xff0c 逻辑清晰 xff0c 对于写作遇到瓶颈的同学有帮助 xff0c 而且易上手使用 Vince老师视频在Bilibili xff0c 备考时间充足的同学建议观看学习 xff01 一 大作文段落详情及
  • 手把手教你做计算机网络基础大题—ARQ协议

    一 题目 假定使用连续ARQ协议 xff0c 发送窗口大小是3 xff0c 而序号范围是 0 xff0c 15 xff0c 而传输媒体保证在接收方能够按序收到分组 在某一时刻 xff0c 在接收方 xff0c 下一个期望收到的序号是5 试问
  • ARM开发中ubuntu设置成静态IP的优劣分析

    ARM linux开发中 xff0c 我们经常将主机ubuntu设置成静态IP 这有一些好处 xff0c 譬如你的uboot的环境变量中serverip就不用每次改来改去的了 我一直也是这么做的 xff0c 但是今天突然发现这样会带来很多问
  • 手把手教你做计算机网络基础大题—路由表

    一 题目 设某路由器建立了如下路由表 xff1a 目的网络子网掩码下一跳128 96 39 0255 255 255 128接口m0128 96 39 128255 255 255 128接口m1128 96 40 0255 255 255
  • 新手入门:ST-Link和J-Link仿真器的使用

    当编译完成之后 xff0c 点击下载 xff0c 出现这样的错误提示 xff0c 说明我们的仿真器配置没有配置好 xff0c 下面我们讲讲J Link和ST Link分别应该如何配置 xff08 1 xff1a 编译 xff0c 后续只编译
  • 深度学习图像分类实战——pytorch搭建卷积神经网络(AlexNet, LeNet, ResNet50)进行场景图像分类(详细)

    目录 1 一 实验过程 1 1 实验目的 1 2 实验简介 1 3 数据集的介绍 1 4 一 LeNet5网络模型 1 5 二 AlexNet网络模型 1 6 三 ResNet50 xff08 残差网络 xff09 网络模型 二 实验代码