基于深度学习的花卉识别

文章介绍了作者创建的花卉识别数据集,包含27万张224x224的花卉图片,已按种类分批发布。使用22种主流图片分类模型进行训练和评估,包括ResNet、VGG、SqueezeNet等,在GPU和CPU平台上测试了模型的推理速度和准确性。SqueezeNet和MobileNet系列因高准确度和快速推理速度成为优选模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、数据集

       春天来了,我在公园的小道漫步,看着公园遍野的花朵,看起来真让人心旷神怡,一周工作带来的疲惫感顿时一扫而光。难得一个糙汉子有闲情逸致俯身欣赏这些花朵儿,然而令人尴尬的是,我一朵都也不认识。那时我在想,如果有一个花卉识别软件,可以用手机拍一下就知道这是一种什么花朵儿,那就再好不过了。我不知道市场上是否有这样一种软件,但是作为一个从事深度学习的工程师,我马上知道了怎么做,最关键的不是怎么做,而是数据采集。住所附近就是大型公园,一年司机繁花似锦,得益于此,我可以在闲暇时间里采集到大量的花卉数据。本数据集由本人亲自使用手机进行拍摄采集,原始数据集包含了27万张图片,图片的尺寸为1024x1024,为了方便储存和传输,把原图缩小为224x224。采集数据是一个漫长的过程,因此数据集的发布采用分批发布的形式,也就是每采集够16种花卉,就发布一次数据集。每种花卉的图片数量约为2000张,每次发布的数据集的图片数量约为32000张,每次发布的数据集包含的花卉种类都不一样。目前花卉的种类只有48种,分为三批发布,不过随着时间的推移,采集到的花卉越来越多。这里就把数据集分享出来,供各位人工智能算法研究者使用。以下是花卉数据集的简要介绍和下载地址。
