当前位置: 首页 > news >正文

YOLOv8 从yaml配置文件生成PyTorch模型

YOLOv8 从 yaml 配置文件生成 PyTorch 模型

说明:

  • 首次发表日期:2024-01-21
  • 参考资料:
    • https://github.com/ultralytics/ultralytics/tree/fcc4496b127bdafc5b137a686392b07d3461e04f
    • https://github.com/ultralytics/ultralytics/blob/fcc4496b127bdafc5b137a686392b07d3461e04f/ultralytics/cfg/models/v8/yolov8-pose.yaml

Overview

以下是 yolov8-pose.yaml 文件:

# Parameters
nc: 1 # number of classes
kpt_shape: [17, 3] # number of keypoints, number of dims (2 for x,y or 3 for x,y,visible)
scales: # model compound scaling constants, i.e. 'model=yolov8n-pose.yaml' will call yolov8-pose.yaml with scale 'n'# [depth, width, max_channels]n: [0.33, 0.25, 1024]s: [0.33, 0.50, 1024]m: [0.67, 0.75, 768]l: [1.00, 1.00, 512]x: [1.00, 1.25, 512]# YOLOv8.0n backbone
backbone:# [from, repeats, module, args]- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4- [-1, 3, C2f, [128, True]]- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8- [-1, 6, C2f, [256, True]]- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16- [-1, 6, C2f, [512, True]]- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32- [-1, 3, C2f, [1024, True]]- [-1, 1, SPPF, [1024, 5]] # 9# YOLOv8.0n head
head:- [-1, 1, nn.Upsample, [None, 2, "nearest"]]- [[-1, 6], 1, Concat, [1]] # cat backbone P4- [-1, 3, C2f, [512]] # 12- [-1, 1, nn.Upsample, [None, 2, "nearest"]]- [[-1, 4], 1, Concat, [1]] # cat backbone P3- [-1, 3, C2f, [256]] # 15 (P3/8-small)- [-1, 1, Conv, [256, 3, 2]]- [[-1, 12], 1, Concat, [1]] # cat head P4- [-1, 3, C2f, [512]] # 18 (P4/16-medium)- [-1, 1, Conv, [512, 3, 2]]- [[-1, 9], 1, Concat, [1]] # cat head P5- [-1, 3, C2f, [1024]] # 21 (P5/32-large)- [[15, 18, 21], 1, Pose, [nc, kpt_shape]] # Pose(P3, P4, P5)

YOLOv8 根据 YAML 配置文件来生成 PyTorch 模型,具体由 parse_model 函数实现。

具体来说,PoseModel 的父类 DetectionModel 的初始化方法包含以下代码:

self.model, self.save = parse_model(deepcopy(self.yaml), ch=ch, verbose=verbose)

与常见的 PyTorch 模型不同,parse_model 返回的模型并没有提供类似如下的 forward 部分:

def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits

了解其 forward 部分的逻辑,我们需要查看 BaseModel 的 _predict_once 方法。

nc, kpt_shape, scales

以下为涉及 nc, kpt_shapescales 的部分:

# Parameters
nc: 1 # number of classes
kpt_shape: [17, 3] # number of keypoints, number of dims (2 for x,y or 3 for x,y,visible)
scales: # model compound scaling constants, i.e. 'model=yolov8n-pose.yaml' will call yolov8-pose.yaml with scale 'n'# [depth, width, max_channels]n: [0.33, 0.25, 1024]s: [0.33, 0.50, 1024]m: [0.67, 0.75, 768]l: [1.00, 1.00, 512]x: [1.00, 1.25, 512]

其中:

nc 表示有多少个分类,默认是 1 个,一般为 person。

kpt_shape 表示有多少个关键点(keypoints)和关键点的维度数。

  • 默认为 17 个关键点
  • 默认关键点的维度数为 3。具体说明:2 表示只有横坐标和纵坐标(x,y),3 表示另外还有一个值表示是否可见(visible)。

scales 列出了可选的模型大小。

具体而言,yaml_model_load 函数读取 yaml 配置文件的时候,会调用 guess_model_scale 来获取模型的 scale:

d["scale"] = guess_model_scale(path)

即从模型 yaml 文件名路径上获取 scale,然后从 scales 中选取对应的参数。其中 scales 默认有 n,s,m,l,x 这几个选项。

