基于CNN的花卉识别系统

 录

第一章  绪论......................................................................................................... 1

1.1  设计背景................................................................................................ 1

1.2  设计依据................................................................................................ 1

1.3 设计的主要内容和功能........................................................................ 1

第二章   设计方案..................................................................................... 2

2.1 设计思路................................................................................................ 2

2.2 系统构成................................................................................................ 2

第三章  系统设计与实现................................................................................... 3

3.1 系统设计................................................................................................ 2

3.2 数据集选取............................................................................................ 2

3.3 模型选择................................................................................................ 2

3.4 模型评估与优化.................................................................................... 2

第四章 系统测试................................................................................................. 8

第五章 总结....................................................................................................... 12

题  

第一章     绪论  

1.1 课题背景

VGG模型的深层结构: VGG模型以其简单而有效的网络结构而闻名,采用多个小尺寸的卷积核和深层次的网络结构,有助于学习图像的复杂特征。在花卉识别中,VGG可以用于提取花卉图像的抽象特征。

AlexNet的先进性: AlexNet在2012年的ImageNet大规模视觉识别挑战赛中取得了胜利,证明了深度学习在图像识别中的优越性。在花卉识别系统中,AlexNet的应用可以带来更好的性能和更高的准确性。

ResNet的残差学习: ResNet引入了残差学习的概念,通过跳跃连接解决了深度神经网络训练过程中的梯度消失和梯度爆炸问题。在花卉识别系统中,ResNet的使用可以有效地训练深层网络,提高模型的泛化能力。

数据集的重要性: 为了训练和评估花卉识别系统,需要使用包含各种花卉图像的大规模数据集。常用的数据集包括Oxford Flower-17和Flower-102等,它们包含了不同种类的花卉图像,有助于模型学习更丰富的特征。

应用前景: 花卉识别系统的成功应用可以拓展到农业、园艺和生态学等领域,帮助自动化植物识别、监测和分类,提高工作效率和精度。

1.2 问题分析 

在实现该次实验过程中,数据集的质量显得尤为重要,不充分或者不代表性数据可能会导致模型过于拟合,或者泛化能力不足,因此评估选定数据集的覆盖范围,图像质量,标签准确性等因素,需要进行数据增强选择更加全面的数据集,

同时需要在实验过程中选择VGG。Alexnet,ResNet模型需要认识到他们呢在花卉识别任务上的性能的比较和评估。进行实验,比较不同模型的训练速度,准确性和泛化能力。考虑模型复杂性和计算资源的需求。研究模型的解释性能,选择适当的解释性技术,以增强系统的可理解性

1.3 课题的主要内容

1.文献综述:

目标: 对与花卉识别相关的文献进行全面综述,了解当前深度学习在图像识别领域的发展趋势,以及先前对花卉识别系统的研究成果。

内容: 包括深度学习模型的演变、花卉识别系统的应用、常用的数据集、性能评估指标等。

2.数据集收集与预处理:

目标: 获取并准备用于训练和测试的花卉图像数据集,确保数据集的多样性和代表性。

内容: 数据集的采集、清理、标注,以及图像预处理的步骤,包括大小调整、标准化等。

3.模型选择与设计:

目标: 选择合适的深度学习模型,并根据问题的需求进行定制或微调。

内容: 对VGG、AlexNet和ResNet等模型的选择,可能进行模型融合或者设计新的网络结构以适应花卉识别任务。

4.模型训练与调优:

目标: 在选定的数据集上训练深度学习模型,并对模型进行调优,以提高性能。

内容: 包括超参数调整、训练集和验证集的划分、正则化技术的应用等。

5.性能评估与比较:

目标: 评估设计的花卉识别系统的性能,并与其他模型进行比较。

内容: 使用准确性、召回率、F1分数等指标对系统进行评估,并可视化模型的性能差异。

6.解释性分析:

目标: 对模型进行解释性分析,以理解模型对花卉图像进行分类的原因。

内容: 可能包括可解释性技术的应用,如梯度热力图、类激活映射等。

7.结果和讨论:

目标: 对整个研究过程的结果进行总结,讨论模型的优劣势,提出可能的改进方向。

内容: 性能指标的总结、模型的局限性和未来工作的展望等。

8.撰写论文和报告:

目标: 将整个研究过程以论文或报告的形式呈现,清晰地传达研究的目的、方法、结果和结论。

内容: 撰写学术论文,包括引言、相关工作、方法、实验结果、讨论和结论等部分。

第二章     相关技术及原理

2.1 相关技术概述   

1卷积神经网络 (CNN)

VGGAlexNetResNet特点: 这三个模型都是卷积神经网络的代表。它们使用卷积层来有效地捕捉图像的局部特征,池化层用于下采样,全连接层用于综合高层次特征。

应用: 在花卉识别系统中,CNN用于提取图像中的特征,帮助模型理解花卉的形状、颜色等信息。

2迁移学习:

VGGAlexNetResNet特点: 这些模型在大规模图像分类任务上进行了预训练,因此可以通过迁移学习来加速在花卉识别任务上的训练。

应用: 将预训练模型的权重加载到花卉识别模型中,并对模型进行微调,以适应花卉数据集的特定特征。

3数据增强:

VGGAlexNetResNet特点: 数据增强是一种通过对训练数据进行随机变换来扩充数据集的技术。这有助于提高模型的泛化能力。

应用: 在花卉识别系统中,可以应用数据增强技术,如旋转、翻转、缩放等,以生成更多多样性的训练样本。

3超参数调优:

VGGAlexNetResNet特点: 模型的性能很大程度上依赖于超参数的选择,如学习率、批量大小等。

应用: 通过交叉验证等技术,对超参数进行系统调优,以获得最佳的模型性能。

4.硬件加速:

VGGAlexNetResNet特点: 这些深度学习模型的训练和推理可能需要大量计算资源,因此硬件加速可以提高系统性能。

应用: 使用GPUTPU等硬件来加速模型的训练和推理过程,特别是对于实时性能要求较高的花卉识别系统。

1. VGG (Visual Geometry Group) 原理:

结构特点:

深度结构: VGG是一个由1619层卷积层组成的深度网络,主要由卷积层和池化层交替构成。

小卷积核: VGG采用小尺寸的3x3卷积核,多次堆叠,使得网络能够学习更复杂的特征。

全连接层: 在卷积层之后是全连接层,最终通过softmax激活函数进行多类别分类。

