当前位置:   article > 正文

【热门创新点】引入可变形卷积DCN改进YOLOv5的鱼虾残饵量检测系统_dcnv3结合yolov5

dcnv3结合yolov5

1.研究背景与意义

项目参考AAAI Association for the Advancement of Artificial Intelligence

研究背景与意义:

近年来,随着计算机视觉技术的快速发展,目标检测成为了计算机视觉领域的一个重要研究方向。目标检测技术在许多领域中具有广泛的应用,如智能交通、安防监控、无人驾驶等。在水产养殖领域,鱼虾残饵量检测是一个重要的任务,可以帮助养殖户实时监测饵料的消耗情况,从而更好地管理养殖过程。

目前,基于深度学习的目标检测方法已经取得了很大的进展,其中YOLO(You Only Look Once)系列是一种非常流行的目标检测算法。YOLOv5是YOLO系列的最新版本,相比于之前的版本,YOLOv5在精度和速度上都有了显著的提升。然而,对于鱼虾残饵量检测这样的特定任务,YOLOv5仍然存在一些问题。

首先,YOLOv5在处理小目标时存在一定的困难。由于鱼虾残饵量通常比较小,因此对于小目标的检测,YOLOv5的性能可能会受到限制。其次,YOLOv5在处理目标形变时也存在一定的挑战。在水产养殖过程中,鱼虾残饵量的形状可能会因为各种因素而发生变化,如水流、饵料浸泡等。这就要求目标检测算法能够对目标的形变进行有效的识别和检测。

为了解决上述问题,本研究引入了可变形卷积(Deformable Convolutional Networks,DCN)来改进YOLOv5的鱼虾残饵量检测系统。可变形卷积是一种能够自适应地调整卷积核形状的卷积操作,可以有效地捕捉目标的形变信息。通过引入DCN,我们希望能够提高YOLOv5在小目标和形变目标上的检测性能,从而更准确地检测鱼虾残饵量。

本研究的意义主要体现在以下几个方面:

首先,改进YOLOv5的鱼虾残饵量检测系统可以提高水产养殖过程中的管理效率。通过实时监测饵料的消耗情况,养殖户可以更好地掌握饵料的使用情况,从而合理调整饵料的投放量,减少浪费,提高养殖效益。

其次,本研究的方法可以为其他类似的目标检测任务提供借鉴。鱼虾残饵量检测是一种特定的目标检测任务,但是目标形变和小目标检测是目标检测领域中普遍存在的问题。通过引入可变形卷积来改进目标检测算法,可以为其他类似任务的解决方案提供参考和借鉴。

最后,本研究的成果对于推动水产养殖行业的智能化发展具有积极的意义。随着人工智能技术的不断进步,智能化养殖已经成为水产养殖行业的发展趋势。改进鱼虾残饵量检测系统可以为智能化养殖提供基础支持,为养殖户提供更加智能、高效的管理方式。

综上所述,引入可变形卷积DCN改进YOLOv5的鱼虾残饵量检测系统具有重要的研究意义和实际应用价值。通过提高目标检测算法在小目标和形变目标上的性能,可以更准确地监测鱼虾残饵量,提高养殖效益,推动水产养殖行业的智能化发展。

2.图片演示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.视频演示

引入可变形卷积DCN改进YOLOv5的鱼虾残饵量检测系统_哔哩哔哩_bilibili

4.数据集的采集&标注和整理

图片的收集

首先,我们需要收集所需的图片。这可以通过不同的方式来实现,例如使用现有的公开数据集BaitDatasets。

在这里插入图片描述

labelImg是一个图形化的图像注释工具,支持VOC和YOLO格式。以下是使用labelImg将图片标注为VOC格式的步骤:

(1)下载并安装labelImg。
(2)打开labelImg并选择“Open Dir”来选择你的图片目录。
(3)为你的目标对象设置标签名称。
(4)在图片上绘制矩形框,选择对应的标签。
(5)保存标注信息,这将在图片目录下生成一个与图片同名的XML文件。
(6)重复此过程,直到所有的图片都标注完毕。

由于YOLO使用的是txt格式的标注,我们需要将VOC格式转换为YOLO格式。可以使用各种转换工具或脚本来实现。
在这里插入图片描述

下面是一个简单的方法是使用Python脚本,该脚本读取XML文件,然后将其转换为YOLO所需的txt格式。

#!/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列表

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
整理数据文件夹结构

我们需要将数据集整理为以下结构:

-----data
   |-----train
   |   |-----images
   |   |-----labels
   |
   |-----valid
   |   |-----images
   |   |-----labels
   |
   |-----test
       |-----images
       |-----labels

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

确保以下几点:

所有的训练图片都位于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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

5.核心代码讲解

5.1 export.py

