【课程总结】Day12:YOLO的深入了解

前言

在【课程总结】Day11(下):YOLO的入门使用一节中,我们已经了解YOLO的使用方法,使用过程非常简单,训练时只需要三行代码:引入YOLO,构建模型,训练模型;预测时也同样简单,只需要两行代码:引入YOLO,预测图像即可。以上过程简单主要是ultralytics的代码库已经做了封装,使得使用者集中精力在模型训练和预测上。

为了更加深入了解YOLO的实现原理,本章内容将对YOLO的工程结构、模型构建过程、模型训练过程尝试深入探究。

YOLO项目的工程结构

|- ultralytics/|- assets/    # 存放项目中使用的资源文件,如图像、样本数据等。|- cfg/     # 存放模型配置文件,包括不同模型的配置信息,如网络结构、超参数等。|- data/    # 存放数据集文件和数据处理相关的代码。|- engine/  # 存放训练和推理引擎的代码,包括训练、测试、评估等功能的实现。|- hub/     # 存放模型库相关的代码和模型文件,用于快速调用和使用预训练模型。|- models/  # 存放模型的定义和实现代码,包括不同模型的网络结构和相关函数。|- nn/      # 存放神经网络模块的代码,包括各种层次的定义和实现。|- solutions/ # 存放解决方案相关的代码和实现,用于特定问题或任务的解决方案。|- trackers/  # 存放目标跟踪相关的代码和实现,包括目标追踪算法的实现。|- utils/   # 存放通用的工具函数和辅助函数,用于项目中的各种功能和任务。

进一步查看cfg目录的内容如下:

- ultralytics|- cfg|- datasets  # 数据集处理和加载相关文件|- default.yaml  # 默认配置信息文件|- models  # 包含不同模型结构的配置文件|- yolov8-cls-resnet101.yaml  # 定义 YOLOv8 模型结构的配置文件(ResNet-101 版本)|- yolov8-cls-resnet50.yaml  # 定义 YOLOv8 模型结构的配置文件(ResNet-50 版本)|- yolov8-cls.yaml  # 定义 YOLOv8 模型结构的配置文件|- ...  # 其他 YOLOv8 模型版本的配置文件|- trackers  # 目标跟踪相关文件

YOLO的yaml文件解析

yaml文件内容

以yolov8.yaml为例,其内容如下:

nc: 80 # 类别数目,nc代表"number of classes",即模型用于检测的对象类别总数。
scales: # 模型复合缩放常数,例如 'model=yolov8n.yaml' 将调用带有 'n' 缩放的 yolov8.yaml# [depth, width, max_channels]n: [0.33, 0.25, 1024]  # YOLOv8n概览:225层, 3157200参数, 3157184梯度, 8.9 GFLOPss: [0.33, 0.50, 1024]  # YOLOv8s概览:225层, 11166560参数, 11166544梯度, 28.8 GFLOPsm: [0.67, 0.75, 768]   # YOLOv8m概览:295层, 25902640参数, 25902624梯度, 79.3 GFLOPsl: [1.00, 1.00, 512]   # YOLOv8l概览:365层, 43691520参数, 43691504梯度, 165.7 GFLOPsx: [1.00, 1.25, 512]   # YOLOv8x概览:365层, 68229648参数, 68229632梯度, 258.5 GFLOPs# YOLOv8.0n 骨干层
backbone:# [from, repeats, module, args]- [-1, 1, Conv, [64, 3, 2]]   # 0-P1/2 第0层,-1代表将上层的输入作为本层的输入。第0层的输入是640*640*3的图像。Conv代表卷积层,相应的参数:64代表输出通道数,3代表卷积核大小k,2代表stride步长。- [-1, 1, Conv, [128, 3, 2]]  # 1-P2/4 第1层,本层和上一层是一样的操作(128代表输出通道数,3代表卷积核大小k,2代表stride步长)- [-1, 3, C2f, [128, True]]   #        第2层,本层是C2f模块,3代表本层重复3次。128代表输出通道数,True表示Bottleneck有shortcut。- [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8 第3层,进行卷积操作(256代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为80*80*256(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样),特征图的长宽已经变成输入图像的1/8。- [-1, 6, C2f, [256, True]]   #        第4层,本层是C2f模块,可以参考第2层的讲解。6代表本层重复6次。256代表输出通道数,True表示Bottleneck有shortcut。经过这层之后,特征图尺寸依旧是80*80*256。- [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16第5层,进行卷积操作(512代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为40*40*512(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样),特征图的长宽已经变成输入图像的1/16。- [-1, 6, C2f, [512, True]]   #        第6层,本层是C2f模块,可以参考第2层的讲解。6代表本层重复6次。512代表输出通道数,True表示Bottleneck有shortcut。经过这层之后,特征图尺寸依旧是40*40*512。- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32第7层,进行卷积操作(1024代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为20*20*1024(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样),特征图的长宽已经变成输入图像的1/32。- [-1, 3, C2f, [1024, True]]  #        第8层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。1024代表输出通道数,True表示Bottleneck有shortcut。经过这层之后,特征图尺寸依旧是20*20*1024。- [-1, 1, SPPF, [1024, 5]] # 9         第9层,本层是快速空间金字塔池化层(SPPF)。1024代表输出通道数,5代表池化核大小k。结合模块结构图和代码可以看出,最后concat得到的特征图尺寸是20*20*(512*4),经过一次Conv得到20*20*1024。# YOLOv8.0n 头部层
head:- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 第10层,本层是上采样层。-1代表将上层的输出作为本层的输入。None代表上采样的size(输出尺寸)不指定。2代表scale_factor=2,表示输出的尺寸是输入尺寸的2倍。nearest代表使用的上采样算法为最近邻插值算法。经过这层之后,特征图的长和宽变成原来的两倍,通道数不变,所以最终尺寸为40*40*1024。- [[-1, 6], 1, Concat, [1]]  # cat backbone P4 第11层,本层是concat层,[-1, 6]代表将上层和第6层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是40*40*1024,第6层的输出是40*40*512,最终本层的输出尺寸为40*40*1536。- [-1, 3, C2f, [512]]  # 12 					 第12层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。512代表输出通道数。与Backbone中C2f不同的是,此处的C2f的bottleneck模块的shortcut=False。- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 第13层,本层也是上采样层(参考第10层)。经过这层之后,特征图的长和宽变成原来的两倍,通道数不变,所以最终尺寸为80*80*512。- [[-1, 4], 1, Concat, [1]]  # cat backbone P3 第14层,本层是concat层,[-1, 4]代表将上层和第4层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是80*80*512,第6层的输出是80*80*256,最终本层的输出尺寸为80*80*768。- [-1, 3, C2f, [256]]  # 15 (P3/8-small)       第15层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。256代表输出通道数。经过这层之后,特征图尺寸变为80*80*256,特征图的长宽已经变成输入图像的1/8。- [-1, 1, Conv, [256, 3, 2]] #                 第16层,进行卷积操作(256代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为40*40*256(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样)。- [[-1, 12], 1, Concat, [1]]  # cat head P4    第17层,本层是concat层,[-1, 12]代表将上层和第12层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是40*40*256,第12层的输出是40*40*512,最终本层的输出尺寸为40*40*768。- [-1, 3, C2f, [512]]  # 18 (P4/16-medium)     第18层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。512代表输出通道数。经过这层之后,特征图尺寸变为40*40*512,特征图的长宽已经变成输入图像的1/16。- [-1, 1, Conv, [512, 3, 2]] #                 第19层,进行卷积操作(512代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为20*20*512(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样)。- [[-1, 9], 1, Concat, [1]]  # cat head P5     第20层,本层是concat层,[-1, 9]代表将上层和第9层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是20*20*512,第9层的输出是20*20*1024,最终本层的输出尺寸为20*20*1536。- [-1, 3, C2f, [1024]]  # 21 (P5/32-large)     第21层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。1024代表输出通道数。经过这层之后,特征图尺寸变为20*20*1024,特征图的长宽已经变成输入图像的1/32。- [[15, 18, 21], 1, Detect, [nc]]  # Detect(P3, P4, P5) 第20层,本层是Detect层,[15, 18, 21]代表将第15、18、21层的输出(分别是80*80*256、40*40*512、20*20*1024)作为本层的输入。nc是数据集的类别数。
yaml文件解析