原理:

VGG通过多个卷积层逐渐提取图像的高级特征,池化层用于减小特征图的尺寸,全连接层用于最终的分类。

2. AlexNet 原理:

结构特点:

卷积层叠加: AlexNet包含5个卷积层,其中某些层后跟随最大池化层。

局部响应归一化 (LRN) 在卷积层之间使用LRN层,以提高模型的泛化能力。

Dropout 引入了Dropout层,用于在训练过程中随机丢弃一些神经元,防止过拟合。

GPU训练: AlexNet是早期采用多GPU进行训练的先驱。

原理:

AlexNet通过多层卷积提取图像特征,使用全连接层进行最终分类。LRN层和Dropout层有助于提高模型的泛化性能。

3. ResNet (Residual Network) 原理:

结构特点:

残差块: ResNet引入了残差学习的概念,通过残差块允许网络学习残差(实际输出与期望输出之差),从而避免梯度消失问题。

跳跃连接: 每个残差块包含跳跃连接,将输入添加到输出,使得信息能够更轻松地传播。

全局平均池化: 在最后一个残差块之后使用全局平均池化,减少参数数量,防止过拟合。

原理:

ResNet通过残差块实现深层网络的训练。跳跃连接和残差学习有助于解决深度神经网络中的梯度消失问题,使得网络可以更轻松地训练。

这三种模型在图像分类任务中都取得了显著的成功,各自的设计原理反映了对深度学习中一些关键问题的解决思路,如层次深度、卷积核尺寸、跳跃连接等。

2.2 核心技术原理   

1. VGG (Visual Geometry Group) 原理:

结构特点:

深度结构: VGG是一个由1619层卷积层组成的深度网络,主要由卷积层和池化层交替构成。

小卷积核: VGG采用小尺寸的3x3卷积核,多次堆叠,使得网络能够学习更复杂的特征。

全连接层: 在卷积层之后是全连接层,最终通过softmax激活函数进行多类别分类。

原理:

VGG通过多个卷积层逐渐提取图像的高级特征,池化层用于减小特征图的尺寸,全连接层用于最终的分类。

2. AlexNet 原理:

结构特点:

卷积层叠加: AlexNet包含5个卷积层,其中某些层后跟随最大池化层。

局部响应归一化 (LRN) 在卷积层之间使用LRN层,以提高模型的泛化能力。

Dropout 引入了Dropout层,用于在训练过程中随机丢弃一些神经元,防止过拟合。

GPU训练: AlexNet是早期采用多GPU进行训练的先驱。

原理:

AlexNet通过多层卷积提取图像特征,使用全连接层进行最终分类。LRN层和Dropout层有助于提高模型的泛化性能。

3. ResNet (Residual Network) 原理:

结构特点:

残差块: ResNet引入了残差学习的概念,通过残差块允许网络学习残差(实际输出与期望输出之差),从而避免梯度消失问题。

跳跃连接: 每个残差块包含跳跃连接,将输入添加到输出,使得信息能够更轻松地传播。

全局平均池化: 在最后一个残差块之后使用全局平均池化,减少参数数量,防止过拟合。

原理:

ResNet通过残差块实现深层网络的训练。跳跃连接和残差学习有助于解决深度神经网络中的梯度消失问题,使得网络可以更轻松地训练。

这三种模型在图像分类任务中都取得了显著的成功,各自的设计原理反映了对深度学习中一些关键问题的解决思路,如层次深度、卷积核尺寸、跳跃连接等。

第三章  模型构建与评估

3.1 数据集选取与预处理

数据集选取csdn上源码

import os
import json

import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt

from model import AlexNet


def main():
    device = torch.device(
"cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose(
#图片预处理
       
[transforms.Resize((224, 224)),
        
transforms.ToTensor(),
        
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

   
# load image
   
img_path = "3.jpg"#python库中载入图片
   
assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)#判断图片是否存在
   
img = Image.open(img_path)

    plt.imshow(img)
#展示图片
   
# [N, C, H, W]
   
img = data_transform(img)#对图片进行预处理操作
   
# expand batch dimension  #batch是指一次处理图片的数量,批
   
img = torch.unsqueeze(img, dim=0)#处理后变成[batch, C, H, W]

    # read class_indict
   
json_path = './class_indices.json'#读取索引对应的类别名称
   
assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)#json文件解码成字典模式

   
json_file = open(json_path, "r")
    class_indict = json.load(json_file)

   
# create model
   
model = AlexNet(num_classes=5).to(device)#初始化网络,类别为5

    # load model weights
   
weights_path = "./AlexNet.pth"
   
assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path))
#载入网络模型

   
model.eval()#进入eval模式,即关闭dropout方法
   
with torch.no_grad():#让变量不去跟踪模型的损失梯度
       
# predict class
       
output = torch.squeeze(model(img.to(device))).cpu()#通过正向传播得到输出,并将输出进行压缩,将batch维度压缩
       
predict = torch.softmax(output, dim=0)#通过softmax处理之后变成概率分布
       
predict_cla = torch.argmax(predict).numpy()#获取概率最大处对应的索引值

   
print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],
                                                
predict[predict_cla].numpy())#打印类别名称以及预测正确的概率
   
plt.title(print_res)
   
print(print_res)
    plt.show()



if __name__ == '__main__':
    main()

运行结果会将数据集分为train和val两个数据集,均有五种花daisy,dandelion,roses,sunflowers,tulips

3.2 模型选择与构建