class YOLOv5Exporter:
    def __init__(self, weights, include):
        self.weights = weights
        self.include = include
        self.FILE = Path(__file__).resolve()
        self.ROOT = self.FILE.parents[0]  # YOLOv5 root directory
        if str(self.ROOT) not in sys.path:
            sys.path.append(str(self.ROOT))  # add ROOT to PATH
        self.ROOT = Path(os.path.relpath(self.ROOT, Path.cwd()))  # relative

    def export_torchscript(self, model, im, file, optimize, prefix=colorstr('TorchScript:')):
        # YOLOv5 TorchScript model export
        try:
            print(f'\n{prefix} starting export with torch {torch.__version__}...')
            f = file.with_suffix('.torchscript.pt')

            ts = torch.jit.trace(model, im, strict=False)
            (optimize_for_mobile(ts) if optimize else ts).save(f)

            print(f'{prefix} export success, saved as {f} ({file_size(f):.1f} MB)')
        except Exception as e:
            print(f'{prefix} export failure: {e}')

    def export_onnx(self, model, im, file, opset, train, dynamic, simplify, prefix=colorstr('ONNX:')):
        # YOLOv5 ONNX export
        try:
            check_requirements(('onnx',))
            import onnx

            print(f'\n{prefix} starting export with onnx {onnx.__version__}...')
            f = file.with_suffix('.onnx')

            torch.onnx.export(model, im, f, verbose=False, opset_version=opset,
                              training=torch.onnx.TrainingMode.TRAINING if train else torch.onnx.TrainingMode.EVAL,
                              do_constant_folding=not train,
                              input_names=['images'],
                              output_names=['output'],
                              dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'},  # shape(1,3,640,640)
                                            'output': {0: 'batch', 1: 'anchors'}  # shape(1,25200,85)
                                            } if dynamic else None)

            # Checks
            model_onnx = onnx.load(f)  # load onnx model
            onnx.checker.check_model(model_onnx)  # check onnx model
            # print(onnx.helper.printable_graph(model_onnx.graph))  # print

            # Simplify
            if simplify:
                try:
                    check_requirements(('onnx-simplifier',))
                    import onnxsim

                    print(f'{prefix} simplifying with onnx-simplifier {onnxsim.__version__}...')
                    model_onnx, check = onnxsim.simplify(
                        model_onnx,
                        dynamic_input_shape=dynamic,
                        input_shapes={'images': list(im.shape)} if dynamic else None)
                    assert check, 'assert check failed'
                    onnx.save(model_onnx, f)
                except Exception as e:
                    print(f'{prefix} simplifier failure: {e}')
            print(f'{prefix} export success, saved as {f} ({file_size(f):.1f} MB)')
            print(f"{prefix} run --dynamic ONNX model inference with: 'python detect.py --weights {f}'")
        except Exception as e:
            print(f'{prefix} export failure: {e}')

    def export_coreml(self, model, im, file, prefix=colorstr('CoreML:')):
        # YOLOv5 CoreML export
        ct_model = None
        try:
            check_requirements(('coremltools',))
            import coremltools as ct

            print(f'\n{prefix} starting export with coremltools {ct.__version__}...')
            f = file.with_suffix('.mlmodel')

            model.train()  # CoreML exports should be placed in model.train() mode
            ts = torch.jit.trace(model, im, strict=False)  # TorchScript model
            ct_model = ct.convert(ts, inputs=[ct.ImageType('image', shape=im.shape, scale=1 / 255.0, bias=[0, 0, 0])])
            ct_model.save(f)

            print(f'{prefix} export success, saved as {f} ({file_size(f):.1f} MB)')
        except Exception as e:
            print(f'\n{prefix} export failure: {e}')

        return ct_model

    def export_saved_model(self, model, im, file, dynamic,
                           tf_nms=False, agnostic_nms=False, topk_per_class=100, topk_all=100, iou_thres=0.45,
                           conf_thres=0.25, prefix=colorstr('TensorFlow saved_model:')):
        # YOLOv5 TensorFlow saved_model export
        keras_model = None
        try:
            import tensorflow as tf
            from tensorflow import keras
            from models.tf import TFModel, TFDetect

            print(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
            f = str(file).replace('.pt', '_saved_model')
            batch_size, ch, *imgsz = list(im.shape)  # BCHW

            tf_model = TFModel(cfg=model.yaml, model=model, nc=model.nc, imgsz=imgsz)
            im = tf.zeros((batch_size, *imgsz, 3))  # BHWC order for TensorFlow
            y = tf_model.predict(im, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
            inputs = keras.Input(shape=(*imgsz, 3), batch_size=None if dynamic else batch_size)
            outputs = tf_model.predict(inputs, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
            keras_model = keras.Model(inputs=inputs, outputs=outputs)
            keras_model.trainable = False
            keras_model.summary()
            keras_model.save(f, save_format='tf')

            print(f'{prefix} export success, saved as {f} ({file_size(f):.1f} MB)')
        except Exception as e:
            print(f'\n{prefix} export failure: {e}')

        return keras_model

    def export_pb(self, keras_model, im, file, prefix=colorstr('TensorFlow GraphDef:')):
        # YOLOv5 TensorFlow GraphDef *.pb export https://github.com/leimao/Frozen_Graph_TensorFlow
        try:
            import tensorflow as tf
            from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2

            print(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
            f = file.with_suffix('.pb')

            m = tf.function(lambda x: keras_model(x))  # full model
            m = m.get_concrete_function(tf.TensorSpec(keras_model.inputs[0].shape, keras_model.inputs[0].dtype))
            frozen_func = convert_variables_to_constants_v2(m)
            frozen_func.graph.as_graph_def()
            tf.io.write_graph(graph_or_graph_def=frozen_func.graph, logdir=str(f.parent), name=f.name, as_text=False)

            print(f'{prefix} export success, saved as {f} ({file_size(f):.1f} MB)')
        except Exception as e:
            print(f'\n{prefix} export failure: {e}')

    def export_tflite(self, keras_model, im, file, int8, data, ncalib, prefix=colorstr('TensorFlow Lite:')):
        # YOLOv5 TensorFlow Lite export
        try:
            import tensorflow as tf
            from models.tf import representative_dataset_gen

            print(f'\n{prefix} starting export with tensorflow {tf.__version__}...')
            batch_size, ch, *imgsz = list(im.shape)  # BCHW
            f = str(file).replace('.pt', '-fp16.tflite')

            converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
            converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]
            converter.target_spec.supported_types = [tf.float16]
            converter.optimizations = [tf.lite.Optimize.DEFAULT]
            if int8:
                dataset = LoadImages(check_dat
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153

该程序文件是用于将YOLOv5 PyTorch模型导出为TorchScript、ONNX、CoreML、TensorFlow等格式的工具。通过命令行参数指定要导出的模型权重文件和导出格式,可以将模型导出为指定格式的文件。

程序文件首先导入了必要的库和模块,然后定义了一些导出函数。其中,export_torchscript函数用于将模型导出为TorchScript格式,export_onnx函数用于将模型导出为ONNX格式,export_coreml函数用于将模型导出为CoreML格式,export_saved_model函数用于将模型导出为TensorFlow saved_model格式,export_pb函数用于将模型导出为TensorFlow GraphDef格式,export_tflite函数用于将模型导出为TensorFlow Lite格式。

程序文件还定义了一些辅助函数和全局变量,用于处理命令行参数、加载模型、检查依赖库等操作。

最后,程序文件通过命令行参数指定要导出的模型权重文件和导出格式,并调用相应的导出函数进行导出操作。

5.2 train.py



def train(hyp,  # path/to/hyp.yaml or hyp dictionary
          opt,
          device,
          callbacks
          ):
    save_dir, epochs, batch_size, weights, single_cls, evolve, data, cfg, resume, noval, nosave, workers, freeze, = \
        Path(opt.save_dir), opt.epochs, opt.batch_size, opt.weights, opt.single_cls, opt.evolve, opt.data, opt.cfg, \
        opt.resume, opt.noval, opt.nosave, opt.workers, opt.freeze

    # Directories
    w = save_dir / 'weights'  # weights dir
    (w.parent if evolve else w).mkdir(parents=True, exist_ok=True)  # make dir
    last, best = w / 'last.pt', w / 'best.pt'

    # Hyperparameters
    if isinstance(hyp, str):
        with open(hyp, errors='ignore') as f:
            hyp = yaml.safe_load(f)  # load hyps dict
    LOGGER.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))

    # Save run settings
    with open(save_dir / 'hyp.yaml', 'w') as f:
        yaml.safe_dump(hyp, f, sort_keys=False)
    with open(save_dir / 'opt.yaml', 'w') as f:
        yaml.safe_dump(vars(opt), f, sort_keys=False)
    data_dict = None

    # Loggers
    if RANK in [-1, 0]:
        loggers = Loggers(save_dir, weights, opt, hyp, LOGGER)  # loggers instance
        if loggers.wandb:
            data_dict = loggers.wandb.data_dict
            if resume:
                weights, epochs, hyp = opt.weights, opt.epochs, opt.hyp

        # Register actions
        for k in methods(loggers):
            callbacks.register_action(k, callback=getattr(loggers, k))

    # Config
    plots = not evolve  # create plots
    cuda = device.type != 'cpu'
    init_seeds(1 + RANK)
    with torch_distributed_zero_first(LOCAL_RANK):
        data_dict = data_dict or check_dataset(data)  # check if None
    train_path, val_path = data_dict['train'], data_dict['val']
    nc = 1 if single_cls else int(data_dict['nc'])  # number of classes
    names = ['item'] if single_cls and len(data_dict['names']) != 1 else data_dict['names']  # class names
    assert len(names) == nc, f'{len(names)} names found for nc={nc} dataset in {data}'  # check
    is_coco = data.endswith('coco.yaml') and nc == 80  # COCO dataset

    # Model
    check_suffix(weights, '.pt')  # check weights
    pretrained = weights.endswith('.pt')
    if pretrained:
        with torch_distributed_zero_first(LOCAL_RANK):
            weights = attempt_download(weights)  # download if not found locally
        ckpt = torch.load(weights, map_location=device)  # load checkpoint
        model = Model(cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device)  # create
        exclude = ['anchor'] if (cfg or hyp.get('anchors')) and not resume else []  # exclude keys
        csd = ckpt['model'].float().state_dict()  # checkpoint state_dict as FP32
        csd = intersect_dicts(csd, model.state_dict(), exclude=exclude)  # intersect
        model.load_state_dict(csd, strict=False)  # load
        LOGGER.info(f'Transferred {len(csd)}/{len(model.state_dict())} items from {weights}')  # report
    else:
        model = Model(cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device)  # create

    # Freeze
    freeze = [f'model.{x}.' for x in range(freeze)]  # layers to freeze
    for k, v in model.named_parameters():
        v.requires_grad = True  # train all layers
        if any(x in k for x in freeze):
            print(f'freezing {k}')
            v.requires_grad = False

    # Image size
    gs = max(int(model.stride.max()), 32)  # grid size (max stride)
    imgsz = check_img_size(opt.imgsz, gs, floor=gs * 2)  # verify imgsz is gs-multiple

    # Batch size
    if RANK == -1 and batch_size == -1:  # single-GPU only, estimate best batch size
        batch_size = check_train_batch_size(model, imgsz)

    # Optimizer
    nbs = 64  # nominal batch size
    accumulate = max(round(nbs / batch_size), 1)  # accumulate loss before optimizing
    hyp['weight_decay'] *= batch_size * accumulate / nbs  # scale weight_decay
    LOGGER.info(f"Scaled weight_decay = {hyp['weight_decay']}")

    g0, g1, g2 = [], [], []  # optimizer parameter groups
    for v in model.modules():
        if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter):  # bias
            g2.append(v.bias)
        if isinstance(v, nn.BatchNorm2d):  # weight (no decay)
            g0.append(v.weight)
        elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter):  # weight (with decay)
            g1.append(v.weight)

    if opt.adam:
        optimizer = Adam(g0, lr=hyp['lr0'], betas=(hyp['momentum'], 0.999))  # adjust beta1 to momentum
    else:
        optimizer = SGD(g0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True)

    optimizer.add_param_group({'params': g1, 'weight_decay': hyp['weight_decay']})  # add g1 with weight_decay
    optimizer.add_param_group({'params': g2})  # add g2 (biases)
    LOGGER.info(f"{colorstr('optimizer:')} {type(optimizer).__name__} with parameter groups "
                f"{len(g0)} weight, {len(g1)} weight (no decay), {len(g2)} bias")
    del g0, g1, g2

    # Scheduler
    if opt.linear_lr:
        lf = lambda x: (1 - x / (epochs - 1)) * (
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115

这个程序文件是用来训练一个YOLOv5模型的。它包含了训练模型所需的各种功能,如数据加载、模型创建、优化器、学习率调度器等。程序的入口函数是train(),它接受一些参数,如超参数、设备、回调函数等。在训练过程中,程序会加载预训练的权重,创建模型,并根据数据集进行训练。训练过程中会使用优化器和学习率调度器来更新模型的参数。训练过程中还会进行一些日志记录和模型保存的操作。程序还支持断点续训功能,可以从之前训练的模型继续训练。

5.3 ui.py


class ObjectDetection:
    def __init__(self):
        self.model, self.stride, self.names, self.pt, self.jit, self.onnx, self.engine = self.load_model()

    def load_model(self,
            weights=ROOT / 'best.pt',  # model.pt path(s)
            data=ROOT / 'data/coco128.yaml',  # dataset.yaml path
            device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu
            half=False,  # use FP16 half-precision inference
            dnn=False,  # use OpenCV DNN for ONNX inference
    ):
        # Load model
        device = select_device(device)
        model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data)
        stride, names, pt, jit, onnx, engine = model.stride, model.names, model.pt, model.jit, model.onnx, model.engine

        # Half
        half &= (pt or jit or onnx or engine) and device.type != 'cpu'  # FP16 supported on limited backends with CUDA
        if pt or jit:
            model.model.half() if half else model.model.float()
        return model, stride, names, pt, jit, onnx, engine

    def run(self, img, stride, pt,
            imgsz=(640, 640),  # inference size (height, width)
            conf_thres=0.25,  # confidence threshold
            iou_thres=0.05,  # NMS IOU threshold
            max_det=1000,  # maximum detections per image
            device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu
            classes=None,  # filter by class: --class 0, or --class 0 2 3
            agnostic_nms=False,  # class-agnostic NMS
            augment=False,  # augmented inference
            half=False,  # use FP16 half-precision inference
    ):
        cal_detect = []

        device = select_device(device)
        names = self.model.module.names if hasattr(self.model, 'module') else self.model.names  # get class names

        # Set Dataloader
        im = letterbox(img, imgsz, stride, pt)[0]

        # Convert
        im = im.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
        im = np.ascontiguousarray(im)

        im = torch.from_numpy(im).to(device)
        im = im.half() if half else im.float()  # uint8 to fp16/32
        im /= 255  # 0 - 255 to 0.0 - 1.0
        if len(im.shape) == 3:
            im = im[None]  # expand for batch dim

        pred = self.model(im, augment=augment)

        pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
        # Process detections
        for i, det in enumerate(pred):  # detections per image
            if len(det):
                # Rescale boxes from img_size to im0 size
                det[:, :4] = scale_coords(im.shape[2:], det[:, :4], img.shape).round()

                # Write results

                for *xyxy, conf, cls in reversed(det):
                    c = int(cls)  # integer class
                    label = f''
                    lbl = names[int(cls)]
                    #print(lbl)
                    #if lbl not in ['bus','truck','car']:
                        #continue
                    cal_detect.append([label, xyxy,float(conf)])
        return cal_detect

    def detect(self, info1):
        image = cv2.imread(info1)
        results = self.run(self.model, image, self.stride, self.pt)  # 识别, 返回多个数组每个第一个为结果,第二个为坐标位置
        count = 0
        for i in results:
            count += 1
            box = i[1]
            p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
            color = [0, 0 , 255]
            cv2.rectangle(image, p1, p2, color, thickness=3, lineType=cv2.LINE_AA)

        cv2.putText(image, str(count), (120, 220),
                    cv2.FONT_HERSHEY_SIMPLEX, 10, (0, 0, 255), 10)
        ui.showimg(image)
        ui.printf('检测到 ' + str(count) + ' 鱼饵')
        QApplication.processEvents()