需要注意的是模型 YAML 配置文件的名称符合 yolov\d+([nslmx]) 的正则表达式。

backbone 和 head

backbonehead 的 YAML 配置采用同样的风格,都是长度为 4 的列表,分别为 from, repeats, module 和 args,其中

from 代表输入数据来源(即 forward 方法 x 参数的实参)。其中大多数为 −1-11,表示来自上一层。

repeats 表示模块的重复次数。

module 表示具体的模块

args 表示用于初始化 module 的参数,其中第一个参数代表输出通道。

其中重复次数和输出通道并不是完全固定的,会受到模型复杂度的影响(备注:yolov8n 比 yolov8s 的模型复杂度要小)

parse_model 函数中的 n, s, m, l, x 这些不同 scale 的影响

不同的 scale 决定了 3 个不同的参数:

depth, width, max_channels = scales[scale]

depth 可能会更新重复次数

首先,摘取 parse_model 函数中和 depth 相关的代码:

for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]):n = n_ = max(round(n * depth), 1) if n > 1 else n  # depth gain...m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args)  # module

可以发现,当配置的 repeats 参数大于 1 时,实际的重复次数会受到 depth 参数的影响

其中 yolov8n 和 yolov8s 的 depth 为 0.33,以下为其原重复次数及对应更新后的重复次数

[{n: max(round(n * 0.33), 1) if n > 1 else n} for n in range(1, 13)]
# [{1: 1}, {2: 1}, {3: 1}, {4: 1}, {5: 2}, {6: 2}, {7: 2}, {8: 3}, {9: 3}, {10: 3}, {11: 4}, {12: 4}]

其中 yolov8m 的 depth 为 0.67,影响如下:

[{n: max(round(n * 0.67), 1) if n > 1 else n} for n in range(1, 13)]
# [{1: 1}, {2: 1}, {3: 2}, {4: 3}, {5: 3}, {6: 4}, {7: 5}, {8: 5}, {9: 6}, {10: 7}, {11: 7}, {12: 8}]

而 yolov8l 和 yolovx 的 depth 为 1.0,不受影响,完全和 yaml 配置文件保持一致

widthchannels 影响模块的输出通道数(output channels)

提取 parse_model 关于大部分模块参数调整的代码:

for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]):  # from, number, module, args...if m in (Classify,Conv,ConvTranspose,...):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)args = [c1, c2, *args[1:]]if m in (BottleneckCSP, C1, C2, C2f, 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)  # module...ch.append(c2)

可以看出,对于大部分模块而言,输出通道数 c2 参数受到 widthmax_channels 的影响。

以下列出不同复杂度输出通道变化前和变化后的对照:

yolov8n

[{c2: make_divisible(min(c2, 1024) * 0.25, 8)} for c2 in [64, 128, 256, 512, 1024]]
# [{64: 16}, {128: 32}, {256: 64}, {512: 128}, {1024: 256}]

yolov8s

[{c2: make_divisible(min(c2, 1024) * 0.50, 8)} for c2 in [64, 128, 256, 512, 1024]]
# [{64: 32}, {128: 64}, {256: 128}, {512: 256}, {1024: 512}]

yolov8m

[{c2: make_divisible(min(c2, 768) * 0.75, 8)} for c2 in [64, 128, 256, 512, 1024]]
# [{64: 48}, {128: 96}, {256: 192}, {512: 384}, {1024: 576}]

yolov8l

[{c2: make_divisible(min(c2, 512) * 1.0, 8)} for c2 in [64, 128, 256, 512, 1024]]
# [{64: 64}, {128: 128}, {256: 256}, {512: 512}, {1024: 512}]

yolov8x

[{c2: make_divisible(min(c2, 512) * 1.25, 8)} for c2 in [64, 128, 256, 512, 1024]]
# [{64: 80}, {128: 160}, {256: 320}, {512: 640}, {1024: 640}]

最后,注意 ch.append(c2) 表示 YAML 每个模块的输出通道都有保存。

forward pass

BaseModel 的 _predict_once 方法说明了不同模块之间是如何连接的。

提取其中部分代码:

def _predict_once(self, x, profile=False, visualize=False, embed=None):y, dt, embeddings = [], [], []  # outputsfor m in self.model:if m.f != -1:  # if not from previous layerx = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f]  # from earlier layers...x = m(x)  # runy.append(x if m.i in self.save else None)  # save output...return x

可以发现,每一层的输出被保存在列表 y

  • 如果 from(输入数据索引)为整数,则直接取 y[m.f]
  • 如果非整数,则用循环取得多个输入数据

Summary

本文以 yolov8-pose.yaml 文件为例,说明了从配置文件到 PyTorch 模型生成的关键过程。


文章转载自:

http://r04Emx5Z.mxcgf.cn
http://wcGxDQq6.mxcgf.cn
http://jAPa6CeI.mxcgf.cn
http://ZQ4DrHgb.mxcgf.cn
http://XKxK1uJt.mxcgf.cn
http://ybuRqpvb.mxcgf.cn
http://Lovuhvlo.mxcgf.cn
http://7lEb9j6s.mxcgf.cn
http://sIyTvQhD.mxcgf.cn
http://UnAvhycl.mxcgf.cn
http://xrBZsJ1q.mxcgf.cn
http://ats1Lmz4.mxcgf.cn
http://6Qs3AfOL.mxcgf.cn
http://65uUzRtL.mxcgf.cn
http://CT6IsOY5.mxcgf.cn
http://9TvAkocp.mxcgf.cn
http://i8e2mFjs.mxcgf.cn
http://ZmgPZuHX.mxcgf.cn
http://4eT6zze3.mxcgf.cn
http://QcdJNXMy.mxcgf.cn
http://oDXqhTtk.mxcgf.cn
http://x55uCBih.mxcgf.cn
http://haHvxwH7.mxcgf.cn
http://hUhjcv9n.mxcgf.cn
http://MJy1Px3A.mxcgf.cn
http://8lKdl03w.mxcgf.cn
http://HBXrBpPu.mxcgf.cn
http://J6Em0TXW.mxcgf.cn
http://ORB8j9aT.mxcgf.cn
http://0sxcAH2y.mxcgf.cn
http://www.dtcms.com/a/379641.html

相关文章:

  • 重复文件清理的标准化操作流程
  • Amazon DocumentDB Serverless 技术深度解析:架构特性、弹性扩缩容机制与实操指南
  • 项目管理方法适合什么类型的企业
  • HTTPS(Hypertext Transfer Protocol Secure,超文本传输安全协议)
  • 【LLM越狱】AI大模型DRA攻击解读与复现
  • k8s下的发布策略详解
  • 第 9 篇:深入浅出学 Java 语言(JDK8 版)—— 吃透泛型机制,筑牢 Java 类型安全防线
  • 机器人防爆与隔爆的本质,两者的区别对比
  • 从蛮力清扫到 “会看路”:室外清洁机器人的文明进阶
  • 大数据毕业设计选题推荐-基于大数据的家庭能源消耗数据分析与可视化系统-Hadoop-Spark-数据可视化-BigData
  • 【Settings】恢复出厂设置密码校验
  • 机器人控制器开发(通讯——ros话题转为websocket)
  • Go 1.25.1 自定义包调用
  • go语言,彩色验证码生成,加减法验证,
  • 深入解析 AST2600 H2B 接口:架构、原理与完整开发指南
  • 手机ip隔离方法
  • RAG检索增强生成:让AI拥有“外部记忆“的黑科技
  • Jmter接口网站压力测试工具使用记录
  • Agentic BI技术解构:多智能体协作框架如何实现“分析-决策-执行”闭环?
  • 如何用AI做海报、IP设计,稿定AI一站式创作
  • Threejs案例实践笔记
  • React18学习笔记(一) 如何创建一个React项目,JSX的基础应用,案例---视频网站评论区
  • 【Threejs】学习笔记
  • 图像显示技术与色彩转换:从基础原理到实际应用
  • C 语言实现 I.MX6ULL 点灯(续上一篇)、SDK、deep及bsp工程管理
  • 飞桨paddlepaddle旧版本2.4.2安装
  • 2.5 DNS(Domain Name System)
  • CK: 03靶场渗透
  • User类CRUD实现
  • AFSim2.9.0学习笔记 —— 4.2、ArkSIM文件结构介绍及项目结构整理