模型选择分为三部分:

  1. Alexnet模型:
  2. # -*- codeing = utf-8 -*- # @Time : 2023/12/11 19:19 # @Author : # @File : model.py.py # @software : PyCharm import torch.nn as nn import torch class AlexNet(nn.Module):     #ALEXNET继承nn.module这个父类     def __init__(self, num_classes=1000, init_weights=False):         #通过初始化函数,定义网络在正向传播过程中需要使用的层结构         #num_classes是指输出的图片种类个数,init_weights=False意味着不定义模型中的初始权重         super(AlexNet, self).__init__()         #nn.Sequential模块,可以将一系列的层结构进行打包,组合成一个新的结构,         # 将专门用于提取图像特征的结构的名称取为features         self.features = nn.Sequential(             nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),             # input[3, 224, 224]  output[48, 55, 55]             #第一个卷积层,彩色图片深度为3,卷积核个数位48,卷积核大小11,步长4padding2             nn.ReLU(inplace=True),             #使用Relu激活函数时要将设置inplace=True             #使用relu激活函数,f(x)=max(0,x),             #相比sigmod函数与tanh函数有以下几个优点             # 1)克服梯度消失的问题             # 2)加快训练速度             # 注:正因为克服了梯度消失问题,训练才会快             # 缺点:             # 1)输入负数,则完全不激活,ReLU函数死掉。             # 2ReLU函数输出要么是0,要么是正数,也就是ReLU函数不是以0为中心的函数             nn.MaxPool2d(kernel_size=3, stride=2),                  # output[48, 27, 27]             nn.Conv2d(48, 128, kernel_size=5, padding=2),#步长默认为1,当步长为1时不用设置# output[128, 27, 27]             nn.ReLU(inplace=True),             nn.MaxPool2d(kernel_size=3, stride=2),                  # output[128, 13, 13]             nn.Conv2d(128, 192, kernel_size=3, padding=1),          # output[192, 13, 13]             nn.ReLU(inplace=True),             nn.Conv2d(192, 192, kernel_size=3, padding=1),          # output[192, 13, 13]             nn.ReLU(inplace=True),             nn.Conv2d(192, 128, kernel_size=3, padding=1),          # output[128, 13, 13]             nn.ReLU(inplace=True),             nn.MaxPool2d(kernel_size=3, stride=2),                  # output[128, 6, 6]         )         #将三个全连接层打包成一个新的模块,分类器         self.classifier = nn.Sequential(             nn.Dropout(p=0.5),#随机将一半的节点失活,默认为0.5             nn.Linear(128 * 6 * 6, 2048),#将特征矩阵展平,128*6*6最后输出的长**高,2048为全连接层节点个数             nn.ReLU(inplace=True),#Relu激活函数             nn.Dropout(p=0.5),             nn.Linear(2048, 2048),#全连接层2的输入为全连接层1的输出2048,全连接层2的节点个数2048             nn.ReLU(inplace=True),             nn.Linear(2048, num_classes),#全连接层3的输入为全连接层2的输出2048             #全连接层最后的输出就是图片类别的个数         )         if init_weights:             self._initialize_weights()         #是否初始化权重,如果初始化函数中的init_weights=Ture,就会进入到初始化权重的函数     def forward(self, x):         #forward正向传播过程,x为输入的变量         x = self.features(x)         #将训练样本输入features         x = torch.flatten(x, start_dim=1)         #将输入的变量进行展平从深度高度宽度三个维度进行展开,索引从1开始,展成一个一维向量         x = self.classifier(x)         #将展平后的数据输入到分类器中(三个全连接层组成的)         return x#最后的输出为图片类别     #初始化权重的函数     def _initialize_weights(self):         for m in self.modules():#遍历self.modules这样一个模块,该模块继承自它的父类nn.module,该模块会迭代每一个层次             if isinstance(m, nn.Conv2d):#如果该层次是一个卷积层,就会使用kaiming_normal_这样一个方法初始化权重                 nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')                 if m.bias is not None:                     nn.init.constant_(m.bias, 0)#如果偏值不为空的话,就用0对权重进行初始化             elif isinstance(m, nn.Linear):#如果该层次是一个全连接层,就用normal进行初始化                 nn.init.normal_(m.weight, 0, 0.01)#正态分布对权重进行赋值,均值为0,方差为0.01                 nn.init.constant_(m.bias, 0)#设置全连接层的偏值为0 class MyAlexNet:     pass

2.VGG16:

# -*- codeing = utf-8 -*-

# @Time : 2023/12/11 20:29

# @Author :

# @File : model.py

# @software : PyCharm

import torch.nn as nn

import torch



# official pretrain weights

model_urls = {

    'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth',

    'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth',

    'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth',

    'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth'

}





class VGG(nn.Module):

    def __init__(self, features, num_classes=1000, init_weights=False):

        #  net = vgg(model_name=model_name, num_classes=5, init_weights=True)

        super(VGG, self).__init__()

        self.features = features  # make_features函数确定,处理特征值的方法

        #  features确定卷积和池化的操作顺序,.classifier 确定全连接分类的参数

        self.classifier = nn.Sequential(

            # 卷积后的结果是512x7x7,输入512*7*7xbatch_size,输出4096xbatch_size

            # 一个有序的容器,神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行,

            # 同时以神经网络模块为元素的有序字典也可以作为传入参数。

            nn.Linear(512 * 7 * 7, 4096)# 全连接层fc,矩阵展平

            nn.ReLU(True),

            nn.Dropout(p=0.5)# 防止过拟合

            nn.Linear(4096, 4096),

            nn.ReLU(True),

            nn.Dropout(p=0.5),

            nn.Linear(4096, num_classes)

        )

        if init_weights:

            self._initialize_weights()



    # 前向传播

    def forward(self, x):

        # N x 3 x 224 x 224

        x = self.features(x)  # 卷积提取特征

        # N x 512 x 7 x 7

        x = torch.flatten(x, start_dim=1# 展平

        # N x 512*7*7

        x = self.classifier(x)  # 全连接分类

        return x



    def _initialize_weights(self):  # 初始化各层权重

        for m in self.modules():

            if isinstance(m, nn.Conv2d):  # 判断是否相同

                # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')

                nn.init.xavier_uniform_(m.weight)  # 初始化权重

                if m.bias is not None:

                    nn.init.constant_(m.bias, 0)

            elif isinstance(m, nn.Linear):

                nn.init.xavier_uniform_(m.weight)

                # nn.init.normal_(m.weight, 0, 0.01)

                nn.init.constant_(m.bias, 0)





# 确定每一层是卷积层还是池化,及其参数(卷积核大小,步长等)

def make_features(cfg: list):

    layers = []

    in_channels = 3

    for v in cfg:  # 遍历层表

        if v == "M":

            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]

        else:

            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)

            # nn.Conv2d(in_channels=3,out_channels=64,kernel_size=4,stride=2,padding=1)

            layers += [conv2d, nn.ReLU(True)]

            in_channels = v

    return nn.Sequential(*layers)





cfgs = {  # M代表池化,数字代表卷积出的个数

    'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],

    'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],

    'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],

    'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],

}





# 确定运行的是哪一个版本的vgg