class Thread_1(QThread):
    def __init__(self, info1):
        super().__init__()
        self.info1 = info1
        self.detector = ObjectDetection()

    def run(self):
        self.detector.detect(self.info1)

class Ui_MainWindow(object):
    def __init__(self):

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

这个程序文件是一个基于PyQt5的图形用户界面(GUI)程序,用于鱼虾残饵量检测系统。程序中使用了目标检测库YOLOv5来进行鱼虾残饵的检测。

程序的主要功能包括:

  1. 加载模型:通过load_model函数加载YOLOv5模型,包括权重文件、数据集文件等。
  2. 运行检测:通过run函数对输入的图像进行目标检测,返回检测结果和坐标位置。
  3. 图像显示:通过showimg函数将检测结果显示在GUI界面上。
  4. 文件检测:通过选择文件按钮选择一个图像文件,然后调用det函数对该图像进行检测并显示结果。
  5. 实时检测:通过打开摄像头,实时获取摄像头图像并进行检测,然后显示检测结果。
  6. 退出系统:通过退出系统按钮退出程序。

程序的界面由一个主窗口和几个控件组成,包括一个标签(label)用于显示标题,一个标签(label_2)用于显示图像,一个文本浏览器(textBrowser)用于显示检测结果,以及几个按钮(pushButton_4、pushButton_5、pushButton、pushButton_3)用于触发不同的功能。

