文章目录
- **基于深度学习算法的植物/中草药分类识别系统**
- 一、项目摘要
- 二、项目运行效果
- 三、项目文件介绍
- 四、项目环境配置
- 1、项目环境库
- 2、环境配置视频教程
- 五、项目系统架构
- 六、项目构建流程
- 1、数据集
- 2、算法网络Mobilenet
- 3、网络模型训练
- 4、训练好的模型预测
- 5、UI界面设计-pyqt5
- 6、项目相关评价指标
- 七、项目论文报告
- 八、版权说明及获取方式
- 1、项目获取方式
- 2、项目版权说明及定制服务
各位同学大家好,本次给大家分享的项目为:
基于深度学习算法的植物/中草药分类识别系统
一、项目摘要
本项目设计并实现一个基于深度学习算法的植物识别系统。该系统采用Mobilenet深度学习模型,利用网络采集的一个包含67类植物、共6877张图像的数据集,结合Pytorch框架进行模型训练和优化,通过准确率、损失值和混淆矩阵三种评价指标,验证了该系统的识别性能,并使用pyqt5库设计了图形用户界面(GUI),实现了便捷的植物识别和结果展示功能。
二、项目运行效果
运行效果视频:
https://www.bilibili.com/video/BV1QgtVeDEDq
运行效果截图:
三、项目文件介绍
四、项目环境配置
1、项目环境库
python=3.8 pytorch pyqt5 opencv matplotlib 等
2、环境配置视频教程
1)anaconda下载安装教程
2)pycharm下载安装教程
3)项目环境库安装步骤教程
五、项目系统架构
该系统的主要功能模块如下:
- 图像选择与上传模块: 用户界面的主页提供了一个“选择图像”按钮,用户点击后可以从本地设备选择需要识别的植物图像。系统支持常见的图像格式(如JPEG、PNG等),并将所选图像显示在界面中央,方便用户确认。
- 图像处理与识别模块:当用户上传图像后,点击“开始检测”按钮,系统会调用Mobilenet模型对图像进行处理和识别。系统将在后台运行深度学习推断过程,识别时间较短,通常在几秒内完成。
2.1 图像处理模块:
用户上传图像后,系统会对输入的植物图像进行预处理。具体步骤包括:
1)图像的尺寸调整:将输入图像的尺寸调整为模型所需的大小,即224×224像素。
2)图像标准化:按照ImageNet预训练模型的标准,对图像的像素值进行归一化处理。
2.2 模型预测模块:
经过处理的图像输入到预训练的Mobilenet模型中进行预测。该系统采用了在Pytorch框架上训练的Mobilenet 模型,该模型能够在计算资源有限的环境中实现高效的植物识别。模型加载了之前在训练集上获得的最佳权重文件,并进行推断操作,输出分类结果。 - 识别结果展示模块:识别完成后,系统会在界面的结果区域显示植物的名称、分类置信度以及对应的植物详细信息。
六、项目构建流程
1、数据集
数据集文件夹:all_data
概述:
在本系统中,使用了一个从网络上收集的植物图像数据集,包含67类不同种类的植物,共计6877张图像。
数据集格式及命令统一代码:to_rgb.py
(对数据集中的图像统一成rgb格式并进行统一规范命名)
2、算法网络Mobilenet
概述:
Mobilenet是专为移动设备和嵌入式系统设计的轻量化卷积神经网络。其主要特点在于采用了深度可分离卷积(Depthwise Separable Convolution)来减少计算量和参数数量,从而在资源受限的环境下实现高效的图像分类和识别。
算法代码为:models文件夹下的mobilenet.py
"""mobilenet in pytorch[1] Andrew G. Howard, Menglong Zhu, Bo Chen, Dmitry Kalenichenko, Weijun Wang, Tobias Weyand, Marco Andreetto, Hartwig AdamMobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applicationshttps://arxiv.org/abs/1704.04861
"""import torch
import torch.nn as nnclass DepthSeperabelConv2d(nn.Module):def __init__(self, input_channels, output_channels, kernel_size, **kwargs):super().__init__()self.depthwise = nn.Sequential(nn.Conv2d(input_channels,input_channels,kernel_size,groups=input_channels,**kwargs),nn.BatchNorm2d(input_channels),nn.ReLU(inplace=True))self.pointwise = nn.Sequential(nn.Conv2d(input_channels, output_channels, 1),nn.BatchNorm2d(output_channels),nn.ReLU(inplace=True))def forward(self, x):x = self.depthwise(x)x = self.pointwise(x)return xclass BasicConv2d(nn.Module):def __init__(self, input_channels, output_channels, kernel_size, **kwargs):super().__init__()self.conv = nn.Conv2d(input_channels, output_channels, kernel_size, **kwargs)self.bn = nn.BatchNorm2d(output_channels)self.relu = nn.ReLU(inplace=True)def forward(self, x):x = self.conv(x)x = self.bn(x)x = self.relu(x)return xclass MobileNet(nn.Module):def __init__(self, width_multiplier=1, class_num=100):super().__init__()alpha = width_multiplierself.stem = nn.Sequential(BasicConv2d(3, int(32 * alpha), 3, padding=1, bias=False),DepthSeperabelConv2d(int(32 * alpha),int(64 * alpha),3,padding=1,bias=False))#downsampleself.conv1 = nn.Sequential(DepthSeperabelConv2d(int(64 * alpha),int(128 * alpha),3,stride=2,padding=1,bias=False),DepthSeperabelConv2d(int(128 * alpha),int(128 * alpha),3,padding=1,bias=False))#downsampleself.conv2 = nn.Sequential(DepthSeperabelConv2d(int(128 * alpha),int(256 * alpha),3,stride=2,padding=1,bias=False),DepthSeperabelConv2d(int(256 * alpha),int(256 * alpha),3,padding=1,bias=False))#downsampleself.conv3 = nn.Sequential(DepthSeperabelConv2d(int(256 * alpha),int(512 * alpha),3,stride=2,padding=1,bias=False),DepthSeperabelConv2d(int(512 * alpha),int(512 * alpha),3,padding=1,bias=False),DepthSeperabelConv2d(int(512 * alpha),int(512 * alpha),3,padding=1,bias=False),DepthSeperabelConv2d(int(512 * alpha),int(512 * alpha),3,padding=1,bias=False),DepthSeperabelConv2d(int(512 * alpha),int(512 * alpha),3,padding=1,bias=False),DepthSeperabelConv2d(int(512 * alpha),int(512 * alpha),3,padding=1,bias=False))#downsampleself.conv4 = nn.Sequential(DepthSeperabelConv2d(int(512 * alpha),int(1024 * alpha),3,stride=2,padding=1,bias=False),DepthSeperabelConv2d(int(1024 * alpha),int(1024 * alpha),3,padding=1,bias=False))self.fc = nn.Linear(int(1024 * alpha), class_num)self.avg = nn.AdaptiveAvgPool2d(1)def forward(self, x):x = self.stem(x)x = self.conv1(x)x = self.conv2(x)x = self.conv3(x)x = self.conv4(x)x = self.avg(x)x = x.view(x.size(0), -1)x = self.fc(x)return xdef mobilenet(alpha=1, class_num=67):return MobileNet(alpha, class_num)
3、网络模型训练
训练代码为:train.py
- 超参数设置:输入图像尺寸:224×224。
- 优化器和损失函数:采用AdamW优化器,学习率设定为0.0001,损失函数为交叉熵损失函数。
- 训练轮数(Epoch):模型训练共进行了300个epoch,每个epoch结束时,计算训练集和验证集的准确率,保存验证集准确率最高时的模型。(可自行修改)
- 批次大小(Batch Size):16。(可自行修改)
- 学习率衰减:在训练过程中,采用学习率衰减策略,确保在模型逐渐收敛的过程中保持适当的更新步长。
import os
import argparseimport torch
import torch.optim as optim
import matplotlib.pyplot as plt
from torch.utils.tensorboard import SummaryWriter
from torchvision import transformsfrom my_dataset import MyDataSet
from models.mobilenet import mobilenet as create_model
from utils import read_split_data, train_one_epoch, evaluatedef draw(train, val, ca):plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.rcParams['axes.unicode_minus'] = Falseplt.cla() # 清空之前绘图数据plt.title('精确度曲线图' if ca == "acc" else '损失值曲线图')plt.plot(train, label='train_{}'.format(ca))plt.plot(val, label='val_{}'.format(ca))plt.legend()plt.grid()plt.savefig('精确度曲线图' if ca == "acc" else '损失值曲线图')# plt.show()def main(args):device = torch.device(args.device if torch.cuda.is_available() else "cpu")if os.path.exists("./weights") is False:os.makedirs("./weights")tb_writer = SummaryWriter()train_images_path, train_images_label, val_images_path, val_images_label = read_split_data(args.data_path)img_size = 224data_transform = {"train": transforms.Compose([transforms.RandomResizedCrop(img_size),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),"val": transforms.Compose([transforms.Resize(int(img_size * 1.143)),transforms.CenterCrop(img_size),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}# 实例化训练数据集train_dataset = MyDataSet(images_path=train_images_path,images_class=train_images_label,transform=data_transform["train"])# 实例化验证数据集val_dataset = MyDataSet(images_path=val_images_path,images_class=val_images_label,transform=data_transform["val"])batch_size = args.batch_sizenw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8]) # number of workersprint('Using {} dataloader workers every process'.format(nw))train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=batch_size,shuffle=True,pin_memory=True,num_workers=nw,collate_fn=train_dataset.collate_fn)val_loader = torch.utils.data.DataLoader(val_dataset,batch_size=batch_size,shuffle=False,pin_memory=True,num_workers=nw,collate_fn=val_dataset.collate_fn)model = create_model(class_num=67).to(device)if args.weights != "":assert os.path.exists(args.weights), "weights file: '{}' not exist.".format(args.weights)model.load_state_dict(torch.load(args.weights, map_location=device))if args.freeze_layers:for name, para in model.named_parameters():# 除head外,其他权重全部冻结if "head" not in name:para.requires_grad_(False)else:print("training {}".format(name))pg = [p for p in model.parameters() if p.requires_grad]optimizer = optim.AdamW(pg, lr=args.lr, weight_decay=5E-2)for epoch in range(args.epochs):# traintrain_loss, train_acc = train_one_epoch(model=model,optimizer=optimizer,data_loader=train_loader,device=device,epoch=epoch)# validateval_loss, val_acc = evaluate(model=model,data_loader=val_loader,device=device,epoch=epoch)train_acc_list.append(train_acc)train_loss_list.append(train_loss)val_acc_list.append(val_acc)val_loss_list.append(val_loss)tags = ["train_loss", "train_acc", "val_loss", "val_acc", "learning_rate"]tb_writer.add_scalar(tags[0], train_loss, epoch)tb_writer.add_scalar(tags[1], train_acc, epoch)tb_writer.add_scalar(tags[2], val_loss, epoch)tb_writer.add_scalar(tags[3], val_acc, epoch)tb_writer.add_scalar(tags[4], optimizer.param_groups[0]["lr"], epoch)if val_acc == max(val_acc_list):print('save-best-epoch:{}'.format(epoch))with open('loss.txt', 'w') as fb:fb.write(str(train_loss) + ',' + str(train_acc) + ',' + str(val_loss) + ',' + str(val_acc))torch.save(model.state_dict(), "./weights/plant-best-epoch.pth")if __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument('--num_classes', type=int, default=67)parser.add_argument('--epochs', type=int, default=300)parser.add_argument('--batch-size', type=int, default=16)parser.add_argument('--lr', type=float, default=0.0001)# 数据集所在根目录parser.add_argument('--data-path', type=str,default="all_data")# 预训练权重路径,如果不想载入就设置为空字符parser.add_argument('--weights', type=str, default='',help='initial weights path')# 是否冻结权重parser.add_argument('--freeze-layers', type=bool, default=False)parser.add_argument('--device', default='cuda:0', help='device id (i.e. 0 or 0,1 or cpu)')opt = parser.parse_args()train_loss_list = []train_acc_list = []val_loss_list = []val_acc_list = []main(opt)draw(train_acc_list, val_acc_list, 'acc')draw(train_loss_list, val_loss_list, 'loss')
开始训练:
在all_data中准备好数据集,并设置好超参数后,即可开始运行train.py
成功运行效果展示:
1)会生成dataset.png数据集分布柱状图
2)pycharm下方实时显示相关训练日志
等待所有epoch训练完成后代码会自动停止,并在weights文件夹下生成训练好的模型pth文件,并生成准确率和损失值曲线图。
4、训练好的模型预测
无界面预测代码为:predict.py
import os
import jsonimport torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as pltfrom models.mobilenet import mobilenet as create_modeldef main(img_path):import osos.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")img_size = 224data_transform = transforms.Compose([transforms.Resize(int(img_size * 1.143)),transforms.CenterCrop(img_size),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])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 dimensionimg = torch.unsqueeze(img, dim=0)# read class_indictjson_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 = create_model(class_num=67).to(device)# load model weights 加载模型model_weight_path = "weights/plant-best-epoch.pth"model.load_state_dict(torch.load(model_weight_path, map_location=device))model.eval()#调用模型进行检测with torch.no_grad():# predict classoutput = torch.squeeze(model(img.to(device))).cpu()predict = torch.softmax(output, dim=0)predict_cla = torch.argmax(predict).numpy()for i in range(len(predict)):print("class: {:10} prob: {:.3}".format(class_indict[str(i)],predict[i].numpy()))# 返回检测结果和准确率res = class_indict[str(list(predict.numpy()).index(max(predict.numpy())))]num= "%.2f" % (max(predict.numpy()) * 100) + "%"print(res,num)return res,numif __name__ == '__main__':img_path = r"all_data\plant_2\3.png"main(img_path)
使用方法:
1)设置好训练好的模型权重路径
2)设置好要预测的图像的路径
直接右键运行即可,成功运行后会在pycharm下方生成预测结果数据
5、UI界面设计-pyqt5
对应代码文件:
1)ui.py 用于设置界面中控件的属性样式和显示的文本内容,可自行修改文本内容
2)主界面.py 用于设置界面中的相关按钮及动态的交互功能
3)plant_data.py 相关介绍及展示信息文本,可自行修改介绍信息
6、项目相关评价指标
1、准确率曲线图(训练后自动生成)
2、损失值曲线图(训练后自动生成)
3、混淆矩阵图
生成方式:训练完模型后,运行confusion_matrix.py文件,设置好使用的模型权重文件后,直接右键运行即可,等待模型进行预测生成
以上为本项目完整的构建实现流程步骤,更加详细的项目讲解视频如下:https://www.bilibili.com/video/BV19KtVeWE1d
(对程序使用,项目中各个文件作用,算法网络结构,所有程序代码等进行的细致讲解,时长1小时)
七、项目论文报告
本项目有配套的论文报告(9000字左右),部分截图如下:
八、版权说明及获取方式
1、项目获取方式
1)项目全套文件代码获取地址:
项目全套代码地址:https://yuanlitui.com/a/ac8
2)项目配套论文报告获取地址:
项目配套报告:https://yuanlitui.com/a/acu
2、项目版权说明及定制服务
本项目由本人 Smaller-孔 设计并开发
可提供服务如下:
1)项目功能及界面定制改进服务
如添加用户登录注册、语音播报、语音输入等系统功能,设计Web端界面,添加mysql数据库,替换数据集、算法、及算法改进对比等服务
2)售后服务:
获取项目后,按照配置视频进行配置,配置过程中出现问题可添加我咨询答疑,配置好后后续因产品自身代码问题无法正常使用,提供1月内免费售后(因使用者对代码或环境进行调整导致程序无法正常运行需有偿售后),项目文件中售后服务文档获取本人联系方式,提供后续售后及相关定制服务。
3)深度学习CV领域的毕设项目,项目及技术1v1辅导,开发等