def vgg(model_name="vgg16", **kwargs):

    assert model_name in cfgs, "Warning: model number {} not in cfgs dict!".format(model_name)

    cfg = cfgs[model_name]  # 确定运行的是哪一个版本的vgg



    model = VGG(make_features(cfg), **kwargs)

    # cfg经过make_features后,已经确定每一层的分布

    return model
  1. ResNet:
  2. # -*- codeing = utf-8 -*- # @Time : 2023/12/11 20:57 # @Author : # @File : model.py.py # @software : PyCharm import torch.nn as nn import torch class BasicBlock(nn.Module):     expansion = 1     def __init__(self, in_channel, out_channel, stride=1, downsample=None, **kwargs):         super(BasicBlock, self).__init__()         self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,                                kernel_size=3, stride=stride, padding=1, bias=False)         self.bn1 = nn.BatchNorm2d(out_channel)         self.relu = nn.ReLU()         self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel,                                kernel_size=3, stride=1, padding=1, bias=False)         self.bn2 = nn.BatchNorm2d(out_channel)         self.downsample = downsample     def forward(self, x):         identity = x         if self.downsample is not None:             identity = self.downsample(x)         out = self.conv1(x)         out = self.bn1(out)         out = self.relu(out)         out = self.conv2(out)         out = self.bn2(out)         out += identity         out = self.relu(out)         return out class Bottleneck(nn.Module):     """     注意:原论文中,在虚线残差结构的主分支上,第一个1x1卷积层的步距是2,第二个3x3卷积层步距是1。     但在pytorch官方实现过程中是第一个1x1卷积层的步距是1,第二个3x3卷积层步距是2,     这么做的好处是能够在top1上提升大概0.5%的准确率。     可参考Resnet v1.5 https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch     """     expansion = 4     def __init__(self, in_channel, out_channel, stride=1, downsample=None,                  groups=1, width_per_group=64):         super(Bottleneck, self).__init__()         width = int(out_channel * (width_per_group / 6)) * groups         self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=width,                                kernel_size=1, stride=1, bias=False# squeeze channels         self.bn1 = nn.BatchNorm2d(width)         # -----------------------------------------         self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, groups=groups,                                kernel_size=3, stride=stride, bias=False, padding=1)         self.bn2 = nn.BatchNorm2d(width)         # -----------------------------------------         self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel*self.expansion,                                kernel_size=1, stride=1, bias=False# unsqueeze channels         self.bn3 = nn.BatchNorm2d(out_channel*self.expansion)         self.relu = nn.ReLU(inplace=True)         self.downsample = downsample     def forward(self, x):         identity = x         if self.downsample is not None:             identity = self.downsample(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)         out += identity         out = self.relu(out)         return out class ResNet(nn.Module):     def __init__(self,                  block,                  blocks_num,                  num_classes=1000,                  include_top=True,                  groups=1,                  width_per_group=64):         super(ResNet, self).__init__()         self.include_top = include_top         self.in_channel = 64         self.groups = groups         self.width_per_group = width_per_group         self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2,                                padding=3, bias=False)         self.bn1 = nn.BatchNorm2d(self.in_channel)         self.relu = nn.ReLU(inplace=True)         self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)         self.layer1 = self._make_layer(block, 64, blocks_num[0])         self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2)         self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2)         self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2)         if self.include_top:             self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # output size = (1, 1)             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')     def _make_layer(self, block, channel, block_num, stride=1):         downsample = None         if stride != 1 or self.in_channel != channel * block.expansion:             downsample = nn.Sequential(                 nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),                 nn.BatchNorm2d(channel * block.expansion))         layers = []         layers.append(block(self.in_channel,                             channel,                             downsample=downsample,                             stride=stride,                             groups=self.groups,                             width_per_group=self.width_per_group))         self.in_channel = channel * block.expansion         for _ in range(1, block_num):             layers.append(block(self.in_channel,                                 channel,                                 groups=self.groups,                                 width_per_group=self.width_per_group))         return nn.Sequential(*layers)     def forward(self, x):         x = self.conv1(x)         x = self.bn1(x)         x = self.relu(x)         x = self.maxpool(x)         x = self.layer1(x)         x = self.layer2(x)         x = self.layer3(x)         x = self.layer4(x)         if self.include_top:             x = self.avgpool(x)             x = torch.flatten(x, 1)             x = self.fc(x)         return x def resnet34(num_classes=1000, include_top=True):     # https://download.pytorch.org/models/resnet34-333f7ecpth     return ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top) def resnet50(num_classes=1000, include_top=True):     # https://download.pytorch.org/models/resnet50-19c8e357.pth     return ResNet(Bottleneck, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top) def resnet101(num_classes=1000, include_top=True):     # https://download.pytorch.org/models/resnet101-5d3b4d8f.pth     return ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes, include_top=include_top) def resnext50_32x4d(num_classes=1000, include_top=True):     # https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth     groups = 32     width_per_group = 4     return ResNet(Bottleneck, [3, 4, 6, 3],                   num_classes=num_classes,                   include_top=include_top,                   groups=groups,                   width_per_group=width_per_group) def resnext101_32x8d(num_classes=1000, include_top=True):     # https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth     groups = 32     width_per_group = 8     return ResNet(Bottleneck, [3, 4, 23, 3],                   num_classes=num_classes,                   include_top=include_top,                   groups=groups,                   width_per_group=width_per_group)

3.3 核心算法描述

VGG(Visual Geometry Group)核心算法:

1. 卷积层和池化层的堆叠:

1.VGG网络采用了深层的卷积神经网络结构,主要由卷积层和池化层交替构成,形成了一个深度的特征提取器。

2. 小尺寸卷积核的使用:

2.VGG采用较小的3x3卷积核,通过多层堆叠,增加了网络的深度,同时提高了感受野。

3. 多层全连接层:

3.在卷积层之后,VGG包含了几个全连接层,最终通过softmax激活函数进行多类别分类。

4. 结构简单、易复现:

4.VGG网络的结构非常简单和规整,由于使用了小卷积核,可以很容易地设计和复现不同深度的网络结构。

AlexNet核心算法:

1. 多通道卷积层:

AlexNet引入了多通道卷积层,提高了模型对图像特征的学习能力。

2. 局部响应归一化(LRN):

在卷积层之间使用LRN层,有助于提高模型的泛化能力。

3. Dropout正则化:

引入了Dropout层,通过在训练过程中随机丢弃一些神经元,有效防止过拟合。