程序的运行流程是先加载模型,然后显示主窗口,等待用户操作。用户可以选择文件进行检测,也可以打开摄像头进行实时检测。检测结果会显示在图像和文本浏览器中。用户可以随时退出系统。

总体来说,这个程序文件实现了一个简单的鱼虾残饵量检测系统的图形界面,并通过YOLOv5模型进行目标检测。

5.4 yolov5-DCN.py

这个程序文件是一个使用了DCNv2(Deformable Convolutional Networks)的模型。DCNv2是一种改进的卷积操作,可以自适应地学习卷积核的形状,从而提高模型在目标检测等任务中的性能。

该程序文件定义了两个类:DCNv2和Bottleneck_DCN。DCNv2类是DCNv2操作的实现,包括了卷积操作、偏置、偏移和掩码等参数。Bottleneck_DCN类是一个标准的瓶颈结构,包括了一个1x1卷积层和一个DCNv2层。

另外,程序文件还定义了一个C3_DCN类,它是一个使用DCNv2的C3模块。C3模块是一种常用的卷积神经网络模块,由多个Bottleneck_DCN组成。

这个程序文件主要用于构建使用DCNv2的模型,可以在目标检测等任务中应用。

6.系统整体结构

整体功能和构架概述:
该项目是一个鱼虾残饵量检测系统,基于YOLOv5模型,并引入了可变形卷积DCN进行改进。程序包含了训练、验证、导出模型和图形用户界面等功能。具体构架如下:

  1. 训练部分:train.py文件负责训练YOLOv5模型,包括数据加载、模型创建、优化器、学习率调度器等。通过命令行参数指定超参数和设备等配置。

  2. 验证部分:val.py文件用于验证训练好的模型在自定义数据集上的准确性,计算模型的准确率和mAP等指标。

  3. 导出部分:export.py文件用于将训练好的模型导出为TorchScript、ONNX、CoreML、TensorFlow等格式,方便在其他平台上部署和使用。

  4. 图形用户界面部分:ui.py文件是一个基于PyQt5的图形用户界面(GUI)程序,用于鱼虾残饵量检测系统。用户可以选择文件进行检测,也可以打开摄像头进行实时检测。

  5. 模型构建部分:yolov5-DCN.py文件实现了使用可变形卷积DCN的YOLOv5模型,通过定义DCNv2和Bottleneck_DCN等类来构建网络结构。

  6. 其他模块:models目录下的common.py和experimental.py文件定义了一些常用的模块和实验性的网络模块,用于构建YOLOv5模型。utils目录下的各个文件包含了一些辅助函数和工具函数,用于数据处理、模型操作、日志记录等。