上述文件包含了 YOLOv8 模型的配置信息,其中包括了模型的类别数目、模型复合缩放常数、主干网络结构和头部网络结构。具体来说:

  • nc 表示模型的类别数目为 1000。
  • scales 包含了不同缩放常数对应的模型结构参数。
    • 子参数:n, s, m, l, x表示不同的模型尺寸,每个尺寸都有对应的depth(深度)、width(宽度)和max_channels(最大通道数)。
    • depth: 表示深度因子,用来控制一些特定模块的数量的,模块数量多网络深度就深;
    • width: 表示宽度因子,用来控制整个网络结构的通道数量,通道数量越多,网络就看上去更胖更宽;
    • max_channels: 最大通道数,为了动态地调整网络的复杂性。在 YOLO 的早期版本中,网络中的每个层都是固定的,这意味着每个层的通道数也是固定的。但在 YOLOv8 中,为了增加网络的灵活性并使其能够更好地适应不同的任务和数据集,引入了 max_channels 参数。
  • backbone 定义了 YOLOv8.0n 模型的主干网络结构,包括了卷积层、C2f 模块等。
    • from(来自)
      • 这个字段表示当前层连接到的上一层的索引。通常,-1 表示连接到上一层,0 表示连接到输入数据。
      • 例如,[-1, 1, Conv, [64, 3, 2]] 表示当前层连接到上一层,即前一层的输出作为当前层的输入。
    • repeats(重复次数)
      • 这个字段表示当前层的模块(module)被重复使用的次数。
      • 例如,[-1, 3, C2f, [128, True]] 表示当前层的模块 C2f 会被重复使用 3 次。
    • module(模块)
      • 这个字段表示当前层使用的模块类型,如 Conv(卷积层)、C2f 等。
      • 例如,[-1, 1, Conv, [64, 3, 2]] 中的 Conv 表示当前层使用的是卷积层。
    • args(参数)
      • 这个字段包含了当前层模块的参数,例如卷积层的通道数、卷积核大小、步长等。
      • 例如,[-1, 1, Conv, [64, 3, 2]] 中的 [64, 3, 2] 表示卷积层的通道数为 64,卷积核大小为 3,步长为 2。
YOLOv8 模型结构

上述yaml定义的模型结构,使用图示显示如下:

主要组成部分:

  • Backbone(主干网络)
  主干网络是模型的基础,负责从输入图像中提取特征。这些特征是后续网络层进行目标检测的基础。
  • Head(头部网络)
  头部网络是目标检测模型的决策部分,负责产生最终的检测结果。
  • ConvModule
  包含卷积层、BN(批量归一化)和激活函数(如SiLU),用于提取特征。
  • DarknetBottleneck:
  通过residual connections(残差结构)增加网络深度,同时保持效率。
  • CSP Layer:
  CSP结构的变体,通过部分连接来提高模型的训练效率。
  • Concat:
  特征图拼接,用于合并不同层的特征。
  • Upsample:
  上采样操作,增加特征图的空间分辨率。

主要过程为:
1.多层卷积:图片输入到主干网络,经过P1-P5层卷积后,在第9层通过SPPF模块进行特征提取。
2.上采样和连接:经过SPPF后进行Upsample上采样操作,然后与第6层进行concat操作,再进行C2f模块特征提取。
类似的:

  • 在14层与第4层进行concat操作,再进行C2f模块特征提取;
  • 在20层与第9层进行concat操作,再进行C2f模块特征提取。

3.目标检测:最后将15层、18层、21层分别经过Detect模块进行目标检测。

YOLO的模型构建过程解析

代码调用时序

构建模型的核心代码

通过查看上述YOLO构建模型过程,其核心代码主要是以下两处:
核心代码1:self._smart_load()

    def _new(self, cfg: str, task=None, model=None, verbose=False) -> None:# 以上部分省略...self.model = (model or self._smart_load("model"))(cfg_dict, verbose=verbose and RANK == -1)  # 核心代码self.overrides["model"] = self.cfgself.overrides["task"] = self.taskself.model.args = {**DEFAULT_CFG_DICT, **self.overrides}  # 核心代码# 以下部分省略...