4. GPU加速:

.AlexNet是早期成功应用GPU进行训练的模型,大大加速了深度神经网络的训练过程。

ResNet(Residual Network)核心算法:

1.残差学习:

ResNet引入了残差学习的概念,通过残差块允许网络学习残差,即实际输出与期望输出之差。这有助于解决深度网络中的梯度消失问题。

2. 跳跃连接:

每个残差块包含跳跃连接,将输入添加到输出,有助于信息更轻松地传播。这种连接方式促使了网络的深度增加,而不会带来梯度消失的问题。

3. 全局平均池化:

.在最后一个残差块之后使用全局平均池化,减少参数数量,提高模型的计算效率。

4. 解决梯度消失问题:

.ResNet的设计使得在训练非常深的网络时,梯度能够更自由地传播,使得网络更易于训练。

这些是VGG、AlexNet和ResNet的核心算法特点。VGG侧重于深度结构和小卷积核的堆叠,AlexNet引入了LRN和Dropout进行正则化,而ResNet通过残差学习和跳跃连接解决了深度网络中的梯度消失问题。

3.4 模型评估与优化

1.Alexnet模型评估和优化:

# -*- codeing = utf-8 -*-

# @Time : 2023/12/11 20:58

# @Author :

# @File : train.py

# @software : PyCharm

import os

import sys

import json



import torch

import torch.nn as nn

import torch.optim as optim

from torchvision import transforms, datasets

from tqdm import tqdm

from model import resnet50





def main():

    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    print("using {} device.".format(device))



    data_transform = {

        "train": transforms.Compose([transforms.RandomResizedCrop(224),

                                     transforms.RandomHorizontalFlip(),

                                     transforms.ToTensor(),

                                     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),

        "val": transforms.Compose([transforms.Resize(256),

                                   transforms.CenterCrop(224),

                                   transforms.ToTensor(),

                                   transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}



    data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  # get data root path

    image_path = r'D:\mongodb\pythonProject1\ResNet大作业\flower_data'  # 新的 flower data set 路径

    assert os.path.exists(image_path), "{} 路径不存在。".format(image_path)



    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),

                                         transform=data_transform["train"])

    train_num = len(train_dataset)



    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}

    flower_list = train_dataset.class_to_idx

    cla_dict = dict((val, key) for key, val in flower_list.items())

    # write dict into json file

    json_str = json.dumps(cla_dict, indent=4)

    with open('class_indices.json', 'w') as json_file:

        json_file.write(json_str)



    batch_size = 6

    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers

    print('Using {} dataloader workers every process'.format(nw))



    train_loader = torch.utils.data.DataLoader(train_dataset,

                                               batch_size=batch_size, shuffle=True,

                                               num_workers=0)



    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),

                                            transform=data_transform["val"])

    val_num = len(validate_dataset)

    validate_loader = torch.utils.data.DataLoader(validate_dataset,

                                                  batch_size=batch_size, shuffle=False,

                                                  num_workers=nw)



    print("using {} images for training, {} images for validation.".format(train_num,

                                                                           val_num))



    net = resnet50(num_classes=5, include_top=True)

    net.to(device)



    # define loss function

    loss_function = nn.CrossEntropyLoss()



    # construct an optimizer

    params = [p for p in net.parameters() if p.requires_grad]

    optimizer = optim.Adam(params, lr=0.1)



    epochs = 5

    best_acc = 0.0

    save_path = './resNet50.pth'

    train_steps = len(train_loader)

    for epoch in range(epochs):

        # train

        net.train()

        running_loss = 0.0

        train_bar = tqdm(train_loader, file=sys.stdout)

        for step, data in enumerate(train_bar):

            images, labels = data

            optimizer.zero_grad()

            logits = net(images.to(device))

            loss = loss_function(logits, labels.to(device))

            loss.backward()

            optimizer.step()



            # print statistics

            running_loss += loss.item()



            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,

                                                                     epochs,

                                                                     loss)



        # validate

        net.eval()

        acc = 0.0  # accumulate accurate number / epoch

        with torch.no_grad():

            val_bar = tqdm(validate_loader, file=sys.stdout)

            for val_data in val_bar:

                val_images, val_labels = val_data

                outputs = net(val_images.to(device))

                # loss = loss_function(outputs, test_labels)

                predict_y = torch.max(outputs, dim=1)[1]

                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()



                val_bar.desc = "valid epoch[{}/{}]".format(epoch + 1,

                                                           epochs)



        val_accurate = acc / val_num

        print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %

              (epoch + 1, running_loss / train_steps, val_accurate))



        if val_accurate > best_acc:

            best_acc = val_accurate

            torch.save(net.state_dict(), save_path)



    print('Finished Training')





if __name__ == '__main__':

    main()

2.VGG16模型评估及优化:

import os

import sys

import json



import torch

import torch.nn as nn

from torchvision import transforms, datasets

# torchvision.datasets: 一些加载数据的函数及常用的数据集接口;

# torchvision.models: 包含常用的模型结构(含预训练模型),例如AlexNetVGGResNet等;

# torchvision.transforms: 常用的图片变换,例如裁剪、旋转等;

import torch.optim as optim

from tqdm import tqdm



from model import vgg