下面是每个文件的功能整理:

文件路径功能
export.py将YOLOv5模型导出为不同格式的文件
train.py训练YOLOv5模型
ui.py图形用户界面程序,用于鱼虾残饵量检测系统
val.py在自定义数据集上验证模型的准确性
yolov5-DCN.py使用可变形卷积DCN的YOLOv5模型
models\common.py定义了一些常用的模块和函数,用于构建网络结构
models\experimental.py定义了一些实验性的网络模块和辅助函数
models\tf.pyTensorFlow模型相关的函数和类
models\yolo.pyYOLOv5模型的定义和相关函数
models_init_.py模型模块的初始化文件
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.pyPyTorch相关的辅助函数
utils_init_.py工具模块的初始化文件
utils\aws\resume.pyAWS相关的函数
utils\aws_init_.pyAWS模块的初始化文件
utils\flask_rest_api\example_request.pyFlask REST API的示例请求
utils\flask_rest_api\restapi.pyFlask REST API的实现
utils\loggers_init_.py日志记录模块的初始化文件
utils\loggers\wandb\log_dataset.py使用WandB记录数据集的日志
utils\loggers\wandb\sweep.py使用WandB进行超参数搜索的日志记录
utils\loggers\wandb\wandb_utils.py使用WandB进行日志记录的辅助函数
utils\loggers\wandb_init_.pyWandB日志记录模块的初始化文件

