Yolov8代码详解,入门代码讲解

news/2024/10/15 21:55:28/

看不懂的代码可以复制进讯飞星火问问AI。以下是逐语句调试得出的执行顺序。

首先在根目录新建一个py文件,能够训练数据。

python">from ultralytics import YOLO
from ultralytics.utils import DEFAULT_CFG
from datetime import datetimecontroller=1def traindata():current_time = datetime.now()time_str = current_time.strftime("%Y-%m-%d_%H-%M-%S")  # 个人习惯, 用训练时间命名保存路径, 或者你自己自定义info=r'_only_yolov8'DEFAULT_CFG.save_dir = f"./Vincent/{time_str+info}"# 加载模型model = YOLO(r"E:\DeepLearning\ultralytics\ultralytics\cfg\models\v8\yolov8.yaml")  # 从头开始构建新模型# model = YOLO("yolov8n.pt")  # 加载预训练模型(推荐用于训练)# Use the modelresults = model.train(data=r"E:\DeepLearning\ultralytics\ultralytics\data\DVOR2024830_yolov8\data.yaml",epochs=200,batch=30,lr0=0.1)  # 训练模型metrics = model.val()  # 在验证集上评估模型性能# results = model("https://ultralytics.com/images/bus.jpg")  # 预测图像# success = model.export(format="onnx")  # 将模型导出为 ONNX 格式1def predict():model = YOLO(r"E:\DeepLearning\ultralytics\runs\detect\train86\weights\best.pt")  # pretrained YOLOv8n modelmetrics = model.val(data=r"E:\DeepLearning\ultralytics\ultralytics\data\DVOR2024830_yolov8\data.yaml")vpath=r"E:\DeepLearning\ultralytics\ultralytics\data\ceshi/"# results = model([vpath+"vor (1).jpg",vpath+"vor (2).jpg",vpath+"vor (3).jpg",vpath+"VORBlack (1).jpg",vpath+"VORBlack (2).jpg",vpath+"VORBlack (3).jpg"], save=True,conf=0.1)  # return a list of Results objects# 视频路径file_path = r"E:\DeepLearning\ultralytics\ultralytics\data\video\vor3.mp4"file_path2 = r"E:\DeepLearning\ultralytics\ultralytics\data\video\vor.mp4"file_path3 = r"E:\DeepLearning\ultralytics\ultralytics\data\video\vor2.mp4"# 检测视频# results = model.predict(source=file_path3, device=0, show=False, save=True, conf=0.1)# # Process results list# for result in results:#     boxes = result.boxes  # Boxes object for bounding box outputs#     masks = result.masks  # Masks object for segmentation masks outputs#     keypoints = result.keypoints  # Keypoints object for pose outputs#     probs = result.probs  # Probs object for classification outputs#     obb = result.obb  # Oriented boxes object for OBB outputs#     result.show()  # display to screen#     result.save(filename="result.jpg")  # save to diskdef exportVincent():# Load the YOLOv8 modelmodel = YOLO(r"E:\DeepLearning\ultralytics\runs\detect\train21\weights\best.pt")# Export the model to ONNX formatmodel.export(format="onnx",dynamic=True)if __name__ == '__main__':if(controller==1):traindata()elif controller==2:predict()else:print("null")## exportVincent()