def main():

    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    print("using {} device.".format(device))

    # 定义一个把数据集的照片处理得到向量的操作

    data_transform = {  # transforms.Compose这个类的主要作用是串联多个图片变换的操作。

        "train": transforms.Compose([transforms.RandomResizedCrop(224),

                                     # transforms.RandomResizedCrop(224)将给定图像随机裁剪为不同的大小和宽高比,

                                     # 然后缩放所裁剪得到的图像为制定的大小;(即先随机采集,然后对裁剪得到的图像缩放为同一大小),默认scale=(0.08, 1.0)



                                     transforms.RandomHorizontalFlip(),

                                     # transforms.RandomHorizontalFlip  随机水平翻转

                                     transforms.ToTensor()# transforms.ToTensor() 将给定图像转为Tensor

                                     # transforms.Normalize() 归一化处理

                                     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),

        "val": transforms.Compose([transforms.Resize((224, 224)),

                                   transforms.ToTensor(),

                                   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}



    data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  # get data root path

    image_path = r'D:\mongodb\pythonProject1\大作业vgg\flower_data'  # 绝对路径



    assert os.path.exists(image_path), "{} 路径不存在.".format(image_path)



    # 把数据集的照片处理得到向量

    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),

                                         transform=data_transform["train"])

    # datasets.ImageFolder

    # root:图片存储的根目录,即各类别文件夹所在目录的上一级目录,在下面的例子中是’./ data / train /’。

    # transform:对图片进行预处理的操作(函数),原始图片作为输入,返回一个转换后的图片。

    # target_transform:对图片类别进行预处理的操作,输入为

    # target,输出对其的转换。如果不传该参数,即对target不做任何转换,返回的顺序索引0, 1, 2…

    # loader:表示数据集加载方式,通常默认加载方式即可。

    # 生成的dataset 有以下成员变量:

    # self.classes:用一个list保存类别名称

    # self.class_to_idx:类别对应的索引,与不做任何转换返回的 对应

    # self.imgs:保存(img - path,class ) tuple list,

    train_num = len(train_dataset)



    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}

    flower_list = train_dataset.class_to_idx

    cla_dict = dict((val, key) for key, val in flower_list.items())  # 保存花种类名称

    # write dict into json file

    json_str = json.dumps(cla_dict, indent=4# 花种类名称写入文件

    with open('class_indices.json', 'w') as json_file:

        json_file.write(json_str)



    batch_size = # 每批次训练8个

    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers

    # num_workers 用多少个子进程加载数据

    print('Using {} dataloader workers every process'.format(nw))



    # torch.utils.data.DataLoader

    # 该接口主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor,

    # 后续只需要再包装成Variable即可作为模型的输入,因此该接口有点承上启下的作用,比较重要

    #

    # 参数:



    # dataset(Dataset) – 加载数据的数据集。

    # batch_size(int, optional) – 每个batch加载多少个样本(默认: 1)。

    # shuffle(bool, optional) – 设置为True时会在每个epoch重新打乱数据(默认: False).

    # sampler(Sampler, optional) – 定义从数据集中提取样本的策略,即生成index的方式,可以顺序也可以乱序

    # num_workers(int, optional) – 用多少个子进程加载数据。0表示数据将在主进程中加载(默认: 0)

    train_loader = torch.utils.data.DataLoader(train_dataset,

                                               batch_size=batch_size, shuffle=True,

                                               num_workers=nw)

    # 处理验证集

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),

                                            transform=data_transform["val"])

    val_num = len(validate_dataset)

    validate_loader = torch.utils.data.DataLoader(validate_dataset,

                                                  batch_size=batch_size, shuffle=False,

                                                  num_workers=nw)

    print("using {} images for training, {} images for validation.".format(train_num,

                                                                           val_num))



    # test_data_iter = iter(validate_loader)

    # test_image, test_label = test_data_iter.next()

    # 开始确定网络模型

    model_name = "vgg16"

    net = vgg(model_name=model_name, num_classes=5, init_weights=True)

    # vgg(model_name="vgg16", **kwargs):model = VGG(make_features(model_name), **kwargs)

    #

    # VGG(self, features, num_classes=1000, init_weights=False):

    net.to(device)

    # 这行代码的意思是将所有最开始读取数据时的tensor变量copy一份到device所指定的GPU上去,之后的运算都在GPU上进行

    loss_function = nn.CrossEntropyLoss()

    # nn.CrossEntropyLosspytorch下的交叉熵损失,用于分类任务使用

    optimizer = optim.Adam(net.parameters(), lr=0.0001)

    # 为了使用torch.optim,需先构造一个优化器对象Optimizer,用来保存当前的状态,并能够根据计算得到的梯度来更新参数。

    # 要构建一个优化器optimizer,你必须给它一个可进行迭代优化的包含了所有参数(所有的参数必须是变量s)的列表。 然后,您可以指定程序优化特定的选项,例如学习速率,权重衰减等。

    # 训练次数

    epochs = 30

    best_acc = 0.0  # 最好准确率

    save_path = './{}Net.pth'.format(model_name)

    train_steps = len(train_loader)  # 训练集的个数

    for epoch in range(epochs):

        # train

        net.train()  # 开始训练

        # b) model.train() :启用 BatchNormalization Dropout。

        # 在模型测试阶段使用model.train() model变成训练模式,此时dropoutbatch

        # normalization的操作在训练q起到防止网络过拟合的问题。

        running_loss = 0.0  # 损失

        # Tqdm 是一个快速,可扩展的Python进度条,可以在 Python 长循环中添加一个进度提示信息,

        # 用户只需要封装任意的迭代器 tqdm(iterator)。

        train_bar = tqdm(train_loader, file=sys.stdout)



        for step, data in enumerate(train_bar):

            images, labels = data

            optimizer.zero_grad()  # 一些优化算法,如共轭梯度和LBFGS需要重新评估目标函数多次,必须清除梯度

            outputs = net(images.to(device))  # 输出是预测值

            loss = loss_function(outputs, labels.to(device))  # 损失,通过预测和标签计算

            loss.backward()  # 反向传播计算梯度

            optimizer.step()  # step()方法来对所有的参数进行更新



            # print statistics

            running_loss += loss.item()



            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,

                                                                     epochs,

                                                                     loss)



        # validate

        net.eval()  # 开始验证,计算准确率

        # a) model.eval(),不启用

        # BatchNormalizationDropout。此时pytorch会自动把BNDropOut固定住,不会取平均,而是用训练好的值。

        # 不然的话,一旦testbatch_size过小,很容易就会因BN层导致模型performance损失较大;

        acc = 0.0  # accumulate accurate number / epoch

        with torch.no_grad():

            val_bar = tqdm(validate_loader, file=sys.stdout)

            for val_data in val_bar:

                val_images, val_labels = val_data  # 获取验证数据

                outputs = net(val_images.to(device))

                predict_y = torch.max(outputs, dim=1)[1# 预测值的种类的索引

                # output = torch.max(input, dim)

                # 输入

                # inputsoftmax函数输出的一个tensor

                # dimmax函数索引的维度0 / 10是每列的最大值,1是每行的最大值

                # 输出

                # 函数会返回两个tensor,第一个tensor是每行的最大值;第二个tensor是每行最大值的索引。



                # 对两个张量Tensor进行逐元素的比较,若相同位置的两个元素相同,则返回True;若不同,返回False。

                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()



        val_accurate = acc / val_num  # 预测准确个数/总数

        print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %

              (epoch + 1, running_loss / train_steps, val_accurate))



        if val_accurate > best_acc:  # 保留最好的准确率(模型)

            best_acc = val_accurate

            torch.save(net.state_dict(), save_path)



    print('Finished Training')