代码解析:
self.model = (model or self._smart_load("model"))(cfg_dict, verbose=verbose and RANK == -1)

  • 特性:使用 Python 的条件表达式,选择不同的函数或对象进行赋值
  • 解释说明:
    • model or self._smart_load("model") 是一个条件表达式,它会根据 model 变量是否为真值来选择赋值的对象。
    • 如果 model 为真值(非空),则 self.model 将被赋值为 model
    • 如果 model 为假值(空),则会调用 self._smart_load("model") 方法来加载模型,并将返回的对象赋值给 self.model
    • 然后与,第二个括号(cfg_dict, verbose=verbose and RANK == -1) 拼接,形成self.model(cfg_dict, verbose=verbose and RANK == -1)

self.model.args = {**DEFAULT_CFG_DICT, **self.overrides}

  • 特性:使用 Python 中的字典合并操作符 ** 来将两个字典合并。
  • 解释说明:
    • DEFAULT_CFG_DICTself.overrides 是两个字典。
    • **DEFAULT_CFG_DICTDEFAULT_CFG_DICT 字典中的所有键值对解包并添加到新的字典中。
    • **self.overrides 同样将 self.overrides 字典中的所有键值对解包并添加到同一个新的字典中。
    • 最终,{\*\*DEFAULT_CFG_DICT, \*\*self.overrides} 表示将这两个字典合并成一个新的字典,其中 self.overrides中的键值对将覆盖 DEFAULT_CFG_DICT 中的同名键值对。
  • 举例:
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
merged_dict = {**dict1, **dict2}
print(merged_dict)
# 运行结果:
# {'a': 1, 'b': 3, 'c': 4}

核心代码2:self.task_map[self.task][key]