7.可变性卷积DCN简介

卷积神经网络由于其构建模块中固定的几何结构,本质上受限于模型几何变换。为了提高卷积神经网络的转换建模能力,《Deformable Convolutional Networks》作者提出了两个模块:可变形卷积(deformable convolution)和可变形RoI池(deformable RoI pooling)。这两个模块均基于用额外的偏移来增加模块中的空间采样位置以及从目标任务中学习偏移的思想,而不需要额外的监督。

第一次证明了在深度神经网络中学习密集空间变换(dense spatial transformation)对于复杂的视觉任务是有效的

视觉识别中的一个关键挑战是如何适应对象比例、姿态、视点和零件变形中的几何变化或模型几何变换。一般有两种方法实现:
1)建立具有足够期望变化的训练数据集。这通常通过增加现有的数据样本来实现,例如通过仿射变换。但是训练成本昂贵而且模型参数庞大。
2)使用变换不变(transformation-invariant)的特征和算法。比如比较有名的SIFT(尺度不变特征变换)便是这一类的代表算法。

但以上的方法有两个缺点:
1)几何变换被假定为固定的和已知的,这些先验知识被用来扩充数据,设计特征和算法。为此,这个假设阻止了对具有未知几何变换的新任务的推广,从而导致这些几何变换可能没有被正确建模。
2)对于不变特征和算法进行手动设计,对于过于复杂的变换可能是困难的或不可行的。

卷积神经网络本质上局限于模拟大型未知转换。局限性源于CNN模块的固定几何结构:卷积单元在固定位置对输入特征图进行采样;池化层以固定比率降低特征矩阵分辨率;RoI(感兴趣区域)池化层将RoI分成固定的空间箱(spatial bins)等。缺乏处理几何变换的内部机制。

这种内部机制的缺乏会导致一些问题,举个例子。同一个CNN层中所有激活单元的感受野大小是相同的,但是这是不可取的。因为不同的位置可能对应于具有不同尺度或变形的对象,所以尺度或感受野大小的自适应确定对于具有精细定位的视觉识别是渴望的。

对于这些问题,作者提出了两个模块提高CNNs对几何变换建模的能力。

deformable convolution(可变形卷积)
将2D偏移量添加到标准卷积中的常规网格采样位置,使得采样网格能够自由变形。通过额外的卷积层,从前面的特征映射中学习偏移。因此,变形采用局部、密集和自适应的方式取决于输入特征。
在这里插入图片描述

deformable RoI pooling(可变形RoI池化)
为先前RoI池化的常规库(bin)分区中的每个库位置(bin partition)增加了一个偏移量。类似地,偏移是从前面的特征图和感兴趣区域中学习的,从而能够对具有不同形状的对象进行自适应部件定位(adaptive part localization)。

Deformable Convolutional Networks

Deformable Convolution
2D卷积由两个步骤组成:
1)在输入特征图x xx上使用规则网格R RR进行采样。
2)把这些采样点乘不同权重w ww后相加。

网格R定义感受野大小和扩张程度,比如内核大小为3x3,扩张程度为1的网格R可以表示为:
R = { ( − 1 , − 1 ) , ( − 1 , 0 ) , … , ( 0 , 1 ) , ( 1 , 1 ) } R = {(-1,-1),(-1,0),\dots,(0,1),(1,1)}
R={(−1,−1),(−1,0),…,(0,1),(1,1)}


一般为小数,使用双线性插值进行处理。(把小数坐标分解到相邻的四个整数坐标点来计算结果)
在这里插入图片描述

具体操作如图所示:
在这里插入图片描述

首先对输入特征层进行一个普通的3x3卷积处理得到偏移域(offset field)。偏移域特征图具有与输入特征图相同的空间分辨率,channels维度2N对应于N个2D(xy两个方向)偏移。其中的N是原输入特征图上所具有的N个channels,也就是输入输出channels保持不变,这里xy两个channels分别对输出特征图上的一个channels进行偏移。确定采样点后就通过与相对应的权重w点乘相加得到输出特征图上该点最终值。

前面也提到过,由于这里xy两个方向所训练出来的偏移量一般来说是一个小数,那么为了得到这个点所对应的数值,会采用双线性插值的方法,从最近的四个邻近坐标点中计算得到该偏移点的数值,公式如下:
在这里插入图片描述

具体推理过程见:双线性插值原理

Deformable RoI Poolingb

所有基于区域提议(RPN)的对象检测方法都使用RoI池话处理,将任意大小的输入矩形区域转换为固定大小的特征图。

在这里插入图片描述

一般为小数,需要使用双线性插值进行处理。
在这里插入图片描述

具体操作如图所示:

在这里插入图片描述

当时看这个部分的时候觉得有些突兀,明明RoI池化会将特征层转化为固定尺寸的区域。其实,我个人觉得,这个部分与上述的可变性卷积操作是类似的。这里同样是使用了一个普通的RoI池化操作,进行一些列处理后得到了一个偏移域特征图,然后重新作用于原来的w × H w \times Hw×H的RoI。只不过这里不再是规律的逐行逐列对每个格子进行池化,而是对于格子进行偏移后再池化处理。