if __name__ == '__main__':

    main()

3.ResNet模型评估及优化
# -*- codeing = utf-8 -*-

# @Time : 2023/12/11 20:58

# @Author :

# @File : train.py

# @software : PyCharm

import os

import sys

import json



import torch

import torch.nn as nn

import torch.optim as optim

from torchvision import transforms, datasets

from tqdm import tqdm

from model import resnet50





def main():

    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    print("using {} device.".format(device))



    data_transform = {

        "train": transforms.Compose([transforms.RandomResizedCrop(224),

                                     transforms.RandomHorizontalFlip(),

                                     transforms.ToTensor(),

                                     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),

        "val": transforms.Compose([transforms.Resize(256),

                                   transforms.CenterCrop(224),

                                   transforms.ToTensor(),

                                   transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}



    data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  # get data root path

    image_path = r'D:\mongodb\pythonProject1\ResNet大作业\flower_data'  # 新的 flower data set 路径

    assert os.path.exists(image_path), "{} 路径不存在。".format(image_path)



    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),

                                         transform=data_transform["train"])

    train_num = len(train_dataset)



    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}

    flower_list = train_dataset.class_to_idx

    cla_dict = dict((val, key) for key, val in flower_list.items())

    # write dict into json file

    json_str = json.dumps(cla_dict, indent=4)

    with open('class_indices.json', 'w') as json_file:

        json_file.write(json_str)



    batch_size = 6

    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers

    print('Using {} dataloader workers every process'.format(nw))



    train_loader = torch.utils.data.DataLoader(train_dataset,

                                               batch_size=batch_size, shuffle=True,

                                               num_workers=0)



    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),

                                            transform=data_transform["val"])

    val_num = len(validate_dataset)

    validate_loader = torch.utils.data.DataLoader(validate_dataset,

                                                  batch_size=batch_size, shuffle=False,

                                                  num_workers=nw)



    print("using {} images for training, {} images for validation.".format(train_num,

                                                                           val_num))



    net = resnet50(num_classes=5, include_top=True)

    net.to(device)



    # define loss function

    loss_function = nn.CrossEntropyLoss()



    # construct an optimizer

    params = [p for p in net.parameters() if p.requires_grad]

    optimizer = optim.Adam(params, lr=0.1)



    epochs = 5

    best_acc = 0.0

    save_path = './resNet50.pth'

    train_steps = len(train_loader)

    for epoch in range(epochs):

        # train

        net.train()

        running_loss = 0.0

        train_bar = tqdm(train_loader, file=sys.stdout)

        for step, data in enumerate(train_bar):

            images, labels = data

            optimizer.zero_grad()

            logits = net(images.to(device))

            loss = loss_function(logits, labels.to(device))

            loss.backward()

            optimizer.step()



            # print statistics

            running_loss += loss.item()



            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,

                                                                     epochs,

                                                                     loss)



        # validate

        net.eval()

        acc = 0.0  # accumulate accurate number / epoch

        with torch.no_grad():

            val_bar = tqdm(validate_loader, file=sys.stdout)

            for val_data in val_bar:

                val_images, val_labels = val_data

                outputs = net(val_images.to(device))

                # loss = loss_function(outputs, test_labels)

                predict_y = torch.max(outputs, dim=1)[1]

                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()



                val_bar.desc = "valid epoch[{}/{}]".format(epoch + 1,

                                                           epochs)



        val_accurate = acc / val_num

        print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %

              (epoch + 1, running_loss / train_steps, val_accurate))



        if val_accurate > best_acc:

            best_acc = val_accurate

            torch.save(net.state_dict(), save_path)



    print('Finished Training')





if __name__ == '__main__':

    main()

3.5 模型对比实验

  1. Alexnet模型预测:
  2. # -*- codeing = utf-8 -*- # @Time : 2023/12/11 20:14 # @Author : # @File : predict.py.py # @software : PyCharm import os import json import torch from PIL import Image from torchvision import transforms import matplotlib.pyplot as plt from model import AlexNet def main():     device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")     data_transform = transforms.Compose(         [transforms.Resize((224, 224)),          transforms.ToTensor(),          transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])     # load image     img_path = "./1.jpg"     assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)     img = Image.open(img_path)     plt.imshow(img)     # [N, C, H, W]     img = data_transform(img)     # expand batch dimension     img = torch.unsqueeze(img, dim=0)     # read class_indict     json_path = './class_indices.json'     assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)     json_file = open(json_path, "r")     class_indict = json.load(json_file)     # create model     model = AlexNet(num_classes=5).to(device)     # load model weights     weights_path = "./AlexNet.pth"     assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)     model.load_state_dict(torch.load(weights_path))    # 关闭 Dropout     model.eval()     with torch.no_grad():         # predict class         output = torch.squeeze(model(img.to(device))).cpu()         predict = torch.softmax(output, dim=0)         predict_cla = torch.argmax(predict).numpy()     print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],                                                  predict[predict_cla].numpy())     plt.title(print_res)     for i in range(len(predict)):         print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],                                                   predict[i].numpy()))     plt.show() if __name__ == '__main__':     main()

2.VGG16模型预测:

# -*- codeing = utf-8 -*-

# @Time : 2023/12/12 10:25

# @Author :

# @File : predict.py

# @software : PyCharm

import os

import json



import torch

from PIL import Image

from torchvision import transforms

import matplotlib.pyplot as plt



from model import vgg