def task_map(self):"""Map head to model, trainer, validator, and predictor classes."""return {"classify": {"model": ClassificationModel,"trainer": yolo.classify.ClassificationTrainer,"validator": yolo.classify.ClassificationValidator,"predictor": yolo.classify.ClassificationPredictor,},"detect": {"model": DetectionModel,"trainer": yolo.detect.DetectionTrainer,"validator": yolo.detect.DetectionValidator,"predictor": yolo.detect.DetectionPredictor,},# 以下部分省略...}

代码解析:

  • 以上代码根据传入的self.task类型(例如:classify),来创建对应的模型、训练器、验证器、预测器类。
  • 特性:使用了 Python 中的字典,将字符串键与类对象值进行关联,以实现将类对象映射到不同的功能模块
  • 字典与类对象映射的举例
class Dog:def __init__(self, name):self.name = name
class Cat:def __init__(self, name):self.name = name
# 创建一个字典,将字符串键映射到不同的类对象
animal_map = {"dog": Dog,"cat": Cat,
}
# 根据键来实例化不同的类对象
my_dog = animal_map["dog"]("Buddy")
my_cat = animaljson_map["cat"]("Whiskers")
print(my_dog.name)  # 输出: Buddy
print(my_cat.name)  # 输出: Whiskers

核心代码3:parse_model()函数

def parse_model(d, ch, verbose=True):  # model_dict, input_channels(3)"""Parse a YOLO model.yaml dictionary into a PyTorch model."""import ast# Argsmax_channels = float("inf")nc, act, scales = (d.get(x) for x in ("nc", "activation", "scales"))depth, width, kpt_shape = (d.get(x, 1.0) for x in ("depth_multiple", "width_multiple", "kpt_shape"))if scales:scale = d.get("scale")if not scale:scale = tuple(scales.keys())[0]LOGGER.warning(f"WARNING ⚠️ no model scale passed. Assuming scale='{scale}'.")depth, width, max_channels = scales[scale]if act:Conv.default_act = eval(act)  # redefine default activation, i.e. Conv.default_act = nn.SiLU()if verbose:LOGGER.info(f"{colorstr('activation:')} {act}")  # printch = [ch]layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch outfor i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]):  # from, number, module, argsm = getattr(torch.nn, m[3:]) if "nn." in m else globals()[m]  # get modulefor j, a in enumerate(args):if isinstance(a, str):with contextlib.suppress(ValueError):args[j] = locals()[a] if a in locals() else ast.literal_eval(a)n = n_ = max(round(n * depth), 1) if n > 1 else n  # depth gainif m in {Classify,Conv,ConvTranspose,GhostConv,Bottleneck,GhostBottleneck,SPP,SPPF,DWConv,Focus,BottleneckCSP,C1,C2,C2f,RepNCSPELAN4,ELAN1,ADown,AConv,SPPELAN,C2fAttn,C3,C3TR,C3Ghost,nn.ConvTranspose2d,DWConvTranspose2d,C3x,RepC3,PSA,SCDown,C2fCIB,}:c1, c2 = ch[f], args[0]if c2 != nc:  # if c2 not equal to number of classes (i.e. for Classify() output)c2 = make_divisible(min(c2, max_channels) * width, 8)if m is C2fAttn:args[1] = make_divisible(min(args[1], max_channels // 2) * width, 8)  # embed channelsargs[2] = int(max(round(min(args[2], max_channels // 2 // 32)) * width, 1) if args[2] > 1 else args[2])  # num headsargs = [c1, c2, *args[1:]]# ...((篇幅原因,代码已做省略))m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)  # modulet = str(m)[8:-2].replace("__main__.", "")  # module typem.np = sum(x.numel() for x in m_.parameters())  # number paramsm_.i, m_.f, m_.type = i, f, t  # attach index, 'from' index, typeif verbose:LOGGER.info(f"{i:>3}{str(f):>20}{n_:>3}{m.np:10.0f}  {t:<45}{str(args):<30}")  # printsave.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1)  # append to savelistlayers.append(m_)if i == 0:ch = []ch.append(c2)return nn.Sequential(*layers), sorted(save)

代码解析:

  • 参数设置:
    • 以上代码定义了一些初始参数,如 max_channels、nc(类别数)、act(激活函数类型)、scales(模型尺度)、depth(深度倍数)、width(宽度倍数)等。
    • 根据配置文件中的 scales 参数设置模型的深度、宽度和最大通道数。
  • 激活函数设置:
    • 如果配置文件中指定了激活函数类型 act,则重新定义默认激活函数为指定的激活函数(如 nn.SiLU())。
  • 模型解析:
    • 遍历配置文件中的 backbone 和 head 部分,解析每个层的信息。
    • 根据模块类型 m(如 Conv、Bottleneck 等)选择相应的处理逻辑,设置输入通道 c1、输出通道 c2 以及其他参数。
  • 创建模块:
    • 根据解析得到的参数和模块类型,通过nn.Sequential(*layers)创建相应的层,最终返回解析后的模型。

YOLO的模型训练过程解析

代码调用时序

构建模型的核心代码

核心代码1:ClassificationDataset数据集

def __init__(self, root, args, augment=False, prefix=""):# 以上内容省略self.torch_transforms = (classify_augmentations(size=args.imgsz,scale=scale,hflip=args.fliplr,vflip=args.flipud,erasing=args.erasing,auto_augment=args.auto_augment,hsv_h=args.hsv_h,hsv_s=args.hsv_s,hsv_v=args.hsv_v,)if augmentelse classify_transforms(size=args.imgsz, crop_fraction=args.crop_fraction))

代码解析:

  • 以上代码是ClassificationDataset数据集的初始化函数
  • self.torch_transforms = …:
    • 根据条件选择不同的数据增强方法:
    • 如果 augment 为 True,则调用 classify_augmentations() 方法进行数据增强。
    • 否则,调用 classify_transforms() 方法进行数据转换。
  • 除此之外,该数据集按照标准规范,实现了__getitem__(),len()等回调函数。

核心代码2:筹备训练

    def _setup_train(self, world_size):"""Builds dataloaders and optimizer on correct rank process."""# Modelself.run_callbacks("on_pretrain_routine_start")ckpt = self.setup_model()self.model = self.model.to(self.device)self.set_model_attributes()# Freeze layers# 篇幅原因,代码已省略# Check AMP# 篇幅原因,代码已省略# Check imgszgs = max(int(self.model.stride.max() if hasattr(self.model, "stride") else 32), 32)  # grid size (max stride)self.args.imgsz = check_imgsz(self.args.imgsz, stride=gs, floor=gs, max_dim=1)self.stride = gs  # for multiscale training# Batch size# 篇幅原因,代码已省略# Dataloadersbatch_size = self.batch_size // max(world_size, 1)self.train_loader = self.get_dataloader(self.trainset, batch_size=batch_size, rank=RANK, mode="train")# 代码已省略# Optimizerself.accumulate = max(round(self.args.nbs / self.batch_size), 1)  # accumulate loss before optimizingweight_decay = self.args.weight_decay * self.batch_size * self.accumulate / self.args.nbs  # scale weight_decayiterations = math.ceil(len(self.train_loader.dataset) / max(self.batch_size, self.args.nbs)) * self.epochsself.optimizer = self.build_optimizer(model=self.model,name=self.args.optimizer,lr=self.args.lr0,momentum=self.args.momentum,decay=weight_decay,iterations=iterations,)# Scheduler# 篇幅原因,代码已省略

核心代码3:开始训练

def _do_train(self, world_size=1):"""Train completed, evaluate and plot if specified by arguments."""if world_size > 1:self._setup_ddp(world_size)self._setup_train(world_size)nb = len(self.train_loader)  # number of batchesnw = max(round(self.args.warmup_epochs * nb), 100) if self.args.warmup_epochs > 0 else -1  # warmup iterationslast_opt_step = -1self.epoch_time = Noneself.epoch_time_start = time.time()self.train_time_start = time.time()# 篇幅原因,代码已省略epoch = self.start_epochself.optimizer.zero_grad()  # zero any resumed gradients to ensure stability on train startwhile True:self.epoch = epochself.run_callbacks("on_train_epoch_start")# 篇幅原因,代码已省略self.model.train()if RANK != -1:self.train_loader.sampler.set_epoch(epoch)pbar = enumerate(self.train_loader)# Update dataloader attributes (optional)if epoch == (self.epochs - self.args.close_mosaic):self._close_dataloader_mosaic()self.train_loader.reset()# 篇幅原因,代码已省略self.tloss = Nonefor i, batch in pbar:self.run_callbacks("on_train_batch_start")# Warmup# 篇幅原因,代码已省略# Forward 正向传播with torch.cuda.amp.autocast(self.amp):batch = self.preprocess_batch(batch)self.loss, self.loss_items = self.model(batch)  # 损失计算if RANK != -1:self.loss *= world_sizeself.tloss = ((self.tloss * i + self.loss_items) / (i + 1) if self.tloss is not None else self.loss_items)# Backward 反向传播self.scaler.scale(self.loss).backward()# Optimize 优化一步if ni - last_opt_step >= self.accumulate:self.optimizer_step()last_opt_step = ni# Timed stopping# 篇幅原因,代码已省略# Logmem = f"{torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0:.3g}G"  # (GB)loss_len = self.tloss.shape[0] if len(self.tloss.shape) else 1losses = self.tloss if loss_len > 1 else torch.unsqueeze(self.tloss, 0)# 篇幅原因,代码已省略self.lr = {f"lr/pg{ir}": x["lr"] for ir, x in enumerate(self.optimizer.param_groups)}  # for loggersself.run_callbacks("on_train_epoch_end")if RANK in {-1, 0}:final_epoch = epoch + 1 >= self.epochsself.ema.update_attr(self.model, include=["yaml", "nc", "args", "names", "stride", "class_weights"])# Validation# 篇幅原因,代码已省略# Save model 保存模型if self.args.save or final_epoch:self.save_model()self.run_callbacks("on_model_save")# Scheduler# 篇幅原因,代码已省略# Early Stoppingif RANK != -1:  # if DDP trainingbroadcast_list = [self.stop if RANK == 0 else None]dist.broadcast_object_list(broadcast_list, 0)  # broadcast 'stop' to all ranksself.stop = broadcast_list[0]if self.stop:break  # must break all DDP ranksepoch += 1# 篇幅原因,代码已省略

代码解析:
在_do_train函数中,可以看到深度学习基本的步骤,即:

  1. 正向传播
  2. 损失计算
  3. 反向传播
  4. 优化一步
  5. 清空梯度
  6. 保存模型

备注:清空梯度封装在optimizer_step函数中了。

内容小结

  • YOLOv8的网络结构
    • 主要有主干网络和Head网络组成
    • 主干网络中进行P1-P5层卷积,经过SPPF后进行Upsample上采样操作后,与P3、P4、P5进行concat操作
    • 最后通过15层、18层、21层分别经过Detect模块进行目标检测。
  • YOLOv8的代码解析
    • 构建模型的核心函数是:self._smart_load()、self.task_map[self.task][key]和parse_model函数
    • self.task_map使用了字符串键与类对象值进行关联,由此达到根据关键字选择对应的模型类
    • parse_model函数中通过读取参数、设置激活函数后,使用nn.Sequential创建对应的模型
    • 训练模型的核心函数为ClassificationDataset的封装、_setup_train()函数和_do_train()函数
    • _do_train()函数中包含深度学习的基本步骤,即:正向传播→损失计算→反向传播→优化一步→清空梯度→保存模型

参考资料

B站:YoloV8Ultralytics模型结构详细讲解

YOLOv8模型yaml结构图理解(逐层分析)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/37047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SwiftUI八与UIKIT交互

代码下载 SwiftUI可以在苹果全平台上无缝兼容现有的UI框架。例如&#xff0c;可以在SwiftUI视图中嵌入UIKit视图或UIKit视图控制器&#xff0c;反过来在UIKit视图或UIKit视图控制器中也可以嵌入SwiftUI视图。 本文展示如何把landmark应用的主页混合使用UIPageViewController和…

Meta CEO 扎克伯格批评闭源AI竞争对手:称其试图“创造上帝”|TodayAI

美国社交媒体巨头Meta(Facebook母公司)的CEO马克扎克伯格&#xff08;Mark Zuckerberg&#xff09;近日在一次采访中&#xff0c;公开批评了那些他认为不够开放的AI竞争对手&#xff0c;称他们的行为就像是在“创造上帝”。扎克伯格坚定表示&#xff0c;AI技术不应该被某一家公…

SysML与MBSE的关系

SysML与MBSE的关系 对于任何基于模型的系统工程 &#xff08;MBSE&#xff09; 方法&#xff0c;推荐的最佳实践是基于模型的语言、基于模型的工具、基于模型的流程和基于模型的架构框架的协同应用&#xff0c;如下图所示 系统架构四元组 图。经过十年将SysML应用于棘手的系统…

戴尔md3400存储控制器脱机故障 电池故障处理

看了一下网上关于DELL MD系列存储故障处理的文档还是比较少的&#xff0c;最近处理了一些关于MD系列存储的问题&#xff0c;稍微整理整理就分享一下&#xff0c;各位喜欢摸索的朋友可以稍稍做些参考&#xff0c;当然如果想寻求外援的也可以快速的找到合适的人。以便安全又快捷的…

C语言基础——操作符

ʕ • ᴥ • ʔ づ♡ど &#x1f389; 欢迎点赞支持&#x1f389; 个人主页&#xff1a;励志不掉头发的内向程序员&#xff1b; 专栏主页&#xff1a;C语言基础&#xff1b; 文章目录 前言 一、操作符的分类 二、二进制和进制转换 2.1 二进制转十进制 2.1.1 十…

零知识证明基础:数字签名

1、绪论 数字签名(Digital Signature)&#xff0c;也称电子签名&#xff0c;是指附加在某一电子文档中的一组特定的符号或代码。它利用密码技术对该电子文档进行关信息提取并进行认证形成&#xff0c;用于标识签发者的身份以及签发者对电子文档的认可&#xff0c;并能被接收者…

pyqt5 制作视频剪辑软件,切割视频

该软件用于切割视频,手动选取视频片段的起始帧和结束帧并保存为json文件。gui界面如下:包含快进、快退、暂停等功能, 代码如下: # coding=UTF-8 """ theme: pyqt5实现动作起始帧和结束帧的定位,将定位到的帧数保存json文件 time: 2024-6-27 author: cong…

详细介绍LP-SCADA系统的核心数据采集单元

关键字:LP-SCADA系统, 传感器可视化, 设备可视化, 独立SPC系统, 智能仪表系统,SPC可视化,独立SPC系统 SCADA系统的数据采集功能是其核心组成部分&#xff0c;它允许系统从各种传感器、仪器和设备中收集实时数据。以下是SCADA系统数据采集功能的详细描述&#xff1a; 传感器和…

Kotlin vs Java:深入解析两者之间的最新差异与优劣(全面指南)

文章目录 1. 概述2. 语法简洁性3. 空安全4. 扩展函数5. 协程6. 数据类7. 智能类型转换8. 默认参数与命名参数9. 无 checked exceptions10. 单例模式总结 &#x1f389;欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨…

Java对象类辨识指南:Object与Objects类的区别详解

今天在写lambda表达式时&#xff0c;用filter来做过滤判断我的结果是否为null时使用到了Objects.nonNull&#xff0c;但是敲着敲着发现不对劲&#xff0c;怎么没有nonNull方法?? 其实时我少敲了一个s&#xff0c;当时自己并没有很清楚Object和Objects两者之前的区别&#xf…

LangGPT:高质量提示词框架

题目&#xff1a;LangGPT: Rethinking Structured Reusable Prompt Design Framework for LLMs from the Programming Language作者: Ming Wang; Yuanzhong Liu; Xiaoming Zhang; Songlian Li; Yijie Huang; Chi Zhang; Daling Wang; Shi Feng; Jigang LiDOI: 10.48550/arXiv.2…

Qt自定义信号

1.Teacher类下定义信号signals: Student类下定义槽函数&#xff1a; Teacher.h #pragma once#include <QObject>class Teacher : public QObject {Q_OBJECTpublic:Teacher(QObject *parent);~Teacher(); signals:void Ask(); //老师向学生提问void Ask(QString str);…

量化投资 日周月报 2024-06-28

文章 深度学习在量化交易中的应用:在BigQuant量化交易平台的文章中,探讨了深度学习在量化交易中,特别是在因子挖掘方面的应用。文章提到,随着传统线性模型的潜力逐渐枯竭,非线性模型逐渐成为量化交易的主要探索方向。深度学习因其对非线性关系的拟合能力,在量化交易中展现…

qmt量化交易策略小白学习笔记第52期【qmt编程之商品期货数据】

qmt编程之获取商品期货数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 主力合约生成规则 每个品种只有一个主连合约。主连合约于下一个交易日进行指向切换&#xff0c;切换前主连合约不变…

Unity Animator 运行时修改某个动画状态的播放速度

1.添加动画参数&#xff0c;选择需要动态修改速度的动画状态 2.在属性面板种设置速度倍速参数

HarmonyOS ArkUi ArkWeb加载不出网页问题踩坑

使用 使用还是比较简单的&#xff0c;直接贴代码了 别忘了配置网络权限 Entry Component struct WebPage {State isAttachController: boolean falseState url: string State title: string Prop controller: web_webview.WebviewController new web_webview.WebviewCont…

自定义注解+AOP形式监控接口调用日志

目的&#xff1a; 通过自定义注解&#xff0c;在需要监控接口调用输出日志的类或方法上&#xff0c;加上自定义注解&#xff0c;实现无侵入式接口监控。 实现&#xff1a; idea结构 1、导入pom <dependency><groupId>org.aspectj</groupId><artifactI…

ASUS/华硕天选Air 2021 FX516P系列 原厂win10系统

安装后恢复到您开箱的体验界面&#xff0c;带原机所有驱动和软件&#xff0c;包括myasus mcafee office 奥创等。 最适合您电脑的系统&#xff0c;经厂家手调试最佳状态&#xff0c;性能与功耗直接拉满&#xff0c;体验最原汁原味的系统。 原厂系统下载网址&#xff1a;http:…

FTP 文件传输协议:概念、工作原理;上传下载操作步骤

目录 FTP 概念 工作原理 匿名用户 授权用户 FTP软件包 匿名用户上传下载实验步骤 环境配置 下载 上传 wget 授权用户上传下载步骤 root用户登录FTP步骤 监听 设置端口号范围 修改用户家目录 匿名用户 授权用户 FTP 概念 FTP&#xff08;File Transfer Prot…

JAVA设计模式-大集合数据拆分

背景 我们在做软件开发时&#xff0c;经常会遇到把大集合的数据&#xff0c;拆分成子集合处理。例如批量数据插入数据库时&#xff0c;一次大约插入5000条数据比较合理&#xff0c;但是有时候待插入的数据远远大于5000条。这时候就需要进行数据拆分。数据拆分基本逻辑并不复杂&…