1.研究背景与意义
项目参考AAAI Association for the Advancement of Artificial Intelligence
研究背景与意义
番茄是全球重要的蔬菜作物之一,具有广泛的经济和营养价值。然而,番茄病虫害的严重威胁导致了产量和质量的损失。因此,开发一种高效准确的番茄病虫害检测系统对于农业生产的可持续发展至关重要。
传统的番茄病虫害检测方法主要依赖于人工目视观察,这种方法存在着效率低、主观性强、易出错等问题。近年来,随着计算机视觉和深度学习技术的快速发展,基于图像处理和机器学习的自动化检测方法逐渐成为研究热点。
目前,基于深度学习的目标检测算法已经在图像识别领域取得了显著的成果。其中,YOLO(You Only Look Once)算法是一种快速、准确的目标检测算法,被广泛应用于各种物体检测任务中。然而,传统的YOLO算法在处理小目标和密集目标时存在一定的困难,且对于番茄病虫害这种细小的目标检测任务来说,其性能仍然有待提高。
为了解决上述问题,本研究提出了一种改进的番茄病虫害检测系统,即融合感受野注意力卷积(RFAConv)改进YOLOv8。该系统通过引入感受野注意力机制,能够自适应地调整网络对不同尺度目标的关注程度,从而提高小目标和密集目标的检测性能。此外,通过融合感受野注意力卷积,可以更好地捕捉番茄病虫害的细节特征,提高检测的准确性和鲁棒性。
本研究的意义主要体现在以下几个方面:
-
提高番茄病虫害检测的准确性:传统的目标检测算法在处理小目标和密集目标时存在一定的困难,而融合感受野注意力卷积改进的YOLOv8算法能够有效地提高小目标和密集目标的检测准确性,从而提高番茄病虫害检测的准确性。
-
提高番茄病虫害检测的效率:传统的人工目视观察方法效率低下,而基于深度学习的自动化检测方法能够实现快速、准确的检测。融合感受野注意力卷积改进的YOLOv8算法具有较高的计算效率,能够在保证准确性的同时提高检测的效率。
-
促进农业生产的可持续发展:番茄病虫害对番茄产量和质量造成了严重的影响,通过开发高效准确的番茄病虫害检测系统,可以及时发现和控制病虫害,减少损失,提高农业生产的可持续发展能力。
总之,融合感受野注意力卷积改进的YOLOv8算法在番茄病虫害检测领域具有重要的研究意义和应用价值。通过提高检测的准确性和效率,可以为农业生产提供有效的技术支持,促进农业的可持续发展。
2.图片演示
3.视频演示
番茄病虫害检测系统:融合感受野注意力卷积(RFAConv)改进YOLOv8_哔哩哔哩_bilibili
4.数据集的采集&标注和整理
图片的收集
首先,我们需要收集所需的图片。这可以通过不同的方式来实现,例如使用现有的公开数据集TdDatasets。
labelImg是一个图形化的图像注释工具,支持VOC和YOLO格式。以下是使用labelImg将图片标注为VOC格式的步骤:
(1)下载并安装labelImg。
(2)打开labelImg并选择"Open Dir"来选择你的图片目录。
(3)为你的目标对象设置标签名称。
(4)在图片上绘制矩形框,选择对应的标签。
(5)保存标注信息,这将在图片目录下生成一个与图片同名的XML文件。
(6)重复此过程,直到所有的图片都标注完毕。
由于YOLO使用的是txt格式的标注,我们需要将VOC格式转换为YOLO格式。可以使用各种转换工具或脚本来实现。
下面是一个简单的方法是使用Python脚本,该脚本读取XML文件,然后将其转换为YOLO所需的txt格式。
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
classes = [] # 初始化为空列表
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
def convert(size, box):
dw = 1. / size[0]
dh = 1. / size[1]
x = (box[0] + box[1]) / 2.0
y = (box[2] + box[3]) / 2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
def convert_annotation(image_id):
in_file = open('./label_xml\%s.xml' % (image_id), encoding='UTF-8')
out_file = open('./label_txt\%s.txt' % (image_id), 'w') # 生成txt格式文件
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
cls = obj.find('name').text
if cls not in classes:
classes.append(cls) # 如果类别不存在,添加到classes列表中
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
xml_path = os.path.join(CURRENT_DIR, './label_xml/')
# xml list
img_xmls = os.listdir(xml_path)
for img_xml in img_xmls:
label_name = img_xml.split('.')[0]
print(label_name)
convert_annotation(label_name)
print("Classes:") # 打印最终的classes列表
print(classes) # 打印最终的classes列表
整理数据文件夹结构
我们需要将数据集整理为以下结构:
-----data
|-----train
| |-----images
| |-----labels
|
|-----valid
| |-----images
| |-----labels
|
|-----test
|-----images
|-----labels
确保以下几点:
所有的训练图片都位于data/train/images目录下,相应的标注文件位于data/train/labels目录下。
所有的验证图片都位于data/valid/images目录下,相应的标注文件位于data/valid/labels目录下。
所有的测试图片都位于data/test/images目录下,相应的标注文件位于data/test/labels目录下。
这样的结构使得数据的管理和模型的训练、验证和测试变得非常方便。
模型训练
Epoch gpu_mem box obj cls labels img_size
1/200 20.8G 0.01576 0.01955 0.007536 22 1280: 100%|██████████| 849/849 [14:42<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:14<00:00, 2.87it/s]
all 3395 17314 0.994 0.957 0.0957 0.0843
Epoch gpu_mem box obj cls labels img_size
2/200 20.8G 0.01578 0.01923 0.007006 22 1280: 100%|██████████| 849/849 [14:44<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:12<00:00, 2.95it/s]
all 3395 17314 0.996 0.956 0.0957 0.0845
Epoch gpu_mem box obj cls labels img_size
3/200 20.8G 0.01561 0.0191 0.006895 27 1280: 100%|██████████| 849/849 [10:56<00:00, 1.29it/s]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|███████ | 187/213 [00:52<00:00, 4.04it/s]
all 3395 17314 0.996 0.957 0.0957 0.0845
5.核心代码讲解
5.1 predict.py
python
from ultralytics.engine.predictor import BasePredictor
from ultralytics.engine.results import Results
from ultralytics.utils import ops
class DetectionPredictor(BasePredictor):
def postprocess(self, preds, img, orig_imgs):
preds = ops.non_max_suppression(preds,
self.args.conf,
self.args.iou,
agnostic=self.args.agnostic_nms,
max_det=self.args.max_det,
classes=self.args.classes)
if not isinstance(orig_imgs, list):
orig_imgs = ops.convert_torch2numpy_batch(orig_imgs)
results = []
for i, pred in enumerate(preds):
orig_img = orig_imgs[i]
pred[:, :4] = ops.scale_boxes(img.shape[2:], pred[:, :4], orig_img.shape)
img_path = self.batch[0][i]
results.append(Results(orig_img, path=img_path, names=self.model.names, boxes=pred))
return results
这段代码定义了一个名为DetectionPredictor
的类,该类继承自BasePredictor
类。其中,postprocess
方法用于对预测结果进行后处理,并返回一个Results
对象的列表。在postprocess
方法中,首先使用ops.non_max_suppression
函数对预测结果进行非最大值抑制处理,然后根据输入图片的形状和原始图片的形状对预测框进行缩放,最后将处理后的结果封装为Results
对象并添加到结果列表中。
该程序文件名为predict.py,是一个用于预测的程序文件。该文件使用了Ultralytics YOLO库,采用AGPL-3.0许可证。
该文件定义了一个名为DetectionPredictor的类,该类继承自BasePredictor类,用于基于检测模型进行预测。
该类中包含了一个postprocess方法,用于对预测结果进行后处理,并返回一个Results对象的列表。在postprocess方法中,首先对预测结果进行非最大抑制处理,根据一些参数进行筛选,得到最终的预测结果。然后,将输入的图像转换为numpy数组,并根据预测结果对边界框进行缩放。最后,将原始图像、图像路径、类别名称和预测框作为参数,创建一个Results对象,并将其添加到结果列表中。
该文件还包含了一个示例代码,展示了如何使用DetectionPredictor类进行预测。首先导入所需的库和模块,然后创建一个DetectionPredictor对象,并使用predict_cli方法进行预测。
总结起来,该程序文件是一个用于基于检测模型进行预测的工具类,其中包含了预测结果的后处理方法和示例代码。
5.2 train.py
python
from copy import copy
import numpy as np
from ultralytics.data import build_dataloader, build_yolo_dataset
from ultralytics.engine.trainer import BaseTrainer
from ultralytics.models import yolo
from ultralytics.nn.tasks import DetectionModel
from ultralytics.utils import LOGGER, RANK
from ultralytics.utils.torch_utils import de_parallel, torch_distributed_zero_first
class DetectionTrainer(BaseTrainer):
def build_dataset(self, img_path, mode='train', batch=None):
gs = max(int(de_parallel(self.model).stride.max() if self.model else 0), 32)
return build_yolo_dataset(self.args, img_path, batch, self.data, mode=mode, rect=mode == 'val', stride=gs)
def get_dataloader(self, dataset_path, batch_size=16, rank=0, mode='train'):
assert mode in ['train', 'val']
with torch_distributed_zero_first(rank):
dataset = self.build_dataset(dataset_path, mode, batch_size)
shuffle = mode == 'train'
if getattr(dataset, 'rect', False) and shuffle:
LOGGER.warning("WARNING ⚠️ 'rect=True' is incompatible with DataLoader shuffle, setting shuffle=False")
shuffle = False
workers = 0
return build_dataloader(dataset, batch_size, workers, shuffle, rank)
def preprocess_batch(self, batch):
batch['img'] = batch['img'].to(self.device, non_blocking=True).float() / 255
return batch
def set_model_attributes(self):
self.model.nc = self.data['nc']
self.model.names = self.data['names']
self.model.args = self.args
def get_model(self, cfg=None, weights=None, verbose=True):
model = DetectionModel(cfg, nc=self.data['nc'], verbose=verbose and RANK == -1)
if weights:
model.load(weights)
return model
def get_validator(self):
self.loss_names = 'box_loss', 'cls_loss', 'dfl_loss'
return yolo.detect.DetectionValidator(self.test_loader, save_dir=self.save_dir, args=copy(self.args))
def label_loss_items(self, loss_items=None, prefix='train'):
keys = [f'{prefix}/{x}' for x in self.loss_names]
if loss_items is not None:
loss_items = [round(float(x), 5) for x in loss_items]
return dict(zip(keys, loss_items))
else:
return keys
def progress_string(self):
return ('\n' + '%11s' *
(4 + len(self.loss_names))) % ('Epoch', 'GPU_mem', *self.loss_names, 'Instances', 'Size')
def plot_training_samples(self, batch, ni):
plot_images(images=batch['img'],
batch_idx=batch['batch_idx'],
cls=batch['cls'].squeeze(-1),
bboxes=batch['bboxes'],
paths=batch['im_file'],
fname=self.save_dir / f'train_batch{ni}.jpg',
on_plot=self.on_plot)
def plot_metrics(self):
plot_results(file=self.csv, on_plot=self.on_plot)
def plot_training_labels(self):
boxes = np.concatenate([lb['bboxes'] for lb in self.train_loader.dataset.labels], 0)
cls = np.concatenate([lb['cls'] for lb in self.train_loader.dataset.labels], 0)
plot_labels(boxes, cls.squeeze(), names=self.data['names'], save_dir=self.save_dir, on_plot=self.on_plot)
这个程序文件是一个用于训练目标检测模型的程序。它使用了Ultralytics YOLO库,该库提供了训练和验证目标检测模型的功能。
该程序文件定义了一个名为DetectionTrainer的类,该类继承自BaseTrainer类。DetectionTrainer类包含了构建数据集、构建数据加载器、预处理批次、设置模型属性等方法,用于训练目标检测模型。
在程序的主函数中,首先定义了一些参数,包括模型文件路径、数据文件路径和训练轮数等。然后创建了一个DetectionTrainer对象,并调用其train方法开始训练。
总体来说,这个程序文件使用Ultralytics YOLO库提供的功能来训练目标检测模型。
5.3 backbone\revcol.py
python
import torch
import torch.nn as nn
class RevCol(nn.Module):
def __init__(self, kernel='C2f', channels=[32, 64, 96, 128], layers=[2, 3, 6, 3], num_subnet=5, save_memory=True) -> None:
super().__init__()
self.num_subnet = num_subnet
self.channels = channels
self.layers = layers
self.stem = Conv(3, channels[0], k=4, s=4, p=0)
for i in range(num_subnet):
first_col = True if i == 0 else False
self.add_module(f'subnet{str(i)}', SubNet(channels, layers, kernel, first_col, save_memory=save_memory))
self.channel = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]
def forward(self, x):
c0, c1, c2, c3 = 0, 0, 0, 0
x = self.stem(x)
for i in range(self.num_subnet):
c0, c1, c2, c3 = getattr(self, f'subnet{str(i)}')(x, c0, c1, c2, c3)
return [c0, c1, c2, c3]
该程序文件名为backbone\revcol.py,是一个用于深度学习的神经网络模型的实现。该文件包含了多个模块和函数,用于定义网络的结构和实现前向传播和反向传播的过程。
该文件中定义了以下模块和函数:
-
RevCol
类:该类是整个神经网络模型的主要部分。它包含了多个子网络(SubNet
),每个子网络由多个层级(Level
)组成。每个层级由多个卷积层(Conv
)组成。RevCol
类的前向传播过程通过调用子网络的前向传播函数来实现。 -
SubNet
类:该类是一个子网络,由多个层级(Level
)组成。每个层级由多个卷积层(Conv
)组成。SubNet
类的前向传播过程通过调用层级的前向传播函数来实现。 -
Level
类:该类是一个层级,由多个卷积层(Conv
)组成。Level
类的前向传播过程通过调用卷积层的前向传播函数来实现。 -
Conv
类:该类是一个卷积层,用于实现卷积操作。 -
ReverseFunction
类:该类是一个自定义的反向传播函数,用于实现反向传播过程。
该文件还定义了一些辅助函数和变量,用于处理张量的状态和梯度等操作。
总体来说,该程序文件实现了一个具有多个子网络和层级的神经网络模型,用于进行深度学习任务。
5.4 backbone\SwinTransformer.py
python
class Mlp(nn.Module):
""" Multilayer perceptron."""
def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
super().__init__()
out_features = out_features or in_features
hidden_features = hidden_features or in_features
self.fc1 = nn.Linear(in_features, hidden_features)
self.act = act_layer()
self.fc2 = nn.Linear(hidden_features, out_features)
self.drop = nn.Dropout(drop)
def forward(self, x):
x = self.fc1(x)
x = self.act(x)
x = self.drop(x)
x = self.fc2(x)
x = self.drop(x)
return x
def window_partition(x, window_size):
"""
Args:
x: (B, H, W, C)
window_size (int): window size
Returns:
windows: (num_windows*B, window_size, window_size, C)
"""
B, H, W, C = x.shape
x = x.view(B, H // window_size, window_size, W // window_size, window_size, C)
windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C)
return windows
def window_reverse(windows, window_size, H, W):
"""
Args:
windows: (num_windows*B, window_size, window_size, C)
window_size (int): Window size
H (int): Height of image
W (int): Width of image
Returns:
x: (B, H, W, C)
"""
B = int(windows.shape[0] / (H * W / window_size / window_size))
x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1)
x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1)
return x
class WindowAttention(nn.Module):
""" Window based multi-head self attention (W-MSA) module with relative position bias.
It supports both of shifted and non-shifted window.
Args:
dim (int): Number of input channels.
window_size (tuple[int]): The height and width of the window.
num_heads (int): Number of attention heads.
qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set
attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0
proj_drop (float, optional): Dropout ratio of output. Default: 0.0
"""
def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.):
super().__init__()
self.dim = dim
self.window_size = window_size # Wh, Ww
self.num_heads = num_heads
head_dim = dim // num_heads
self.scale = qk
该程序文件是Swin Transformer模型的一个实现。Swin Transformer是一种基于窗口的多头自注意力模块,用于图像分类任务。该模型包含了多个Swin Transformer块和Patch Merging层。
SwinTransformer_Tiny是一个小型的Swin Transformer模型,用于处理输入特征。模型的主要组成部分包括:
- Mlp:多层感知机模块,用于将输入特征映射到隐藏特征空间。
- window_partition和window_reverse:用于将输入特征划分为窗口,并将窗口重新组合成特征。
- WindowAttention:基于窗口的多头自注意力模块,用于计算特征之间的相似度。
- SwinTransformerBlock:Swin Transformer块,包含了窗口注意力模块、多层感知机模块和残差连接。
- PatchMerging:用于将输入特征的分辨率降低一半。
该程序文件实现了SwinTransformer_Tiny模型的前向传播函数,包括窗口划分、窗口注意力计算、窗口合并等操作。
5.5 backbone\VanillaNet.py
python
import torch
import torch.nn as nn
from timm.layers import weight_init
class activation(nn.ReLU):
def __init__(self, dim, act_num=3, deploy=False):
super(activation, self).__init__()
self.deploy = deploy
self.weight = torch.nn.Parameter(torch.randn(dim, 1, act_num*2 + 1, act_num*2 + 1))
self.bias = None
self.bn = nn.BatchNorm2d(dim, eps=1e-6)
self.dim = dim
self.act_num = act_num
weight_init.trunc_normal_(self.weight, std=.02)
def forward(self, x):
if self.deploy:
return torch.nn.functional.conv2d(
super(activation, self).forward(x),
self.weight, self.bias, padding=(self.act_num*2 + 1)//2, groups=self.dim)
else:
return self.bn(torch.nn.functional.conv2d(
super(activation, self).forward(x),
self.weight, padding=self.act_num
该程序文件名为backbone\VanillaNet.py,是一个用于构建VanillaNet模型的Python代码文件。
该文件定义了以下类和函数:
- activation类:继承自nn.ReLU类,用于定义激活函数。
- Block类:继承自nn.Module类,用于定义模型的基本块。
- VanillaNet类:继承自nn.Module类,用于定义VanillaNet模型。
- update_weight函数:用于更新模型的权重。
- vanillanet_5到vanillanet_13_x1_5_ada_pool函数:用于创建不同版本的VanillaNet模型。
其中,VanillaNet类是该文件的主要内容,它定义了VanillaNet模型的结构和前向传播方法。根据输入的参数,可以创建不同版本的VanillaNet模型。
在文件的最后,通过调用vanillanet
5.6 extra_modules\rep_block.py
python
class DiverseBranchBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size,
stride=1, padding=None, dilation=1, groups=1,
internal_channels_1x1_3x3=None,
deploy=False, single_init=False):
super(DiverseBranchBlock, self).__init__()
self.deploy = deploy
self.nonlinear = Conv.default_act
self.kernel_size = kernel_size
self.out_channels = out_channels
self.groups = groups
if padding is None:
padding = autopad(kernel_size, padding, dilation)
assert padding == kernel_size // 2
if deploy:
self.dbb_reparam = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride,
padding=padding, dilation=dilation, groups=groups, bias=True)
else:
self.dbb_origin = conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups)
self.dbb_avg = nn.Sequential()
if groups < out_channels:
self.dbb_avg.add_module('conv',
nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1,
stride=1, padding=0, groups=groups, bias=False))
self.dbb_avg.add_module('bn', BNAndPadLayer(pad_pixels=padding, num_features=out_channels))
self.dbb_avg.add_module('avg', nn.AvgPool2d(kernel_size=kernel_size, stride=stride, padding=0))
self.dbb_1x1 = conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=stride,
padding=0, groups=groups)
else:
self.dbb_avg.add_module('avg', nn.AvgPool2d(kernel_size=kernel_size, stride=stride, padding=padding))
self.dbb_avg.add_module('avgbn', nn.BatchNorm2d(out_channels))
if internal_channels_1x1_3x3 is None:
internal_channels_1x1_3x3 = in_channels if groups < out_channels else 2 * in_channels # For mobilenet, it is better to have 2X internal channels
self.dbb_1x1_kxk = nn.Sequential()
if internal_channels_1x1_3x3 == in_channels:
self.dbb_1x1_kxk.add_module('idconv1', IdentityBasedConv1x1(channels=in_channels, groups=groups))
else:
self.dbb_1x1_kxk.add_module('conv1', nn.Conv2d(in_channels=in_channels, out_channels=internal_channels_1x1_3x3,
kernel_size=1, stride=1, padding=0, groups=groups, bias=False))
self.dbb_1x1_kxk.add_module('bn1', BNAndPadLayer(pad_pixels=padding, num_features=internal_channels_1x1_3x3, affine=True))
self.dbb_1x1_kxk.add_module('conv2', nn.Conv2d(in_channels=internal_channels_1x1_3x3, out_channels=out_channels,
kernel_size=kernel_size, stride=stride, padding=0, groups=groups, bias=False))
self
该程序文件是一个名为"rep_block.py"的模块文件,主要包含了一个名为"DiverseBranchBlock"的类。该类是一个继承自nn.Module的模型类,用于定义一个多样分支块。
该模块文件中还包含了一些辅助函数和类,如"transI_fusebn"、"transII_addbranch"、"transIII_1x1_kxk"等用于处理卷积和批归一化操作的函数,以及"conv_bn"、"IdentityBasedConv1x1"、"BNAndPadLayer"等用于定义卷积、批归一化和填充操作的类。
"DiverseBranchBlock"类的构造函数接受一些参数,包括输入通道数、输出通道数、卷积核大小、步长、填充、膨胀率、分组数等。在构造函数中,根据是否部署模型,会选择不同的操作路径。如果是部署模型,则使用nn.Conv2d定义一个卷积层;否则,使用conv_bn函数定义一个卷积和批归一化的组合层,并定义了其他一些辅助操作层。
该类还包含了一些方法,如"get_equivalent_kernel_bias"用于获取等效的卷积核和偏置,"switch_to_deploy"用于切换到部署模式,"forward"用于前向传播计算输出,"init_gamma"用于初始化gamma参数,"single_init"用于单一初始化参数。
总体来说,该程序文件定义了一个多样分支块的模型类,提供了一些辅助函数和类来支持模型的构建和操作。
6.系统整体结构
根据以上分析,该程序是一个番茄病虫害检测系统的项目,主要包含了训练和预测两个主要功能。整体构架如下:
- models:包含了模型的定义和实现,如common.py、experimental.py、tf.py、yolo.py等文件。
- utils:包含了各种辅助功能和工具函数,如activations.py、augmentations.py、autoanchor.py、autobatch.py等文件。
- extra_modules:包含了一些额外的模块和功能,如rep_block.py、RFAConv.py等文件。
- backbone:包含了不同的神经网络模型的实现,如revcol.py、SwinTransformer.py、VanillaNet.py等文件。
- code:包含了训练和预测的主要代码文件,如train.py、predict.py等文件。
下面是每个文件的功能概述:
文件路径 | 功能概述 |
---|---|
predict.py | 用于预测的程序文件,包含预测结果的后处理方法和示例代码。 |
train.py | 用于训练目标检测模型的程序文件,包含数据集构建、数据加载器构建、模型属性设置等方法。 |
backbone/revcol.py | 实现了一个深度学习的神经网络模型,包含多个子网络和层级。 |
backbone/SwinTransformer.py | 实现了Swin Transformer模型,用于图像分类任务。 |
backbone/VanillaNet.py | 实现了VanillaNet模型,用于构建一个基本的神经网络模型。 |
extra_modules/rep_block.py | 定义了一个多样分支块的模型类,提供了辅助函数和类来支持模型的构建和操作。 |
extra_modules/RFAConv.py | 实现了RFAConv模块,用于改进YOLOv8目标检测系统。 |
extra_modules/init.py | 空文件,用于标识extra_modules为一个Python模块。 |
extra_modules/ops_dcnv3/setup.py | 用于安装DCNv3模块的配置文件。 |
extra_modules/ops_dcnv3/test.py | 用于测试DCNv3模块的功能的测试脚本。 |
extra_modules/ops_dcnv3/functions/dcnv3_func.py | 实现了DCNv3模块的功能函数。 |
extra_modules/ops_dcnv3/functions/init.py | 空文件,用于标识functions为一个Python模块。 |
extra_modules/ops_dcnv3/modules/dcnv3.py | 实现了DCNv3模块的模型类。 |
extra_modules/ops_dcnv3/modules/init.py | 空文件,用于标识modules为一个Python模块。 |
models/common.py | 包含了一些通用的模型定义和实用函数。 |
models/experimental.py | 包含了一些实验性的模型定义和实用函数。 |
models/tf.py | 包含了一些TensorFlow模型定义和实用函数。 |
models/yolo.py | 包含了YOLO模型的定义和实用函数。 |
models/init.py | 空文件,用于标识models为一个Python模块。 |
utils/activations.py | 包含了各种激活函数的定义。 |
utils/augmentations.py | 包含了数据增强的函数和类。 |
utils/autoanchor.py | 包含了自动锚框生成的函数和类。 |
utils/autobatch.py | 包含了自动批处理的函数和类。 |
utils/callbacks.py | 包含了一些回调函数的定义。 |
utils/datasets.py | 包含了数据集的定义和处理函数。 |
utils/downloads.py | 包含了文件下载的函数和类。 |
utils/general.py | 包含了一些通用的辅助函数。 |
utils/loss.py | 包含了损失函数的定义。 |
utils/metrics.py | 包含了评估指标的定义。 |
utils/plots.py | 包含了绘图函数的定义。 |
utils/torch_utils.py | 包含了一些PyTorch的辅助函数。 |
utils/init.py | 空文件,用于标识utils为一个Python模块。 |
utils/aws/resume.py | 包含了AWS上的恢复功能的函数和类。 |
utils/aws/init.py | 空文件,用于标识aws为一个Python模块。 |
utils/flask_rest_api/example_request.py | 包含了Flask REST API的示例请求函数。 |
7.YOLOv8简介
YOLOv8目标检测算法继承了YOLOv1系列的思考,是一种新型端到端的目标检测算法,尽管现在原始检测算法已经开源,但是鲜有发表的相关论文.YOLOv8的网络结构如图所示,主要可分为Input输入端、Backbone骨干神经网络、Neck 混合特征网络层和Head预测层网络共4个部分.
YOLO目标检测算法是一种端到端的One-Slage 目标检测算法,其核心思想是将图像按区域分块进行预测。YOLO将输入图像按照32x32的大小划分成若干个网格,例如416x416的图像将被划分为13x13个网格。当目标物体的中心位于某个网格内时,该网格就会负责输出该物体的边界框和类别置信度。每个网格可以预测多个边界框和多个目标类别,这些边界框和类别的数量可以根据需要进行设置。YOLO算法的输出是一个特征图,包含了每个网格对应的边界框和类别置信度的信息呵。本文采用YOLO最新的YOLOv8模型,其是2022年底发布的最新YOLO系列模型,采用全新的SOTA模型,全新的网络主干结构,如图1所示。
整个网络分为Backbone 骨干网络部分和Head头部网络部分。YOLOv8汲取了前几代网络的优秀特性,骨干网络和 Neck部分遵循CSP的思想,将YOLOv5中的C3模块被替换成了梯度流更丰富C2模块,去掉YOLOv5中 PAN-FPN上采样阶段中的卷积结构,将Backbone不同阶段输出的特征直接送入了上采样操作,模型提供了N/S/M/L/X尺度的不同大小模型,能够满足不同领域业界的需求。本文基于YOLOv8模型设计番茄病虫害检测系统,通过配置模型参数训练番茄图像,得到能够用于部署应用的最优模型。
8.感受野注意力卷积(RFAConv)
标准卷积操作回顾
标准的卷积操作是构造卷积神经网络的基本构件。它利用具有共享参数的滑动窗口提取特征信息,克服了全连通层构造神经网络固有的参数多、计算开销大的问题。设 X R∈C×H×W
表示输入特征图,其中C、H、W分别表示特征图的通道数、高度、宽度。为了清楚地演示卷积核的特征提取过程,我们使用 C = 1 的例子。从每个接受域滑块中提取特征信息的卷积运算可以表示为:
这里,Fi 表示计算后每个卷积滑块得到的值,Xi 表示每个滑块内对应位置的像素值,K表示卷积核,S表示卷积核中的参数个数,N表示接收域滑块的总数。可以看出,每个滑块内相同位置的 feature共享相同的参数Ki。因此,标准的卷积运算并不能捕捉到不同位置所带来的信息差异,这最终在一定程度上限制了卷积神经网络的性能。
空间注意力回顾
目前,空间注意机制是利用学习得到的注意图来突出每个特征的重要性。与前一节类似,这里以 C=1为例。突出关键特征的空间注意机制可以简单表述为:这里,Fi 表示加权运算后得到的值。xi 和Ai 表示输入特征图和学习到的注意图在不同位置的值,N为输入特征图的高和宽的乘积,表示像素值的总数。
空间注意与标准卷积运算
将注意力机制整合到卷积神经网络中,可以提高卷积神经网络的性能。通过对标准卷积运算和现有空间注意机制的研究,我们认为空间注意机制有效地克服了卷积神经网络固有的参数共享的局限性。目前卷积神经网络中最常用的核大小是 1 × 1和3 × 3。在引入空间注意机制后,提取特征的卷积操作可以是 1 × 1或3 × 3卷积操作。为了直观地展示这个过程,在 1 × 1卷积运算的前面插入了空间注意机制。通过注意图对输入特征图(Re-weight"×")进行加权运算,最后通过 1 × 1卷积运算提取接收域的滑块特征信息。整个过程可以简单地表示如下:
这里卷积核K仅代表一个参数值。如果取A i× ki 的值作为一种新的卷积核参数,有趣的是它解决了 1×1卷积运算提取特征时的参数共享问题。然而,关于空间注意机制的传说到此结束。当空间注意机制被插入到3×3卷积运算前面时。具体情况如下:
如上所述,如果取A的值 i × ki (4)式作为一种新的卷积核参数,完全解决了大规模卷积核的参数共享问题。然而,最重要的一点是,卷积核在提取每个接受域滑块的特征时,会共享一些特征。换句话说,每个接收域滑块内都有一个重叠。仔细分析后会发现A12= a21, a13 = a22, a15 = a24......,在这种情况下,每个滑动窗口共享空间注意力地图的权重。因此,空间注意机制没有考虑整个接受域的空间特征,不能有效地解决大规模卷积核的参数共享问题。因此,空间注意机制的有效性受到限制。
创新空间注意力和标准卷积操作
该博客提出解决了现有空间注意机制的局限性,为空间处理提供了一种创新的解决方案。受RFA的启发,一系列空间注意机制被开发出来,可以进一步提高卷积神经网络的性能。RFA可以看作是一个轻量级即插即用模块,RFA设计的卷积运算(RFAConv)可以代替标准卷积来提高卷积神经网络的性能。因此,我们预测空间注意机制与标准卷积运算的结合将继续发展,并在未来带来新的突破。
接受域空间特征:为了更好地理解接受域空间特征的概念,我们将提供相关的定义。接收域空间特征是专门为卷积核设计的,并根据核大小动态生成。如图1所示,以3×3卷积核为例。在图1中,"Spatial Feature"指的是原始的Feature map。"接受域空间特征"是空间特征变换后的特征图。
由不重叠的滑动窗口组成。当使用 3×3卷积内核提取特征时,接收域空间特征中的每个 3×3大小窗口代表一个接收域滑块。接受域注意卷积(RFAConv):针对接受域的空间特征,我们提出了接受域注意卷积(RFA)。该方法不仅强调了接收域滑块内不同特征的重要性,而且对接收域空间特征进行了优先排序。通过该方法,完全解决了卷积核参数共享的问题。接受域空间特征是根据卷积核的大小动态生成的,因此,RFA是卷积的固定组合,不能与卷积操作的帮助分离,卷积操作同时依赖于RFA来提高性能,因此我们提出了接受场注意卷积(RFAConv)。具有3×3大小的卷积核的RFAConv整体结构如图所示。
目前,最广泛使用的接受域特征提取方法是缓慢的。经过大量的研究,我们开发了一种快速的方法,用分组卷积来代替原来的方法。具体来说,我们利用相应大小的分组卷积来动态生成基于接受域大小的展开特征。尽管与原始的无参数方法(如PyTorch提供的nn.())相比,该方法增加了一些参数,但它的速度要快得多。注意:如前一节所述,当使用 3×3卷积内核提取特征时,接收域空间特征中的每个 3×3大小窗口表示一个接收域滑块。而利用快速分组卷积提取感受野特征后,将原始特征映射为新的特征。最近的研究表明。交互信息可以提高网络性能,如[40,41,42]所示。同样,对于RFAConv来说,通过交互接受域特征信息来学习注意图可以提高网络性能。然而,与每个接收域特征交互会导致额外的计算开销,因此为了最小化计算开销和参数的数量,我们使用AvgPool来聚合每个接收域特征的全局信息。然后,使用 1×1 组卷积操作进行信息交互。最后,我们使用softmax来强调每个特征在接受域特征中的重要性。一般情况下,RFA的计算可以表示为:
这里gi×i 表示一个大小为 i×i的分组卷积,k表示卷积核的大小,Norm表示归一化,X表示输入的特征图,F由注意图 a相乘得到 rf 与转换后的接受域空间特征 Frf。与CBAM和CA不同,RFA能够为每个接受域特征生成注意图。卷积神经网络的性能受到标准卷积操作的限制,因为卷积操作依赖于共享参数,对位置变化带来的信息差异不敏感。然而,RFAConv通过强调接收域滑块中不同特征的重要性,并对接收域空间特征进行优先级排序,可以完全解决这个问题。通过RFA得到的feature map是接受域空间特征,在"Adjust Shape"后没有重叠。因此,学习到的注意图将每个接受域滑块的特征信息聚合起来。换句话说,注意力地图不再共享在每个接受域滑块。这完全弥补了现有 CA和CBAM注意机制的不足。RFA为标准卷积内核提供了显著的好处。而在调整形状后,特征的高度和宽度是 k倍,需要进行 stride = k的k × k卷积运算来提取特征信息。RFA设计的卷积运算RFAConv为卷积带来了良好的增益,对标准卷积进行了创新。
此外,我们认为现有的空间注意机制应该优先考虑接受域空间特征,以提高网络性能。众所周知,基于自注意机制的网络模型[43,44,45]取得了很大的成功,因为它解决了卷积参数共享的问题,并对远程信息进行建模。然而,自注意机制也为模型引入了显著的计算开销和复杂性。我们认为,将现有的空间注意机制的注意力引导到接受场空间特征上,可以以类似于自我注意的方式解决长期信息的参数共享和建模问题。与自我关注相比,这种方法需要的参数和计算资源少得多。答案如下:(1)将以接收场空间特征为中心的空间注意机制与卷积相结合,消除了卷积参数共享的问题。(2)现有的空间注意机制已经考虑了远程信息,可以通过全局平均池或全局最大池的方式获取全局信息,其中明确考虑了远程信息。因此,我们设计了新的 CBAM和CA模型,称为RFCBAM和RFCA,它们专注于接受域空间特征。与RFA类似,使用最终的k × k stride = k 的卷积运算来提取特征信息。这两种新的卷积方法的具体结构如图 3所示,我们称这两种新的卷积操作为 RFCBAMConv和RFCAConv。与原来的CBAM相比,我们在RFCBAM中使用SE attention来代替CAM。因为这样可以减少计算开销。此外,在RFCBAM中,通道注意和空间注意不是分开执行的。相反,它们是同时加权的,使得每个通道获得的注意力地图是不同的。
9.系统整合
参考博客《番茄病虫害检测系统:融合感受野注意力卷积(RFAConv)改进YOLOv8》
10.参考文献
[1]林文树,张金生,何乃磊.基于改进YOLO v4的落叶松毛虫侵害树木实时检测方法[J].农业机械学报.2023,54(4).DOI:10.6041/j.issn.1000-1298.2023.04.031 .
[2]银霞,叶绍泽.基于卷积神经网络的嵌入式排水管道缺陷检测系统[J].城市勘测.2023,(2).DOI:10.3969/j.issn.1672-8262.2023.02.043 .
[3]安小松,宋竹平,梁千月,等.基于CNN-Transformer的视觉缺陷柑橘分选方法[J].华中农业大学学报.2022,41(4).
[4]王道累,李明山,姚勇,等.改进SSD的光伏组件热斑缺陷检测方法[J].太阳能学报.2023,44(4).DOI:10.19912/j.0254-0096.tynxb.2021-1470 .
[5]胡定一.基于深度学习的缺陷柑橘分类识别检测方法研究[D].2021.