def main():

    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")



    data_transform = transforms.Compose(

        [transforms.Resize((224, 224)),

         transforms.ToTensor(),

         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])



    # load image

    img_path = "./1.jpg"

    # img_path = "image/1.jpg"

    # img_path = "image/roses.jpg"

    # img_path = "image/zpp.jpg"

    # img_path = "image/zx4.jpg"

    # img_path = "image/sunflowersP.jpg"

    # img_path = "image/sunflowers.jpg"

    # img_path = "image/dandelion.jpg"

    # img_path = "image/daisy.jpg"

    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)

    img = Image.open(img_path)

    plt.imshow(img)

    # [N, C, H, W]

    img = data_transform(img)  # 处理测试图片为张量

    # expand batch dimension

    img = torch.unsqueeze(img, dim=0)

    # unsqueeze()函数起升维的作用,参数表示在哪个地方加一个维度。

    # 在第一个维度(中括号)的每个元素加中括号 0表示在张 量最外层加一个中括号变成第一维。

    # 图像的向量为一个列表,大小是224*224unsqueeze后变为1 x 224*224的张量



    # read class_indict

    json_path = './class_indices.json'  # 读取花种类名称,字典格式

    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)



    with open(json_path, "r") as f:

        class_indict = json.load(f)



    # create model

    model = vgg(model_name="vgg16", num_classes=5).to(device)

    # load model weights

    weights_path = "./vgg16Net.pth"  # 加载训练好的权重

    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)

    # 把权重填入模型,

    # map_location=torch.device('cpu'),意思是映射到cpu上,在cpu上加载模型,无论你这个模型从哪里训练保存的。

    # 一句话:map_location适用于修改模型能在gpu上运行还是cpu上运行

    model.load_state_dict(torch.load(weights_path, map_location=device))



    model.eval()  # 开始验证,不启用 BatchNormalization  Dropout。

    with torch.no_grad():

        # predict class

        output = torch.squeeze(model(img.to(device))).cpu()

        # queeze()

        # 函数的功能是维度压缩。返回一个tensor(张量),其中input中大小为1的所有维都已删除。

        #

        # 举个例子:如果 input的形状为(A×1×B×C×1×D),那么返回的tensor的形状则为(A×B×C×D)

        predict = torch.softmax(output, dim=0)

        # torch.nn.Softmax中只要一个参数:来制定归一化维度如果是dim=0指代的是行,dim=1指代的是列。

        predict_cla = torch.argmax(predict).numpy()  # 返回最大值的索引,也就是预测种类的名称

        # torch.argmax(input, dim=None, keepdim=False)

        # 返回指定维度最大值的序号

        # dim给定的定义是:the demention to reduce.也就是把dim这个维度的,变成这个维度的最大值的index。



    # 打印种类名字和正确概率

    print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],

                                                 predict[predict_cla].numpy())

    plt.title(print_res)



    # 分别打印每个种类的可能概率

    for i in range(len(predict)):

        print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],

                                                  predict[i].numpy()))

    plt.show()





if __name__ == '__main__':

    main()

3.ResNet模型预测:

import torch

from torchvision import transforms

from PIL import Image

import os

import json

import matplotlib.pyplot as plt

from model import resnet34  # Replace 'model' with the actual module where resnet34 is defined





def main():

    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")



    data_transform = transforms.Compose(

        [transforms.Resize(256),

         transforms.CenterCrop(224),

         transforms.ToTensor(),

         transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])



    # load image

    img_path = "./1.jpg"

    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)

    img = Image.open(img_path)

    plt.imshow(img)



    # [N, C, H, W]

    img = data_transform(img)



    # Change batch size to 6

    img = torch.unsqueeze(img, dim=0)[:6]



    # read class_indict

    json_path = './class_indices.json'

    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)

    from torchvision.models import resnet50

    model = resnet50(num_classes=5).to(device)



    with open(json_path, "r") as f:

        class_indict = json.load(f)



    # create model

    model = resnet50(num_classes=5).to(device)



    # load model weights

    weights_path = "./resNet50.pth"

    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)

    model.load_state_dict(torch.load(weights_path, map_location=device), strict=False)



    # prediction

    model.eval()

    with torch.no_grad():

        # predict class

        output = torch.squeeze(model(img.to(device))).cpu()

        predict = torch.softmax(output, dim=0)

        predict_cla = torch.argmax(predict).numpy()



    print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],

                                                 predict[predict_cla].numpy())

    plt.title(print_res)

    for i in range(len(predict)):

        print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],

                                                  predict[i].numpy()))

    plt.show()





if __name__ == '__main__':

    main()

第四章 总结

在本次实验过程中,通过对花卉数据集进行处理,运用Alexnet,VGG,ResNet三种模型进行训练,分析再通过函数进行预测分析,对三种模型进行对比测试的过程中,深入体验了三种模型的不同之处,明白了各个模型的不同,各有优缺点:

AlexNet:

优点:

1.先进的架构: AlexNet在2012年的ImageNet大规模视觉识别挑战赛中引入了深度学习,并以其先进的架构赢得了比赛,推动了深度学习的发展。

2.多通道卷积: 引入了多通道卷积层,增强了网络对图像特征的学习能力。

3.GPU加速: AlexNet是早期成功应用GPU进行训练的模型,加速了深度神经网络的发展。

缺点:

4.计算资源需求高: 相对于后续模型,AlexNet的计算资源需求较高,不太适用于资源有限的环境。

5.较大的模型尺寸: 相较于后续模型,AlexNet的模型尺寸相对较大,可能导致在一些轻量化场景中不太适用。

VGG (Visual Geometry Group):

优点:

6.简洁而规整的结构: VGG网络结构非常简洁和规整,易于理解和实现。采用了小尺寸的卷积核和深层次的网络结构。

7.易复现: 由于结构的规整性,VGG网络易于复现,使其成为学术研究和教育中的常用模型。

缺点:

8.参数较多: 由于采用了深层次的卷积层,VGG的模型参数较多,导致训练和推理的计算成本较高。

9.计算资源需求大: 同样因为较多的参数,VGG对计算资源的需求相对较大。

ResNet (Residual Network):

优点:

10.解决梯度消失问题: 引入了残差学习,通过跳跃连接解决了深度网络中的梯度消失问题,使得可以训练更深的网络。

11.更轻松的网络设计: 可以更轻松地设计和训练非常深的网络,而无需担心梯度消失的问题。

12.高性能: 在ImageNet等任务中取得了很好的性能,成为许多视觉任务的基准模型。

缺点:

13.模型较为复杂: ResNet的模型相对较为复杂,不如VGG那样简洁,可能在资源受限的环境中使用较为困难。

14.计算资源需求高: 由于网络的深度,ResNet对计算资源的需求也相对较高。

总体而言,每个模型都有其优势和限制,选择模型应该基于任务需求、可用资源以及性能要求等综合考虑。近年来,一些后续模型也在不同方面做了改进,如EfficientNet、MobileNet等,特别适用于轻量级和移动设备上,在实验过程中也遇到了许多困难,但是都通过各种渠道良好的解决,因此,在未来的学习生活过程中应该不惧困难,直面困难。

                             

     月   日

  • 22
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值