Postion﹣Sensitive RoI Pooling

除此之外,论文还提出一种PS RoI池化(Postion﹣Sensitive RoI Pooling)。不同于上述可变形RoI池化中的全连接过程,这里使用全卷积替换。

具体操作如图所示:
在这里插入图片描述

首先,对于原来的特征图来说,原本是将输入特征图上的RoI区域分成k × k k\times kk×k个bin。而在这里,则是将输入特征图进行卷积操作,分别得到一个channels为k 2 ( C + 1 ) k^{2}(C+1)k (C+1)的得分图(score maps)和一个channels为2 k 2 ( C + 1 ) 2k{2}(C+1)2k 2 (C+1)的偏移域(offset fields),这两个特征矩阵的宽高是与输入特征矩阵相同的。其中,得分图的channels中,k × k k \times kk×k分别表示的是每一个网格,C CC表示的检测对象的类别数目,1表示背景。而在偏移域中的2表示xy两个方向的偏移。
也就是说,在PS RoI池化中,对于RoI的每一个网格都独自占一个通道形成一层得分图,然后其对于的偏移量占两个通道。offset fields得到的偏移是归一化后的偏移,需要通过和deformable RoI pooling中一样的变换方式得到∆ p i j ∆p_{ij}∆p ij,然后对每层得分图进行偏移池化处理。最后处理完的结果就对应着最后输出的一个网格。所以其包含了位置信息。

原文论述为:

Understanding Deformable ConvNets

当可变形卷积叠加时,复合变形的效果是深远的。如图所示:
在这里插入图片描述

ps:a是标准卷积的固定感受野,b是可变形卷积的适应性感受野。

感受野和标准卷积中的采样位置在整个顶部特征图上是固定的(左)。在可变形卷积中,它们根据对象的比例和形状进行自适应调整(右)。
在这里插入图片描述

8.引入可变形DCN改进YOLOv5

在深度学习和计算机领域,YOLO(You Only Look Once)系列是一种非常流行的实时工件检测算法。YOLOv5是这个系列中的一个版本,以实现快速和准确着称。然而,尽管YOLOv5在任务上表现,它在处理形变明显增大的物体时可能会遇到困难。这是因为YOLOv5的神经网络(CNN)结构在设计时假设物体的形态是相对固定和规则的。

为了解决这个问题,近期我们引入了可变形卷积网络(Deformable Convolutional Networks,简称DCN),旨在提高YOLOv5在检测形状变化更大或姿势多样的物体时的准确性。以下是对“引入”可变形DCN改进YOLOv5”网络结构改进部分的详细描述。

可变形网络(DCN)

可变形的视觉网络是一种特殊的视觉网络,它可以动态地改变其视觉视觉的形状以适应视觉的形态。在标准的视觉视觉中,视觉视觉的尺寸和形状是固定的,例如3x3或5x5的格局。然而,在DCN中,表面清晰的点每个都可以根据输入数据的特征进行偏移,允许表面清晰以非常灵活的方式适应物体的形状和结构。增强了网络对目标形状变化的预见性,从而提高了检测的准确性。

YOLOv5网络结构的改进

在改进YOLOv5时,我们将DCN整合到了网络结构的多个层次。传统YOLOv5的架构主要由格式层、批量归一化层和激活层组成。在改进后的模型中,特定的标准卷这些改变主要发生在网络的中间和晚期层,因为这些层负责捕获更高级别的抽象特征,这对于理解视线的形态和结构至关重要。

除了替换DCN层,中断们还引入了新的点生成策略,以适应可变形层的输出。由于可变形图层能够更好地捕捉物体的形状,某个点生成策略也需要调整利用这些精确的特征。某一点物体是检测中的一个概念,指预定义的框架,它们作为网络预测物体位置的更基准。

引入可变形DCN改进YOLOv5的网络结构
# YOLOv5-DCN

# Parameters
nc: 1  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.25  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

# YOLOv5 v6.0 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, C3_DCN, [1024]],
   [-1, 1, SPPF, [1024, 5]],  # 9
  ]

# YOLOv5 v6.0 head
head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

9.训练结果可视化分析

评价指标

框损失(训练和验证中):表示模型预测目标周围边界框的效果如何。
对象损失(训练和验证中):反映模型检测对象的准确性。
类别损失(训练和验证中):表示对检测到的对象进行分类的性能。
Precision、Recall、mAP(平均平均精度)为 0.5,mAP 为 0.5:0.95:这些是对象检测模型的关键指标,其中精度衡量正预测的准确性,召回衡量正确识别的正预测的比例,以及mAP 提供了模型在不同阈值下的准确性的总体衡量标准。
学习率(lr0、lr1、lr2):这些值表示训练过程中使用的学习率。

训练结果可视化

为了进行全面分析,我将创建几个可视化来说明这些指标在训练过程中如何演变。这些可视化将有助于理解模型随时间的表现和行为。生成这些可视化后,我将提供详细的分析。让我们从创建可视化开始。

import matplotlib.pyplot as plt
import seaborn as sns

# Set the aesthetic style of the plots
sns.set_style("whitegrid")

def plot_metrics(df, metric_names, title):
    plt.figure(figsize=(12, 6))
    for metric in metric_names:
        plt.plot(df['epoch'], df[metric], label=metric)
    plt.title(title)
    plt.xlabel('Epoch')
    plt.ylabel('Value')
    plt.legend()
    plt.show()