创建Yolov8模型结构

  • 根据vincentTrain.py中。代码model = YOLO("yolov8n.yaml")初始化时,调用ultralytics/models/yolo/model.py中的YOLO类初始化函数。

  • ultralytics/models/yolo/model.py 中def __init__(self, model="yolov8n.pt", task=None, verbose=False):由于传入参数为"yolov8n.yaml",执行super().__init__(model=model, task=task, verbose=verbose)
    super().init()函数调用基类ultralytics/engine/model.py中Model类的构造函数,这里的self参数传入的是子类也就是YOLO类。

  • ultralytics/engine/model.py中。在基类Model的构造函数中,识别到yaml文件,需要新创建模型,则执行代码self._new(model, task=task, verbose=verbose)。该代码会调用Model类中的_new()函数。

  • ultralytics/engine/model.py中。在_new函数中通过cfg_dict = yaml_model_load(cfg)读取模型结构yaml文件。

  • ultralytics/engine/model.py。读取结构配置后,通过self.task = task or guess_model_task(cfg_dict),确定任务类型。guess_model_task()函数用于猜测任务类型检测,分割,分类等。

  • ultralytics/engine/model.py。self.model = (model or self._smart_load("model"))(cfg_dict, verbose=verbose and RANK == -1)self._smart_load()只读属性表示调用YOLO类中的_smart_load创建只读属性,该属性变量在基类中并未实现。self._smart_load(“model”)获取到需要实例化的哪个类,然后传入参数(cfg_dict, verbose=verbose and RANK == -1)。

  • ultralytics/models/yolo/model.py。"detect": { "model": DetectionModel, "trainer": yolo.detect.DetectionTrainer, "validator": yolo.detect.DetectionValidator, "predictor": yolo.detect.DetectionPredictor, },根据代码self.model返回DetectionModel类,该类定义在ultralytics/nn/tasks.py中。

  • ultralytics/nn/tasks.py。调用DetectionModel的构造函数def init(self, cfg=“yolov8n.yaml”, ch=3, nc=None, verbose=True)

  • ultralytics/nn/tasks.py。self.model, self.save = parse_model(deepcopy(self.yaml), ch=ch, verbose=verbose)parse_model()函数对模型进行生成。deepcopy函数用于深度拷贝yaml模型配置信息。ch表示通道数。是后来加上去的参数。

开始遍历yaml中的backbone+head内容

  • ultralytics/nn/tasks.py。在parse_model(d, ch, verbose=True)函数中,for i, (f, n, m, args) in enumerate(d[“backbone”] + d[“head”])对yaml内容解析。enumerate函数使得d[“backbone”] + d[“head”]将backbone与head拼接到一个list中。i表示list的索引位置,(f, n, m, args)表示yaml中的参数。
  • ultralytics/cfg/models/v8/yolov8.yaml中。在yaml文件中标注如下
python">backbone:# [from, repeats, module, args]- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2

f表示from -1,n表示repeats 1,m表示模块module Conv,args代表参数[64, 3, 2]

  • ultralytics/nn/tasks.py的parse_model()。m = getattr(torch.nn, m[3:]) if "nn." in m else globals()[m]。getattr函数用于获取它某个属性的值。该语句判断m中是否包含nn.字段,1)如果m为nn.Upsample,则m=torch.nn中Upsample对应的值。2)如果m为Conv,则globals()找到当前位置的全局变量(返回的可以是类型,也可以是具体的变量,或者函数,此处返回类型),返回名为Conv对应的值。class Conv(nn.Module)定义在ultralytics/nn/modules/conv.py