(1)花卉数据集01(数据集+训练代码下载地址
       花卉数据集01,采集自2022年,一共16种花卉,数据集大小为32000张,图片大小为224x224的彩色图像。数据集包含的花卉名称为:一年蓬,三叶草,三角梅,两色金鸡菊,全叶马兰,全缘金光菊,剑叶金鸡菊,婆婆纳,油菜花,滨菊,石龙芮,绣球小冠花,蒲公英,蓝蓟,诸葛菜,鬼针草。数据集的缩略图如下:
图1 花卉数据集01
(2)花卉数据集02(数据集+训练源码下载地址
       花卉数据集02,采集与2023年,一共16种花卉,每种花卉约2000张,总共32000,图片大小为224x224。数据集包含的花卉有:千屈菜,射干,旋覆花,曼陀罗,桔梗,棣棠,狗尾草,狼尾草,石竹,秋英,粉黛乱子草,红花酢浆草,芒草,蒲苇,马鞭草,黄金菊。数据集缩略图如下:
图2 花卉数据集02
(3)花卉数据集3(数据集+训练源码下载地址
       花卉数据集03,采集与2023年,一共16种花卉,每种花卉约2000张,总共32000,图片大小为224x224。数据集包含的花卉有:北香花介,大花耧斗菜,小果蔷薇,小苜蓿,小蜡,泽珍珠菜,玫瑰,粉花绣线菊,线叶蓟,美丽月见草,美丽芍药,草甸鼠尾草,蓝花鼠尾草,蛇莓,长柔毛野豌豆,高羊茅。数据集缩略图如下:
图3 花卉数据集3

2、图片分类模型

       为了研究不同图片分类模型对于花朵的分类效果,以及图片分类模型在不同硬件平台的推理速度,这里分别使用目前主流的22种图片分类模型进行训练,并在cpu平台和GPU平台进行部署测试。这些模型是如下:

  • resnet系列:resnet18、resnet34、resnet50、resnet101、resnet152。
  • vgg系列:vgg11、vgg13、vgg6、vgg19。
  • squeezenet系列:squeezenet_v1、squeezenet_v2、squeezenet_v3。
  • mobilenet系列:mobilenet_v1、mobilenet_v2。
  • inception系列:inception_v1、inception_v2、inception_v3。
  • 其他系列:alexnet、lenet、mnist、tsl16、zfnet。

以上模型的训练代码基于pytorch架构,内置集成22了种模型,可进行傻瓜式训练。以下的代码块为训练代码的主脚本,完整的训练代码以及数据集请在此链接下载:源码下载链接

import torch
import torch.nn as nn
import torch.optim as optim
from utils.dataloader import CustomImageDataset
from torch.utils.data import DataLoader
from utils.build_model import build_model
import argparse
import time
import os

if __name__ == '__main__':
    parser =argparse.ArgumentParser(description='图片分类模型训练')
    parser.add_argument('-input_shape', type=tuple,default=(3,224,224),help='模型输入的通道数、高度、宽度')
    parser.add_argument('-train_imgs_dir', type=str,default='dataset/train',help='训练集目录')
    parser.add_argument('-test_imgs_dir', type=str,default='dataset/test',help='测试集目录')
    parser.add_argument('-classes_file', type=str,default='dataset/classes.txt',help='类别文件')
    parser.add_argument('-epochs', type=int,default=50,help='迭代次数')
    parser.add_argument('-batch_size', type=int,default=64,help='批大小,根据显存大小调整')
    parser.add_argument('-init_weights', type=str,default="init_weights/squeezenet_v1.pth",help='用于初始化的权重,请确保初始化的权重和训练的模型相匹配')
    parser.add_argument('-optim', type=str,default="adam",help='优化器选择,可选sgd或者adam. sgd优化器训练效果较好,但参数比较难调节,不好收敛')
    parser.add_argument('-lr', type=float,default=0.0001,
                        help='初始学习率,此参数对模型训练影响较大,如果选择不合适,模型甚至不收敛.\
                              如果遇到模型训练不收敛(损失函数不下降,准确度很低),可以尝试调整学习率.\
                              resnet系列推荐优化器选择sgd,学习率设0.001;vgg系列优化器推荐adam,学习率为0.0001,其他模型优化器选择adam,推荐学习率为0.0002') 
    parser.add_argument('-model_name', type=str,default='squeezenet_v1',
                        help='模型名称,可选resnet18/resnet34/resnet50/resnet101/resnet152\
                             /alexnet/lenet/zfnet/tsl16/mnist\
                             vgg11/vgg13/vgg16,vgg19\
                             squeezenet_v1/squeezenet_v2/squeezenet_v3\
                             inception_v1/inception_v2/inception_v3\
                             mobilenet_v1/mobilenet_v2/\
                          ')
    parser.add_argument('-argument', type=bool,default=True,help='是否在训练时开启数据增强模式')


args = parser.parse_args()
print("模型:",args.model_name)
device = (
    "cuda"
    if torch.cuda.is_available()
    else "mps"
    if torch.backends.mps.is_available()
    else "cpu"
)

classes=[]
try:
    with open(args.classes_file,"rt",encoding="ANSI")as f:
        for line in f:
            classes.append(line.strip())
except:
    with open(args.classes_file,"rt",encoding="UTF-8")as f:
        for line in f:
            classes.append(line.strip())

num_class=len(classes)
model=build_model(args.model_name,args.input_shape,num_class)
if os.path.exists(args.init_weights):
    try:
        model.load_state_dict(args.init_weights)
    except:
        model.weights_init()
        print("参数初始化失败!请确保初始化参数与模型相一致.")
else:
    model.weights_init()
    print("没有找到名称为%s的权重文件,模型将跳过参数初始化"%(args.init_weights))
model=model.to(device)
# Create data loaders.

training_data=CustomImageDataset(args.train_imgs_dir,classes,args.argument)
test_data=CustomImageDataset(args.test_imgs_dir,classes,False)


train_dataloader = DataLoader(training_data, batch_size=args.batch_size,shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=args.batch_size)

loss_fn=nn.CrossEntropyLoss()
if args.optim=="adam":
    optimizer=optim.Adam(model.parameters(), lr=args.lr)
else:
    optimizer=optim.SGD(model.parameters(), lr=args.lr,momentum=0.9,weight_decay=0.0005)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer,args.epochs)
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    model.train()
    start=time.time()
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)
        # Compute prediction error
        optimizer.zero_grad()
        pred = model(X)
        loss = loss_fn(pred, y)
        # Backpropagation
        loss.backward()
        optimizer.step()
        scheduler.step()
        if batch % 100 == 0:
            loss, current = loss.item(), (batch + 1) * len(X)
            end=time.time()
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]  time: {end-start:>3f}s")

def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()
    test_loss, correct = 0, 0
    with torch.no_grad():
        start=time.time()
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
        end=time.time()
    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} , Time: {end-start:>3f}s\n")
    return correct