# Plotting the loss metrics for training and validation
plot_metrics(data, ['train/box_loss', 'train/obj_loss', 'train/cls_loss'], 'Training Loss Metrics')
plot_metrics(data, ['val/box_loss', 'val/obj_loss', 'val/cls_loss'], 'Validation Loss Metrics')

# Plotting precision, recall, and mAP metrics
plot_metrics(data, ['metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95'], 'Precision, Recall, and mAP Metrics')

# Plotting the learning rate changes
plot_metrics(data, ['x/lr0', 'x/lr1', 'x/lr2'], 'Learning Rate Changes over Epochs')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

可视化提供了实验进度和性能的详细视图。让我们深入分析这些结果:

训练损失指标

Box Loss、Object Loss 和 Class Loss:所有这些指标都显示出随时间推移而下降的趋势,表明模型正在有效地学习。框损失的减少表明预测边界框的准确性得到提高。同样,随着时间的推移,减少对象损失和类别损失意味着更好的对象检测和分类。

验证损失指标

验证损失指标反映了训练损失指标,显示出持续的下降。这是一个积极的信号,因为它表明模型具有良好的泛化能力,并且没有过度拟合训练数据。

精度、召回率和 mAP 指标

精度:该指标从零开始,并显示出显着的增加,表明模型正确识别正实例的能力随着训练的进展而提高。
召回率:召回率的逐渐增加表明模型越来越有能力找到数据中的所有相关实例。
mAP(平均平均精度):mAP_0.5 和 mAP_0.5:0.95 均呈现上升趋势。mAP_0.5 通常高于 mAP_0.5:0.95,这是预期的,因为后者是更严格的措施。这些指标的增加表明模型准确性和稳健性的整体提高。

学习率变化

学习率(lr0、lr1、lr2)似乎是恒定的或遵循特定的时间表。学习率的变化会显着影响训练过程,影响收敛的速度和稳定性。

混淆矩阵

混淆矩阵显示“提要”的真阳性率很高,表明该模型在正确识别提要方面非常有效。假阴性极少,这表明该模型很少会错过存在的提要。“背景”没有误报意味着该模型非常精确,并且不会错误地将背景识别为提要。背景真阴性的满分 1.00 表明该模型在识别非饲料区域方面表现出色,没有任何错误。
在这里插入图片描述

F1分数曲线

F1 分数是精确率和召回率的调和平均值,提供平衡这两个问题的单一分数。您提供的曲线显示 F1 分数在某个置信度阈值处达到峰值,表明精确度和召回率达到平衡的最佳点。该模型的最高 F1 分数表明其预测的稳健性,因为它保持较高的真阳性率,同时保持较低的误报和漏报率。
在这里插入图片描述

精密曲线

精度曲线显示了精度和置信度之间的关系。精确度是指相关结果的百分比。在这种情况下,在较低的置信水平下,精度开始较低,这表明当模型置信度较低时,更有可能出现误报错误。随着置信度的提高,精度也会提高,这意味着模型的预测更有可能是正确的。高置信水平的稳定表明该模型对其预测的很大一部分具有很强的确定性。
在这里插入图片描述

召回曲线

召回曲线表明模型在数据集中查找所有相关案例(提要)的能力。即使置信度阈值较低,召回率也很高,这意味着即使模型不太确定大多数提要,它也能够检测到它们。这是一个理想的特性,特别是在缺少目标对象实例(在本例中为提要)可能会产生严重后果的应用程序中。
在这里插入图片描述

精确率-召回率曲线

该曲线是不同阈值的精度(y 轴)和召回率(x 轴)的图。这是评估二元分类模型性能的另一个工具。它显示了不同阈值的精度和召回率之间的权衡。曲线下面积大代表高召回率和高准确率,其中高准确率与低假阳性率相关,高召回率与低假阴性率相关。所提供的精度-召回率曲线中模型的性能表明,随着召回率的增加,它保持了较高的精度,这表明模型具有良好的预测性能。
在这里插入图片描述

整体分析

模型性能:损失指标和准确性指标(精度、召回率、mAP)的不断改进表明,通过可变形卷积网络(DCN)增强的YOLOv5模型正在有效地学习检测和分类鱼虾饵料残留。训练和验证指标的提高表明了良好的泛化能力。
学习率:学习率策略似乎是有效的,模型性能指标的持续改进就证明了这一点。探索不同的学习率方案是否可以导致更快的收敛或更高的最终精度将会很有趣。
未来的改进:为了进一步增强模型性能,可以考虑尝试不同的架构、数据增强技术或更复杂的学习率计划。此外,进行错误分析以了解模型所产生的错误类型(误报、误报)可以为进一步细化提供见解。

结论

结果表明,DCN 成功应用于改进 YOLOv5,用于检测鱼虾饵料残留。该模型在学习效率和准确性方面显示出可喜的结果,使其成为环境监测或水产养殖管理等相关应用中潜在有价值的工具。在现实场景中的进一步优化和测试可以更深入地了解其实际效果。​

10.系统整合

下图[完整源码&数据集&环境部署视频教程&自定义UI界面]
在这里插入图片描述

参考博客《引入可变形卷积DCN改进YOLOv5的鱼虾残饵量检测系统》

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/93903
推荐阅读
相关标签
  

闽ICP备14008679号