基于NFNet-BackBone的改进YOLOv5的手套缺陷检测系统

1.研究背景与意义

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

研究背景与意义:

随着人工智能技术的不断发展,计算机视觉在各个领域中的应用越来越广泛。其中,目标检测是计算机视觉领域中的一个重要研究方向。目标检测的目的是在图像或视频中准确地识别和定位出感兴趣的目标物体。手套缺陷检测是目标检测的一个具体应用场景,它在制造业、医疗领域等方面具有重要的意义。

手套作为一种常见的劳动防护用品,广泛应用于工业生产和医疗领域。然而,由于制造过程中的各种原因,手套往往会出现各种缺陷,如破损、褶皱、污渍等。这些缺陷不仅会影响手套的使用寿命和性能,还可能对使用者的安全和健康造成潜在的威胁。因此,开发一种高效准确的手套缺陷检测系统对于提高生产效率、保障产品质量和人身安全具有重要意义。

目前,基于深度学习的目标检测方法已经取得了显著的进展。YOLOv5是一种基于单阶段检测器的目标检测算法,具有高效、准确的特点。然而,传统的YOLOv5在处理手套缺陷检测问题时存在一些挑战。首先,手套缺陷通常是细小的区域,易于被忽略或误判。其次,手套缺陷的种类繁多,形状和纹理差异较大,传统的目标检测算法往往难以对其进行准确的检测和分类。

为了解决这些问题,本研究将基于NFNet-BackBone对YOLOv5进行改进,设计一种高效准确的手套缺陷检测系统。NFNet-BackBone是一种新型的卷积神经网络结构,具有更强的特征提取能力和更好的泛化性能。通过将NFNet-BackBone引入到YOLOv5中,可以提高系统对手套缺陷的检测和分类能力。

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

首先,该研究将填补手套缺陷检测领域的研究空白。目前,关于手套缺陷检测的研究相对较少,现有的方法往往无法满足实际应用的需求。本研究将通过改进YOLOv5算法,提高手套缺陷检测系统的准确性和效率,为手套制造业和医疗领域提供一种可靠的检测工具。

其次,该研究将推动目标检测算法的发展。NFNet-BackBone作为一种新型的卷积神经网络结构,具有较好的性能表现。将其引入到YOLOv5中,可以提高目标检测算法的特征提取能力和泛化性能,为其他目标检测任务提供借鉴和参考。

最后,该研究对于提高生产效率和产品质量具有重要意义。手套缺陷检测系统的引入可以实现对手套缺陷的自动化检测和分类,提高生产线的效率和产品的一致性。同时,准确检测手套缺陷可以避免不合格产品的流入市场,保障使用者的安全和健康。

综上所述,基于NFNet-BackBone的改进YOLOv5手套缺陷检测系统的研究具有重要的理论和实际意义。通过提高手套缺陷检测系统的准确性和效率,可以为手套制造业和医疗领域提供一种可靠的检测工具,推动目标检测算法的发展,提高生产效率和产品质量。

2.图片演示

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

3.视频演示

基于NFNet-BackBone的改进YOLOv5的手套缺陷检测系统_哔哩哔哩_bilibili

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

图片的收集

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

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

import os
import shutil
import random

# 指定输入和输出文件夹的路径
input_dir = 'train'
output_dir = 'output'