best_accuracy=0
for t in range(args.epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    current_accuracy=test(test_dataloader, model, loss_fn)
    if current_accuracy>best_accuracy:
        best_accuracy=current_accuracy
        torch.save(model,"weights/%s_best_accuracy.pth"%(args.model_name))
torch.save(model,"weights/%s_last_accuracy_%.2f.pth"%(args.model_name,current_accuracy))
print("Done!")

3、图片分类模型评估

       分别训练了22种模型,图片为224x224的RGB图像。将约96000张图片划分为训练集和测试集,其中测试集占10%,一共9600张,训练集90%,一共86400张。训练充分后,对各种模型的top1、top2、top3、top4、top5准确度进行评估,并分别在cpu平台(intel i9)和gpu平台(RTX 3090)进行推理速度的测试。模型性能评估以及推理速度测试结果如表1所示。

表1 模型性能评估以及推理速度测试结果

模型参数量 [M]计算量 [G]GPU速度[FPS]CPU速度[FPS]Top1准确度[%]Top2准确度[%]Top3准确度[%]Top4准确度[%]Top5准确度[%]
resnet1811.193.64557.30204.0598.8499.7099.8699.9399.96
resnet3421.307.35347.39104.3598.6499.7499.8699.9399.96
resnet5034.9410.31295.2468.1998.6799.6699.8699.9499.97
resnet10153.9017.77171.8441.2798.6199.6499.8399.9099.94
resnet15268.4123.46130.2131.2098.4499.5899.8699.9299.96
vgg11128.9615.25462.2430.0092.8897.3598.6999.2699.48
vgg13129.1522.67411.5522.2795.1898.2299.2199.5599.73
vgg16134.4630.99340.3420.1495.3598.4899.2999.5099.60
vgg19139.7739.33292.5116.7194.8998.2599.0199.3999.62
mobilenet_v13.251.16942.09506.4297.4599.4499.7199.8399.90
mobilenet_v24.030.91489.69386.2095.9998.9899.5299.7599.81
inception_v16.023.20343.56203.4395.8098.7999.4499.7499.83
inception_v27.853.34291.10165.4998.3099.5499.8099.8599.90
inception_v321.877.65136.2571.8999.0599.8199.9299.9599.97
squeezenet_v10.761.61758.27362.2297.4499.3699.6999.8199.86
squeezenet_v20.761.61704.60360.7597.2799.2399.6799.8099.85
squeezenet_v31.102.37658.07267.2898.2899.5499.7799.8599.89
mnist_net214.4551.37189.8110.1389.4796.2098.1498.9199.28
AlexNet17.692.35858.92211.7396.2098.7999.4999.6999.76
LeNet78.450.941041.7576.1284.8693.7596.5597.9498.70
TSL16116.9523.56381.6324.0995.6198.4099.1599.5399.69
ZF_Net72.092.68351.8782.6896.5899.0399.4799.6899.80

       从表一展示的结果来看,面对48种花卉的分类任务:如果只关心Top5分类准确度,那么这些模型均能达到98%以上的分类准确度,大部分模型的Top5准确度都能达到99%以上,对于实际应用而言,花卉分类程序通常会给出5个备选项,这样的话,只要5个备选项里边存在一个正确选项,就可以认为花卉分类是成功的。当然,如果追求单一选项的准确性,resnet系列模型、inception系列、squeezenet系列模型,在Top1分类准确度上表现不俗,可以达到97%以上的准确度。通常来说,可以工程化的图片分类模型,不仅仅要求其具备良好的分类准确度,还对其推理速度有一定的要求。表1的推理速度测试数据,分别在Intel i9 CPU平台和英伟达RTX 3090 GPU平台进行测试,推理用的软件接口是onnx推理架构,测试的策略是逐一对1000张224x224的彩色图片输送到模型中进行推理,统计其总的推理时间,然后计算平均推理帧率。从表1的数据可以得知,GPU平台的推理速度要比CPU平台的推理的速度快很多,而且在GPU平台推理帧率高的模型,在CPU平台的推理帧率未必高,反之亦然,也就是说,模型推理帧率的排名,跟硬件平台是有关的。在GPU平台上推理帧率比较靠前的模型是squeezenet系列、mobilenet系列,以及alexnet和lenet;在CPU平台上推理帧率比较靠前的模型是squeezenet系列、mobilenet系列、alexnet、inception_v1、resnet18。综合来说,squeezenet系列、mobilenet系列得分是最高的,因为他们在分类准确度上表现优秀,并且在GPU和CPU平台上的推理帧率都变现不错,而且模型的参数量很小,适合在线部署和嵌入式部署,所以这些模型应当优先选择。另外,从评估和测试的结果来看,还可以得到以下几个结论:

  • 对于可以并行计算的硬件平台来说,比如GPU、NPU以及一些具有批处理能力的CPU,模型的推理帧率跟模型的参数量和计算量没有绝对的关联性,更多的是跟模型的结构有关,如果模型适合于并行计算,那么即使模型具有较大的计算量,其推理速度也可以很快;
  • 对于串行执行的计算硬件来说,比如常规指令的CPU,模型的推理速度跟模型的计算量的是线性相关的,也就是说计算量越大,推理帧率越低;
  • 适合GPU平台部署的模型未必适合在CPU平台上部署,所以模型的选择要根据最终的部署平台而定。

4、总结

       花卉数据集共包括96000张图片,囊括了48种花卉的类别,其中10%为测试集,90%为训练集。图片的大小为224x224,通道数为3。一共使用了22种模型进行训练,通过模型评估和硬件平台部署测试得出结论:squeezenet_v1、squeezenet_v2、squeezenet_v3、mobilenet_v1、mobilenet_v2几个模型,具有参数量小,计算量小,分类准确度高的优点,并且在GPU平台和CPU平台上推理速度较快,适合在各种平台上部署,特别是适合移动端和嵌入式的部署。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shifenglv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值