python"># ultralytics/nn/modules/conv.py
class Conv(nn.Module):"""Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""default_act = nn.SiLU()  # default activationdef __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):"""Initialize Conv layer with given arguments including activation."""super().__init__()self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)self.bn = nn.BatchNorm2d(c2)self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()def forward(self, x):"""Apply convolution, batch normalization and activation to input tensor."""return self.act(self.bn(self.conv(x)))
  • ultralytics/nn/tasks.py的parse_model()。c2 = make_divisible(min(c2, max_channels) * width, 8)。make_divisible函数将yaml模型结构中arg参数中第一个表示通道数的数据处理,确保是8的倍数。若不是,则c2设置为比参数1大的8倍数。
  • ultralytics/nn/tasks.py的parse_model()。m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)m存放了当前需要创建的模块类型,当前是Conv,m(*args)相当于Conv(*args),创建Conv类实例,调用ultralytics/nn/modules/conv.py中的构造函数,配置该类。并放入nn.Sequential类型的m_中。
  • ultralytics/nn/tasks.py的parse_model()。m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)。m存放了当前需要创建的模块类型,当前是Conv,m(*args)相当于Conv(*args),创建Conv类实例,调用ultralytics/nn/modules/conv.py中的构造函数,配置该类。并放入nn.Sequential类型的m_中。
  • ultralytics/nn/tasks.py的parse_model()。m.np = sum(x.numel() for x in m_.parameters())。m_表示整个神经网络模型,x作为模型中所有参数的迭代项,使用numel()函数计算每个参数的元素数量,最后求和得总数。

第三次循环出现m出现C2f模块

  • ultralytics/nn/tasks.py的parse_model()。
python">if m in {BottleneckCSP, C1, C2, C2f, C2fAttn, C3, C3TR, C3Ghost, C3x, RepC3}:args.insert(2, n)  # number of repeatsn = 1
m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)

识别到模型为C2f后,在arg中插入参数n。代用m(*args)创建C2f模块。

  • ultralytics/nn/modules/block.py。class C2f(nn.Module),创建C2f模块,使用构造函数def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5)
python"># ultralytics/nn/modules/block.py
class C2f(nn.Module):"""Faster Implementation of CSP Bottleneck with 2 convolutions."""def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):"""Initialize CSP bottleneck layer with two convolutions with arguments ch_in, ch_out, number, shortcut, groups,expansion."""super().__init__()self.c = int(c2 * e)  # hidden channelsself.cv1 = Conv(c1, 2 * self.c, 1, 1)self.cv2 = Conv((2 + n) * self.c, c2, 1)  # optional act=FReLU(c2)self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))def forward(self, x):"""Forward pass through C2f layer."""y = list(self.cv1(x).chunk(2, 1))y.extend(m(y[-1]) for m in self.m)return self.cv2(torch.cat(y, 1))
  • ultralytics/nn/modules/block.py。self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))。构造函数中实例化Bottleneck类,该类同样定义在ultralytics/nn/modules/block.py中。
python"># ultralytics/nn/modules/block.py
class Bottleneck(nn.Module):"""Standard bottleneck."""def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):"""Initializes a bottleneck module with given input/output channels, shortcut option, group, kernels, andexpansion."""super().__init__()c_ = int(c2 * e)  # hidden channelsself.cv1 = Conv(c1, c_, k[0], 1)self.cv2 = Conv(c_, c2, k[1], 1, g=g)self.add = shortcut and c1 == c2def forward(self, x):"""'forward()' applies the YOLO FPN to input data."""return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
  • ultralytics/nn/modules/block.py。在Bottleneck中关注构造函数__init__(),以及forward()函数。

  • 模型创建迭代到SPPF模块时,ultralytics/nn/tasks.py的parse_model()。ultralytics/nn/modules/block.py中的class SPPF(nn.Module)。定义了该类的结构。关注构造函数__init__(),以及forward()函数即可。

  • 接下来创建模型中head内容。nn.Upsample创建方式与前面相似。接下来详细说Concat。

  • ultralytics/cfg/models/v8/yolov8.yaml。在模型结构配置文件head中 - [[-1, 6], 1, Concat, [1]] 。其中[-1, 6]的-1表示前面一层,6表示第六层,在ultralytics/nn/tasks.py的parse_model()中layers变量按顺序存放了之前创建的每个层级模块。在yolov8的模型结构图中已经标识了层级的编号。这里的concat使用了6层的C2f输出,也使用了-1层也就是第10层的Upsample输出。

  • 创建concat时,concat类定义在ultralytics/nn/modules/conv.py中。在concat类中,无论是构造函数,还是前向传播函数,均没有涉及到使用参数[-1,6],只是返回了一个torch.cat(x, self.d)的拼接操作。

detect检测层
  • ultralytics/nn/tasks.py的parse_model()。最后,创建detect检测层时候。args[j] = locals()[a] if a in locals() else ast.literal_eval(a)。a表示-[[15,18,21],1,Detect,[nc]]中的nc,为了映射为前面的nc值,使用了locals()查询当前局部变量有哪些,若存在有名字为nc的变量,则将变量的值赋值给args[j]。所谓检测的种类数nc被放入了args中。
python">#ultralytics/nn/tasks.py的parse_model()elif m in {Detect, WorldDetect, Segment, Pose, OBB, ImagePoolingAttn}:args.append([ch[x] for x in f])if m is Segment:args[2] = make_divisible(min(args[2], max_channels) * width, 8)
  • f表示yaml中的from参数,这里是[15,18,21],分别指第15层,18层,21层的输出。将这些输出的通道数量存放入args中。

  • ultralytics/nn/tasks.py的parse_model()。使用m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)。其中m(*args)语句创建detect模块。调用Detect类。

  • Detect类构造函数如下

python">#ultralytics/nn/modules/head.py的class Detect(nn.Module)
#class Detect(nn.Module):def __init__(self, nc=80, ch=()):"""Initializes the YOLOv8 detection layer with specified number of classes and channels."""super().__init__()self.nc = nc  # number of classesself.nl = len(ch)  # number of detection layersself.reg_max = 16  # DFL channels (ch[0] // 16 to scale 4/8/12/16/20 for n/s/m/l/x)self.no = nc + self.reg_max * 4  # number of outputs per anchorself.stride = torch.zeros(self.nl)  # strides computed during buildc2, c3 = max((16, ch[0] // 4, self.reg_max * 4)), max(ch[0], min(self.nc, 100))  # channelsself.cv2 = nn.ModuleList(nn.Sequential(Conv(x, c2, 3), Conv(c2, c2, 3), nn.Conv2d(c2, 4 * self.reg_max, 1)) for x in ch)self.cv3 = nn.ModuleList(nn.Sequential(Conv(x, c3, 3), Conv(c3, c3, 3), nn.Conv2d(c3, self.nc, 1)) for x in ch)self.dfl = DFL(self.reg_max) if self.reg_max > 1 else nn.Identity()

DFL,全称Distribution Focal Loss

未完待续。。。。。


http://www.ppmy.cn/news/1539631.html

相关文章

成绩分析报告中的统计指标算法

统计指标 成绩分析报告中涉及到很多统计指标,包括满分、平均分、最高分、最低分、得分率、难度、区分度、标准差、标准分、信度等,下面将概念较复杂的指标进行简单说明。 得分率 考生在某一题或整卷的得分情况,计算公式:得分率…

【前端】JQ验证每个单选按钮是否已经选择

验证每个单选题是否都已经选择&#xff0c;其中每个input中不带name值&#xff0c;直接遍历input[type"radio"]验证 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewpor…

Java面经--JVM篇

前言&#xff1a;资料由本人从网上寻找加上本人的个人理解进行编写总结&#xff0c;为的就是帮助自己快速掌握知识点&#xff0c;如有疑问或错误的点&#xff0c;欢迎评论区留言或者私信。 1.什么是JVM&#xff1f; JVM即Java虚拟机&#xff08;Java Virtual Machine&#xff0…

Flash Attention:高效注意力机制的突破

近年来&#xff0c;注意力机制(Attention)已成为自然语言处理和深度学习领域的重要工具。然而&#xff0c;传统的注意力实现在处理长序列时存在计算和内存效率低下的问题。为了解决这一挑战&#xff0c;研究者们提出了Flash Attention&#xff0c;一种快速、内存高效的注意力算…

使用 `fork()` 和 `waitpid()` 进行进程管理的详解

使用 fork() 和 waitpid() 进行进程管理的详解 在 C/C 编程中&#xff0c;fork() 和 waitpid() 是处理进程创建和管理的关键函数。本文将深入探讨 fork() 的用法、参数解析、wait() 和 waitpid() 的区别&#xff0c;以及如何正确获取子进程的退出状态。 1. fork() 函数概述 …

单链表算法题(一)(超详细版)

前言 : 通过算法题 &#xff0c; 学习解决问题的思路 &#xff0c; 再面对类似的算法题时 &#xff0c; 能快速定位解决方案 一 . 移除链表元素 移除链表元素 : . - 力扣&#xff08;LeetCode&#xff09; 思路一 &#xff1a; 通过遍历链表找到值为val 的结点 &#xff0c; …

应急响应:DHCP$DNS劫持实战

目录 DHCP DHCP安全性&#xff1a; DHCP常见的攻击手段&#xff1a; DNS DNS常见的攻击方式&#xff1a; DNS&DHCP攻击实战演练&#xff1a; 环境配置&#xff1a; 利用&#xff1a; 排查&#xff1a; 防御&#xff1a; DHCP 介绍&#xff1a; DHCP&#xff08;…

【代码随想录Day43】动态规划Part11

1143.最长公共子序列 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;动态规划子序列问题经典题目 | LeetCode&#xff1a;1143.最长公共子序列_哔哩哔哩_bilibili class Solution {public int longestCommonSubsequence(String text1, String text2) {// 将输…