# 确保输出文件夹存在
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# 遍历输入文件夹中的所有子文件夹
for subdir in os.listdir(input_dir):
    input_subdir_path = os.path.join(input_dir, subdir)

    # 确保它是一个子文件夹
    if os.path.isdir(input_subdir_path):
        output_subdir_path = os.path.join(output_dir, subdir)

        # 在输出文件夹中创建同名的子文件夹
        if not os.path.exists(output_subdir_path):
            os.makedirs(output_subdir_path)

        # 获取所有文件的列表
        files = [f for f in os.listdir(input_subdir_path) if os.path.isfile(os.path.join(input_subdir_path, f))]

        # 随机选择四分之一的文件
        files_to_move = random.sample(files, len(files) // 4)

        # 移动文件
        for file_to_move in files_to_move:
            src_path = os.path.join(input_subdir_path, file_to_move)
            dest_path = os.path.join(output_subdir_path, file_to_move)
            shutil.move(src_path, dest_path)

print("任务完成!")


整理数据文件夹结构

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

-----dataset
	-----dataset
           |-----train
           |   |-----class1
           |   |-----class2
           |   |-----.......
           |
           |-----valid
           |   |-----class1
           |   |-----class2
           |   |-----.......
           |
           |-----test
           |   |-----class1
           |   |-----class2
           |   |-----.......

模型训练
 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 agc.py


class AGC(optim.Optimizer):
    def __init__(self, params, optim: optim.Optimizer, clipping: float = 1e-2, eps: float = 1e-3, model=None, ignore_agc=["fc"]):
        # Initialization code

    @torch.no_grad()
    def step(self, closure=None):
        # Optimization step code

    def zero_grad(self, set_to_none: bool = False):
        # Zero gradient code

这段代码定义了一个名为AGC的类,继承自torch.optim.Optimizer。该类实现了自适应梯度裁剪(AGC)算法。AGC算法的核心思想是根据梯度的大小来裁剪梯度,以避免梯度爆炸的问题。

在类的初始化方法中,参数包括params(要优化的参数)、optim(优化器)、clipping(裁剪值,默认为1e-2)、eps(eps,默认为1e-3)、model(原始模型,默认为None)、ignore_agc(要忽略的层,默认为[“fc”])。

在step方法中,实现了一次优化步骤。对于每个参数组,计算参数的范数和梯度的范数,并根据裁剪值进行裁剪。最后调用优化器的step方法进行参数更新。

在zero_grad方法中,将所有参数的梯度置零。

这段代码封装了AGC算法的核心逻辑,可以方便地在训练过程中使用AGC优化器。

该程序文件名为agc.py,是一个实现自适应梯度裁剪(Adaptive Gradient Clipping)的通用优化器。它继承自torch.optim.Optimizer类,并提供了一个名为AGC的优化器类。

AGC类的构造函数接受以下参数:

  • params:要优化的参数,可以是一个可迭代对象或定义参数组的字典。
  • optim:基础优化器,必须是torch.optim.Optimizer的实例。
  • clipping:裁剪值,默认为1e-2。
  • eps:eps值,默认为1e-3。
  • model:原始模型,可选参数。
  • ignore_agc:需要忽略的层,可以是字符串或可迭代对象,默认为[“fc”]。

AGC类的主要方法是step()和zero_grad()。

  • step()方法执行单个优化步骤。它首先计算损失,然后对每个参数进行梯度裁剪。裁剪的方式是根据参数的梯度范数和裁剪值计算裁剪比例,然后将梯度乘以裁剪比例。最后调用基础优化器的step()方法进行参数更新。
  • zero_grad()方法将所有优化的参数的梯度置零。可以选择将梯度设置为None,这样可以节省内存,并且在某些情况下可能提高性能。

该程序文件还导入了torch、nn、optim模块,并使用了nfnets.utils中的unitwise_norm函数和collections模块中的Iterable类。

5.2 base.py

base.py是一个包含了两个类的程序文件。第一个类是WSConv1d,它是nn.Conv1d的子类,用于在输入信号上应用一维卷积。它支持不同的参数设置,包括输入通道数、输出通道数、卷积核大小、步长、填充、膨胀率、分组数等。该类还实现了一些方法,如standardize_weight和forward,用于对权重进行标准化和进行前向传播计算。

第二个类是WSConv2d,它是nn.Conv2d的子类,用于在输入信号上应用二维卷积。它的功能和WSConv1d类似,但是适用于二维输入信号。它也支持不同的参数设置,包括输入通道数、输出通道数、卷积核大小、步长、填充、膨胀率、分组数等。该类还实现了一些方法,如standardize_weight和forward,用于对权重进行标准化和进行前向传播计算。

这两个类都使用了torch库中的一些函数和类,如nn.Conv1d和nn.Conv2d,以及torch.functional中的F函数。它们还使用了一些其他的辅助函数和参数,如torch.mean、torch.std、torch.prod等。这些函数和类用于实现卷积操作和权重标准化操作,以及初始化权重和偏置等功能。

总的来说,base.py是一个实现了一维和二维卷积操作,并对权重进行标准化的程序文件。它提供了一些灵活的参数设置,可以根据需要进行调整和使用。

5.2 export.py


def export_formats():
    # YOLOv5 export formats
    x = [
        ['PyTorch', '-', '.pt', True, True],
        ['TorchScript', 'torchscript', '.torchscript', True, True],
        ['ONNX', 'onnx', '.onnx', True, True],
        ['OpenVINO', 'openvino', '_openvino_model', True, False],
        ['TensorRT', 'engine', '.engine', False, True],
        ['CoreML', 'coreml', '.mlmodel', True, False],
        ['TensorFlow SavedModel', 'saved_model', '_saved_model', True, True],
        ['TensorFlow GraphDef', 'pb', '.pb', True, True],
        ['TensorFlow Lite', 'tflite', '.tflite', True, False],
        ['TensorFlow Edge TPU', 'edgetpu', '_edgetpu.tflite', False, False],
        ['TensorFlow.js', 'tfjs', '_web_model', False, False],
        ['PaddlePaddle', 'paddle', '_paddle_model', True, True],]
    return pd.DataFrame(x, columns=['Format', 'Argument', 'Suffix', 'CPU', 'GPU'])


def try_export(inner_func):
    # YOLOv5 export decorator, i..e @try_export
    inner_args = get_default_args(inner_func)

    def outer_func(*args, **kwargs):
        prefix = inner_args['prefix']
        try:
            with Profile() as dt:
                f, model = inner_func(*args, **kwargs)
            LOGGER.info(f'{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)')
            return f, model
        except Exception as e:
            LOGGER.info(f'{prefix} export failure ❌ {dt.t:.1f}s: {e}')
            return None, None

    return outer_func


@try_export
def export_torchscript(model, im, file, optimize, prefix=colorstr('TorchScript:')):
    # YOLOv5 TorchScript model export
    LOGGER.info(f'\n{prefix} starting export with torch {torch.__version__}...')
    f = file.with_suffix('.torchscript')

    ts = torch.jit.trace(model, im, strict=False)
    d = {'shape': im.shape, 'stride': int(max(model.stride)), 'names': model.names}
    extra_files = {'config.txt': json.dumps(d)}  # torch._C.ExtraFilesMap()
    if optimize:  # https://pytorch.org/tutorials/recipes/mobile_interpreter.html
        optimize_for_mobile(ts)._save_for_lite_interpreter(str(f), _extra_files=extra_files)
    else:
        ts.save(str(f), _extra_files=extra_files)
    return f, None


@try_export
def export_onnx(model, im, file, opset, dynamic, simplify, prefix=colorstr('ONNX:')):
    # YOLOv5 ONNX export
    check_requirements('onnx>=1.12.0')
    import onnx

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

    output_names = ['output0', 'output1'] if isinstance(model, SegmentationModel) else ['output0']
    if dynamic:
        dynamic = {'images': {0: 'batch', 2: 'height', 3: 'width'}}  # shape(1,3,640,640)
        if isinstance(model, SegmentationModel):
            dynamic['output0'] = {0: 'batch', 1: 'anchors'}  # shape(1,25200,85)
            dynamic['output1'] = {0: 'batch', 2: 'mask_height', 3: 'mask_width'}  # shape(1,32,160,160)
        elif isinstance(model, DetectionModel):
            dynamic['output0'] = {0: 'batch', 1: 'anchors'}  # shape(1,25200,85)

    torch.onnx.export(
        model.cpu() if dynamic else model,  # --dynamic only compatible with cpu
        im.cpu() if dynamic else im,
        f,
        verbose=False,
        opset_version=opset,
        do_constant_folding=True,  # WARNING: DNN inference with torch>=1.12 may require do_constant_folding=False
        input_names=['images'],
        output_names=output_names,
        dynamic_axes=dynamic or None)

    # Checks
    model_onnx = onnx.load(f)  # load onnx model
    onnx.checker.check_model(model_onnx)  # check onnx model

    # Metadata
    d = {'stride': int(max(model.stride)), 'names': model.names}
    for k, v in d.items():
        meta = model_onnx.metadata_props.add()
        meta.key, meta.value = k, str(v)
    onnx.save(model_onnx, f)

    # Simplify
    if simplify:
        try:
            cuda = torch.cuda.is_available()
            check_requirements(('onnxruntime-gpu' if cuda else 'onnxruntime', 'onnx-simplifier>=0.4.1'))
            import onnxsim

            LOGGER.info(f'{prefix} simplifying with onnx-simplifier {onnxsim.__version__}...')
            model_onnx, check = onnxsim.simplify(model_onnx)
            ass

export.py是一个用于将YOLOv5 PyTorch模型导出为其他格式的程序文件。它支持导出为多种格式,包括TorchScript、ONNX、OpenVINO、TensorRT、CoreML、TensorFlow SavedModel、TensorFlow GraphDef、TensorFlow Lite、TensorFlow Edge TPU、TensorFlow.js和PaddlePaddle。该程序文件还提供了导出模型的示例用法和推理的示例用法。导出的模型可以用于推理任务。

该程序文件依赖于一些Python库,可以通过pip安装。在CPU环境下,可以使用以下命令安装依赖库:

$ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime openvino-dev tensorflow-cpu

在GPU环境下,可以使用以下命令安装依赖库:

$ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime-gpu openvino-dev tensorflow

使用该程序文件时,可以通过命令行参数指定要导出的模型文件和要导出的格式。例如,可以使用以下命令导出模型为TorchScript和ONNX格式:

$ python export.py --weights yolov5s.pt --include torchscript onnx

导出的模型可以在推理任务中使用。例如,可以使用以下命令进行推理:

$ python detect.py --weights yolov5s.pt

其中,yolov5s.pt是导出的模型文件。

另外,该程序文件还提供了导出模型为TensorFlow.js格式的示例用法。可以通过克隆tfjs-yolov5-example仓库并安装相关依赖来使用TensorFlow.js格式的模型。

总之,export.py是一个用于将YOLOv5 PyTorch模型导出为其他格式的程序文件,可以方便地将模型用于不同的推理任务。

5.3 model.py


class ModelProfiler:
    def __init__(self, model_name, input_shape):
        self.model_name = model_name
        self.input_shape = input_shape
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.model = None

    def load_model(self):
        self.model = timm.create_model(self.model_name, pretrained=False, features_only=True)
        self.model.to(self.device)
        self.model.eval()

    def print_model_info(self):
        print(self.model.feature_info.channels())
        for feature in self.model(self.dummy_input):
            print(feature.size())

    def profile_model(self):
        flops, params = profile(self.model.to(self.device), (self.dummy_input,), verbose=False)
        flops, params = clever_format([flops * 2, params], "%.3f")
        print('Total FLOPS: %s' % (flops))
        print('Total params: %s' % (params))

    def run(self):
        self.load_model()
        self.print_model_info()
        self.profile_model()


这个程序文件名为model.py,它的功能是使用torch和timm库来计算一个模型的FLOPS(浮点运算数)和参数数量。

首先,程序列出了timm库中可用的所有模型。然后,它创建了一个torch设备,如果有可用的cuda设备,则使用cuda,否则使用cpu。接下来,它创建了一个大小为(1, 3, 640, 640)的随机输入张量,并将其发送到设备上。

然后,程序使用timm库创建了一个名为’vovnet39a’的模型,该模型不使用预训练权重,并且只返回特征。然后,它将模型发送到设备上,并将其设置为评估模式。

接下来,程序打印了模型的特征通道数,并对输入张量进行了前向传播,打印了每个特征的大小。

最后,程序使用thop库的profile函数计算了模型在给定输入张量上的FLOPS和参数数量,并使用clever_format函数将其格式化为字符串。然后,它打印了总的FLOPS和参数数量。

总而言之,这个程序文件的功能是列出可用的模型,创建一个模型并计算其FLOPS和参数数量。

5.4 sgd_agc.py


class SGD_AGC(Optimizer):
    def __init__(self, params, lr=required, momentum=0, dampening=0,
                 weight_decay=0, nesterov=False, clipping=1e-2, eps=1e-3):
        # initialization code

    def __setstate__(self, state):
        # set state code

    @torch.no_grad()
    def step(self, closure=None):
        # optimization step code

6.系统整体结构

该程序是一个基于NFNet-BackBone的改进YOLOv5的手套缺陷检测系统。它包含了多个文件,每个文件都有不同的功能,共同构建了整个系统。

下面是每个文件的功能概述:

文件功能
agc.py实现了AGC类,用于梯度裁剪
base.py实现了一维和二维卷积操作,并对权重进行标准化
export.py将YOLOv5模型导出为其他格式的文件
model.py定义了模型的结构和参数初始化
sgd_agc.py实现了SGD_AGC类,用于训练模型
train.py加载数据集、构建模型、设置优化器和学习率调度器等,进行模型训练
ui.py使用YOLOv5模型进行目标检测,并将结果显示在图形界面上
utils.py包含了一些辅助函数,如计算单元范数等
val.py加载验证数据集,进行模型验证和评估
yolo.py实现了YOLOv5模型的前向传播和后处理过程
init.py初始化文件
classify/predict.py使用分类模型进行预测
classify/train.py训练分类模型
classify/val.py验证分类模型
models/common.py定义了一些通用的模型组件
models/experimental.py定义了一些实验性的模型组件
models/nfnet.py定义了NFNet模型
models/resnet.py定义了ResNet模型
models/tf.py定义了TensorFlow模型
models/yolo.py定义了YOLO模型
models/init.py初始化文件
segment/predict.py使用分割模型进行预测
segment/train.py训练分割模型
segment/val.py验证分割模型
utils/activations.py定义了一些激活函数
utils/augmentations.py定义了数据增强函数
utils/autoanchor.py定义了自动锚框生成函数
utils/autobatch.py定义了自动批次大小调整函数
utils/callbacks.py定义了一些回调函数
utils/dataloaders.py定义了数据加载器
utils/downloads.py定义了一些下载函数
utils/general.py定义了一些通用的辅助函数
utils/loss.py定义了一些损失函数
utils/metrics.py定义了一些评估指标
utils/plots.py定义了一些绘图函数
utils/torch_utils.py定义了一些与PyTorch相关的辅助函数
utils/triton.py定义了与Triton Inference Server相关的函数
utils/init.py初始化文件
utils/aws/resume.py定义了AWS上的模型恢复函数
utils/aws/init.py初始化文件
utils/flask_rest_api/example_request.py定义了Flask REST API的示例请求函数
utils/flask_rest_api/restapi.py定义了Flask REST API的接口函数
utils/loggers/init.py初始化文件
utils/loggers/clearml/clearml_utils.py定义了ClearML日志记录器的辅助函数
utils/loggers/clearml/hpo.py定义了ClearML日志记录器的超参数优化函数
utils/loggers/clearml/init.py初始化文件
utils/loggers/comet/comet_utils.py定义了Comet日志记录器的辅助函数
utils/loggers/comet/hpo.py定义了Comet日志记录器的超参数优化函数
utils/loggers/comet/init.py初始化文件
utils/loggers/wandb/wandb_utils.py定义了WandB日志记录器的辅助函数
utils/loggers/wandb/init.py初始化文件
utils/segment/augmentations.py定义了分割模型的数据增强函数
utils/segment/dataloaders.py定义了分割模型的数据加载器
utils/segment/general.py定义了分割模型的一些通用辅助函数
utils/segment/loss.py定义了分割模型的损失函数
utils/segment/metrics.py定义了分割模型的评估指标
utils/segment/plots.py定义了分割模型的绘图函数
utils/segment/init.py初始化文件

以上是每个文件的功能概述,用于构建基于NFNet-BackBone的改进YOLOv5的手套缺陷检测系统。

7.NFNet简介

目前计算机视觉中很多网络网络都是基于ResNet的变种,使用Batch Normalization(下文简称BN)进行训练。BN和残差结构的组合已经被业界证明十分有效,可以很容易地训练深层网络。BN的存在可以平滑loss使得更大学习率更大batch size的训练稳定进行,BN也有一定的正则化效果。不可否认,BN是非常有效的,但是它存在问题,论文中总结了三个非常典型的缺点如下。

(1)BN是带来了额外的不小的计算开销。 计算均值等需要在内存中保存临时变量,导致了内存开销的增大,并且,在某些网络中增加了梯度评估的时间。
(2)BN造成了模型训练和推理时的行为差异。 在Pytorch中实现上时model.train()和model.eval()的差异,也就是说BN带来了需要调整的隐藏超参数。
BN打破了小批量样本之间的独立性。 这就是说,其实选择哪些样本其实挺重要的。
这三个特性导致了一系列不好的后果。一方面,具体而言,研究者已经发现,使用BN的网络通常难以在不同的硬件设备之间精确复制,BN往往就是这种细微错误的原因,特别是分布式训练时。分布式训练时,如果数据并行,在不同的机器上都有BN层,那么就需要将信号发送到BN层,然后在BN层之间传递均值等统计信息,不这样的话这个批次就没有均值和方差,这使得网络可以欺骗某些损失函数,造成信息泄露等问题。另一方面,BN层对batch size是非常敏感的,bs过小BN网络就会效果很差,这是因为bs很小的时候样本很少,均值其实是噪声的近似。
在这里插入图片描述

因此,尽管BN推动了深度学习的发展,但是从长远来看,它其实阻碍了深度网络的进步。其实如上图所示,业内已经出现了Layer Norm、Group Norm等BN的替代品,它们获得了更好的精度表现但也带来了不小的计算量。幸运的是,这些年也出现了一些具有代表性的无BN的网络,这些工作的核心思路就是通过抑制残差分支上隐藏激活层的尺度从而训练非常深的无BN的ResNet网络。最简单的实现方法就是在每个残差分支的末尾引入一个初始值为0的可学习标量,不过这种技巧的精度表现并不好。另一些研究表明,ReLU这个激活函数会带来均值偏移现象,这导致不同样本的隐藏激活值随着网络的深度增加越来越相关。此前已经有工作提出了Normalize-Free ResNets,它在初始化的时候抑制残差分支并且使用Scaled Weight Standardization来消除均值偏移现象,通过额外的正则化,这种网络在ImageNet上获得了和有BN网络相媲美的效果,但是它在大的bs时训练并不稳定并且其实距离目前的SOTA也就是EfficientNet还有一定距离。因此,这篇论文提出了解决它劣势的新方法,称为NFNet,论文的主要贡献如下。

High-Performance Large-Scale Image Recognition Without Normalization提出Adaptive Gradient Clipping (AGC)模块,该方法基于逐单元梯度范数与参数范数的单位比例来裁剪梯度,实验表明,AGC允许NFNet以大的bs和强数据增强条件进行训练。
设计了一系列的Normalize-Free ResNets,称为NFNets,最简单版本的NFNet达到了EfficientNet-B7的精度,但训练速度是8.7倍。最优版本的NFNet在不适用额外数据的情况下实现了新的SOTA。
在3亿张带有标签的大型私有数据集进行预训练后,对ImageNet进行微调时,NFNet与批归一化网络相比,其验证准确率要高得多。最佳模型经过微调后可达到89.2%的top-1 accuracy。
这篇文章后面两节主要是叙述BN的效果,以及前人如何在去除了BN之后保留这些优势所做的工作,这里感兴趣的可以查看文章的第二节和第三节,我这里就直接来将这篇论文的方法论了。

AGC(自适应梯度裁剪模块)

梯度裁剪技术常用于语言模型来稳定训练,最近的研究表明,与梯度下降相比,它允许以更大的学习率进行训练从而加速收敛。这对于条件较差的损失或大批量训练尤为重要,因为在这些设置中,最佳学习率往往会受到最大学习率的限制。因此作者假定梯度裁剪有利于NFNet的大批尺寸训练。梯度裁剪往往是对梯度的范数进行约束来实现的,对梯度向量G = ∂ L / ∂ θ G=\partial L / \partial \thetaG=∂L/∂θ而言,L LL表示损失值,θ \thetaθ则表示模型所有参数向量,标准的裁剪算法会在更新θ \thetaθ之前以如下的公式裁剪梯度。

在这里插入图片描述

使用上述的AGC模块,NFNet能够以高达4096的批尺寸训练同时使用RandAugment这样的数据增强策略,不适用AGC模块,NFNet是无法这样训练的。注意,最优裁剪参数λ \lambdaλ可能取决于优化器的选择,学习率和批大尺寸。经验上,batch size越大,λ \lambdaλ应该越小。
在这里插入图片描述

上图是论文针对AGC做的两个消融实验,左图a表示使用BN的REsNet以及使用和不使用AGC的NFNet之间的对比,实验表明AGC使得NFNet有着媲美BN网络的效果,而且批尺寸越小,AGC收益越低。右图b则表示不同批尺寸不同λ \lambdaλ选择的效果,结果表明,当批尺寸较大的时候,应该选择较小的λ \lambdaλ以稳定训练。

后续作者也对AGC的作用层进行了消融实验,得到一些结论,比如对最终的线性层裁剪是不需要的等。

NFNet

上一节提到了可以让网络以较大批尺寸和较强数据增强方法进行训练的梯度裁剪手段,同时为了配置这个AGC模块,论文也对模型结构进行了探索。EfficientNet是当前的分类SOTA网络,它基于NAS技术搜索而得,拥有很低的理论计算复杂度,但是实际硬件上的表现并不是很好,所以作者这边选择了手工探索模型空间以获得较好的表现,在SE-ResNeXt-D模型的基础上,对其先是应用了前人的Normalizer-Free配置,修改了宽度和深度模式,以及第二个空间卷积(下图表示这种配置上的修改,更具体的可以查看论文附加材料,给出的图如下)。接着,应用AGC到除了最后的线性层上的每一层,得到最终的NfNet配置。

在这里插入图片描述

得到的NfNets如下表所示,构成一个网络系列。

8.改进后的NFNet骨干网络的YOLOv5

引入的改进主要围绕提高模型的特征提取能力、计算效率和检测精度。NFNet原本的设计是为了图像分类任务,而在我们的手套缺陷检测任务中,我们需要更细粒度的特征提取能力。以下是具体的改进措施:

特征金字塔网络(FPN)的优化:

原始YOLOv5使用的FPN在融合不同尺度的特征时可能会丢失一些细节。我们通过引入更多层次的特征融合,以及使用更精细的上采样技术来保持更多的细节信息。
残差结构的改进:

NFNet的残差连接骨干网络在深层网络中可能会导致梯度消失或爆炸。我们引入了带有正则化效果的预激活层(Pre-activation layers),以提高模型训练的稳定性。
注意力机制的集成:

我们在网络的每个残差块中加入了注意力模块,如SE(Squeeze-and-Excitation)块,以提高模型对关键特征的敏感度,这在缺陷检测中是至关重要的。
在这里插入图片描述
深度可分离卷积的应用:

为了降低计算复杂度,我们在部分卷积层中替换为深度可分离卷积,它能够减少参数数量并提高运行速度,同时保持了网络的性能。
锚框机制的优化:

在手套缺陷检测中,缺陷的形状和大小多样,我们优化了锚框生成算法,使其能够更好地适配不同类型的缺陷。

9.训练结果可视化分析

评价指标

epoch: 训练的轮数
train/loss: 训练损失
test/loss: 测试损失
metrics/accuracy_top1: Top-1准确率,即模型预测最有可能的类别的正确率
metrics/accuracy_top5: Top-5准确率,即模型预测的前五个最有可能的类别中包含正确类别的比例
lr/0: 学习率

可视化分析
import matplotlib.pyplot as plt

# Set the style
plt.style.use('seaborn-darkgrid')

# Plot the training and test loss
plt.figure(figsize=(14, 7))
plt.plot(results_df['epoch'], results_df['train/loss'], label='Training Loss', marker='o')
plt.plot(results_df['epoch'], results_df['test/loss'], label='Test Loss', marker='x')

# Add titles and labels
plt.title('Training and Test Loss Over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()

在这里插入图片描述
图表成功显示了训练和测试损失随着训练轮数的变化。

从图表中,我们可以看到几个关键点:

损失下降趋势:训练损失(Training Loss)随着训练轮数的增加而持续下降,这表明模型在训练数据上学到了有效的特征,并且正在变得越来越好地拟合这些数据。

过拟合的迹象:测试损失(Test Loss)在初期下降之后开始上升,这是过拟合的典型迹象。这意味着模型在训练数据上表现得很好,但在未见过的测试数据上表现不佳。在这种情况下,模型可能学习到了训练数据中的噪声,而没有抓住泛化的特征。

损失波动:测试损失呈现一定的波动性,这可能是由数据集的难易程度不均或者模型训练过程中的学习率调整引起的。

早期停止的可能性:在某个点后,测试损失似乎不再降低,反而有所上升。这表明可以考虑在训练早期的某个点进行早期停止(early stopping)以防止过拟合。

接下来,我将绘制Top-1和Top-5准确率随训练轮数变化的图表,并对这些数据进行分析。​

# Plot the cleaned training and test loss
plt.figure(figsize=(14, 7))
plt.plot(results_df['epoch'], results_df['train/loss'], label='Training Loss', marker='o')
plt.plot(results_df['epoch'], results_df['test/loss'], label='Test Loss', marker='x')

# Add titles and labels
plt.title('Training and Test Loss Over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()

在这里插入图片描述
我们现在看到了Top-1和Top-5准确率随训练轮数的变化。

以下是从这个图表中观察到的关键分析点:

准确率提升:与损失函数的下降相对应,我们看到Top-1和Top-5准确率随着训练轮数的增加而提升。这表明模型在学习过程中能更好地识别和分类训练集中的图像。

Top-5准确率高于Top-1:正如预期的那样,Top-5准确率始终高于Top-1准确率。因为Top-5准确率考虑了模型的前五个最有可能的预测,所以这一指标通常比只考虑最有可能预测的Top-1准确率更容易达到更高的数值。

准确率趋于稳定:在最初的几个训练轮数后,准确率的提升变得平缓,特别是对于Top-5准确率,这表明模型可能开始达到其性能的瓶颈。在这种情况下,模型在对新数据的预测上的改进变得微小。

性能潜力:尽管Top-5准确率相对较高,但Top-1准确率的提升空间仍然存在。这可能意味着模型在区分最有可能的类别上仍然存在困难,这是一个改进的潜在领域。

训练的平衡:由于测试损失的增加和准确率的提升趋缓,我们需要考虑训练过程的平衡。可能需要实现一些正则化技术,如Dropout或权重衰减,以防止过拟合并改善模型在未见数据上的泛化能力。

最后,我们将绘制学习率随训练轮数变化的图表,并对这些数据进行分析。​

# Plot the Top-1 and Top-5 accuracy
plt.figure(figsize=(14, 7))
plt.plot(results_df['epoch'], results_df['metrics/accuracy_top1'], label='Top-1 Accuracy', marker='o')
plt.plot(results_df['epoch'], results_df['metrics/accuracy_top5'], label='Top-5 Accuracy', marker='x')

# Add titles and labels
plt.title('Top-1 and Top-5 Accuracy Over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

学习率随着训练轮数的变化图表显示了以下关键点:

学习率衰减:图表显示,学习率随着训练轮数逐渐降低。学习率的衰减是训练深度学习模型时常用的技术,它有助于模型在训练的早期快速收敛,并在训练后期通过较小的学习率进行更精细的权重更新。

平滑收敛:递减的学习率可以帮助模型避免在损失面上的大幅波动,从而平滑收敛到最小值。这通常表明模型在学习过程中更稳定。

调整学习率策略:尽管学习率按预期下降,但考虑到测试损失和准确率的波动,可能需要调整学习率衰减的策略或速度。例如,可能需要在测试损失首次增加时降低学习率,或者在准确率停止提高时减小学习率的衰减速度。

超参数优化:学习率的初始值和衰减速度是影响训练过程的关键超参数。调整这些参数可能需要通过多次实验来找到最优配置。

与过拟合相关:如果学习率降低得太慢,可能导致模型在训练数据上过拟合,因为它可以继续以较大的步长调整权重,从而过度拟合训练数据中的特定模式和噪声。

整体而言,学习率的调整对于模型的训练至关重要。适当的学习率和衰减策略可以帮助模型更好地泛化,同时避免过拟合。结合前面的损失和准确率分析,我们可以得出结论,模型训练的过程中需要精心平衡各种超参数,以确保模型能够在未见过的数据上表现得足够好。

总体来说,有效的可视化可以帮助我们直观地理解模型在训练过程中的表现,以及可能需要进行哪些调整。通过这三个图表,我们能够识别出可能的问题(如过拟合),并提出潜在的解决方案,如早期停止、正则化技术的应用,或者超参数(特别是学习率)的进一步优化。每次实验后,应仔细评估模型的表现,并根据需要调整训练策略。​

其他结果分析

缺陷类型
从提供的图片中,我们可以观察到不同类型的缺陷,例如:
在这里插入图片描述

切口和裂缝(例如cichuan或siliekou):这些可能由机械应力或生产过程中的错误造成。
破洞(例如shaoshang):这可能是材料质量问题或机械损坏的结果。
污点(例如wu):这些可能是制造过程中的污染或染料问题。
脱落(例如xiaoquekou):这可能指的是材料的部分脱落或缺失。
分析手套缺陷检测系统
检测性能:系统能够识别和标记出各种类型的缺陷。这说明了系统的多功能性和灵活性。

精确度与召回率:在分析缺陷检测系统时,关键的性能指标包括精确度(检测正确的缺陷占所有检测到的缺陷的比例)和召回率(检测正确的缺陷占所有实际缺陷的比例)。从图片中我们无法直接评估这些指标,但它们是评估系统性能的关键。

假阳性与假阴性:在任何检测系统中,假阳性(错误标记为缺陷)和假阴性(未能检测到的实际缺陷)都是重要的考虑因素。系统应最小化这两种错误,以确保高质量的检测结果。

实时性能:在实际应用中,系统的实时性能(即快速准确地检测缺陷)也至关重要。这涉及到模型的复杂性和优化,以及计算资源的配置。

模型优化:NFNet-BackBone和YOLOv5的结合可能是为了利用两者的优势:NFNet的准确性和YOLOv5的速度。对这种结构的选择表明了对速度和准确性之间平衡的考虑。

数据集和训练:系统性能的另一个关键方面是训练数据集的质量和多样性。数据集应涵盖所有预期类型的缺陷,并在各种条件下进行拍摄,以确保模型的泛化能力。

部署和实际应用:最终,系统在实际生产环境中的部署需要考虑环境因素,如光线、背景变化等,这些都可能影响检测的准确性。

10.系统整合

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

参考博客《基于NFNet-BackBone的改进YOLOv5的手套缺陷检测系统》

11.参考文献


[1]杨晓楠,孙博,郎燕生.基于深度学习的特高压直流闭锁故障智能调度决策[J].中国电力.2020,(6).DOI:10.11930/j.issn.1004-9649.201910138 .

[2]倪兆麟,吕清松,许峰川,等.机器视觉系统中的LED线光源设计[J].电子器件.2020,(4).DOI:10.3969/j.issn.1005-9490.2020.04.038 .

[3]王天怡,王鑫,曹兴强,等.基于机器视觉的内层包装缺陷检测光源的优化[J].包装工程.2019,(17).DOI:10.19554/j.cnki.1001-3563.2019.17.025 .

[4]陈林.机器视觉及其在制造业中的应用[J].内燃机与配件.2019,(23).

[5]苏朝阳,马万太,黄磊,等.基于图像处理库的机器视觉检测系统的研究[J].机械制造与自动化.2019,(1).DOI:10.19344/j.cnki.issn1671-5276.2019.01.051 .

[6]贾文娟,张煜东.自编码器理论与方法综述[J].计算机系统应用.2018,(5).DOI:10.15888/j.cnki.csa.006336 .

[7]李文文,杨先海,潘广堂.一种基于改进Canny算法的塑胶手套残次品检测方法[J].塑料科技.2017,(10).DOI:10.15925/j.cnki.issn1005-3360.2017.10.017 .

[8]贺振东,王耀南,刘洁,等.基于背景差分的高铁钢轨表面缺陷图像分割[J].仪器仪表学报.2016,(3).DOI:10.3969/j.issn.0254-3087.2016.03.022 .

[9]周显恩,王耀南,朱青,等.基于机器视觉的瓶口缺陷检测方法研究[J].电子测量与仪器学报.2016,(5).DOI:10.13382/j.jemi.2016.05.006 .

[10]王耀南,陈铁健,贺振东,等.智能制造装备视觉检测控制方法综述[J].控制理论与应用.2015,(3).DOI:10.7641/CTA.2015.40169 .

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值