YOLOv26详解 | 一文带你从代码角度深入理解ultralytics最新作品yolov26的创新 | 训练、推理、验证、导出 (附网络结构图PPT可用,全网最全)
一、本文介绍
ultralytics发布了最新的作品YOLOv26,这一次YOLOv26的变化相对于ultralytics公司的上一代作品YOLOv11变化主要提现在后处理上,其网络结构并没有很大的变化,其中改变的位置涉及到SPPF和C3k2,在损失函数方面采用的GIoU作为边界框回归损失,下面带大家深入理解一下ultralytics最新作品YOLOv26的创新点。
本文将系统地介绍YOLOv26的使用教程(全网最详细的YOLOv26介绍),包括网络结构的分析、训练环境的配置、数据集的获取、模型训练和推理过程、结果的评估与验证、模型的导出以及实际部署,文中还配有手绘的网络结构图(如果做PPT可联系作者获取无水印图片),帮助大家更直观地理解YOLOv26。
YOLOv26网络结构图(个人手绘)
本专栏后面会推出YOLOv26改进,包括:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等各种改进机制,以及本人对于YOLOv26架构的一些优化方式,辅助大家发表论文,手把手教大家如何用YOLOv26系列发表论文。
专栏地址:YOLOv26有效涨点专栏包含:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制
目录
二、YOLOv26核心创新点解析
YOLOv26创新可以总结为下面6点(后面每一点会仔细介绍):
1、双检测头机制(一对一和一对多检测头)
2、端到端无 NMS 推理
3、移除 DFL
4、ProgLoss + STAL 小目标优化
5、MuSGD 优化器
6、YOLOv26网络结构变化
2.1 双检测头机制(一对一和一对多检测头)
在 YOLOv26 中,我认为 双检测头机制 是实现端到端无 NMS 推理的关键设计(所以我把他放在YOLOv26端到端无 NMS 推理的前面介绍)。它不是简单地多加了一个检测头(训练阶段:One-to-Many + One-to-One 推理阶段:主要使用 One-to-One),而是把 训练阶段 和 推理阶段 的目标进行了区分:训练时需要更充分的监督,推理时需要更干净、更直接的输出。
One-to-Many和One-to-One检测头介绍:
One-to-Many Head 可以理解为传统 YOLO 检测头的延续。它的特点是:一个真实目标可以匹配多个预测位置,也就是一个 GT 对应多个正样本。这种方式的好处是训练信号更充分,尤其是在训练早期,模型还没有学会准确定位目标时,多分配一些正样本可以帮助网络更稳定地学习目标特征(就是说一个预测目标会生成多个候选框)。
我理解它的作用是:
练阶段提供更多正样本监督(类似一个目标生成多个候选框)
帮助 Backbone 和 Neck 更充分地学习目标特征
提升模型训练稳定性和召回能力但是它的问题也很明显:由于一个目标可能对应多个预测框,所以推理阶段容易产生重复框,需要 NMS 进行后处理。
One-to-One Head 是 YOLOv26 中更核心的推理分支。它的目标是让一个真实目标尽量只对应一个主要预测结果,从而减少重复框的产生。
作者:YOLOv26 默认使用 One-to-One Head 进行推理,该分支不依赖传统 NMS(下一节介绍这个),可以直接输出更加接近最终检测结果的预测框。
我认为 One-to-One Head 的意义在于,它让 YOLOv26 从传统的:
大量候选框 + NMS筛选转变为:
更少冗余预测 + Top-K分数筛选
在训练阶段,One-to-Many 分支可以让模型在早期获得更多正样本监督,避免训练不稳定;One-to-One 分支则让模型逐渐学习端到端输出方式,为后续无 NMS 推理做准备。
在推理阶段,YOLOv26 默认使用 One-to-One Head 输出结果,而不是使用 One-to-Many Head。这样做的原因很简单:One-to-Many Head 会产生更多候选框,虽然精度可能略高,但仍然需要 NMS;而 One-to-One Head 输出更简洁,不需要传统 NMS,因此更适合端到端部署。
2.2 YOLOv26端到端无 NMS 推理
在我看来,YOLOv26 最核心的变化之一,就是将传统 YOLO 中依赖 NMS 后处理 的检测流程,进一步改造成了更加直接的 End-to-End NMS-Free Inference。传统 YOLO 模型在推理阶段通常会生成大量候选框,这些候选框之间往往存在高度重叠,因此还需要通过 NMS 根据置信度和 IoU 阈值进行筛选,最终保留分数较高且重复度较低的检测框。这个过程虽然有效,但它本质上属于模型外部的后处理步骤,会额外引入推理延迟,也会让部署过程依赖手工设置的阈值参数。
Ultralytics作者:YOLOv26 的设计思路是让模型尽可能直接输出最终检测结果,而不是先生成大量重复框,再依赖 NMS 进行筛选。官方资料中也明确提到,YOLO26 是原生端到端模型,可以直接生成预测结果,不需要传统 NMS 后处理,从而让推理过程更快、更轻量,也更容易部署到真实系统中。
NMS知识普及(如果你明白可跳过):
NMS 的全称是 Non-Maximum Suppression,中文叫 非极大值抑制。它的作用很简单:当模型对同一个目标预测出多个框时,只保留最合适的那个框,把重复框删除掉(YOLOv11系列是生成6400预测框不论多少个目标和图片)。
在目标检测中,模型通常会在同一个目标周围预测出多个候选框。例如一张图里只有一辆车,但模型可能会预测出好几个位置相近、大小相似的框。如果不处理,最终结果就会出现一个目标被多个框重复标出来的情况(很多人如果模型没训练好应该会遇见过这种情况,这个时候调高置信度会减少这种情况)。
NMS 的基本流程可以理解为:
1. 先按照置信度分数从高到低排序;
2. 选择分数最高的框作为保留框;
3. 计算其他框与这个框的 IoU 重叠程度;
4. 如果某个框和保留框重叠太高,就认为它是重复框并删除;
5. 重复这个过程,直到筛选出最终检测结果。这里的 IoU 表示两个框的重叠程度。IoU 越大,说明两个框越可能检测的是同一个目标。
举个简单例子:
A框:置信度 0.92
B框:置信度 0.85
C框:置信度 0.60如果 A 和 B 高度重叠,NMS 会保留分数更高的 A,删除 B;如果 C 和 A 不怎么重叠,就会保留 C。
个人总结:我理解的 NMS,本质上就是一个 重复框筛选器。传统 YOLO 通常需要 NMS,是因为它会输出大量候选框,其中很多框会围绕同一个目标重复预测。而 YOLOv26 的端到端无 NMS 推理,就是希望模型通过 one-to-one 检测头 直接输出更干净的结果,减少重复框,再用 Top-K 分数筛选替代传统基于 IoU 的 NMS。
在开始之前我们要明白YOLO系列的简易检测流程:
在传统 YOLO 中,检测流程大致可以理解为: 输入图像 ↓ Backbone 提取特征 ↓ Neck 多尺度特征融合 ↓ Detect Head 输出大量候选框 ↓ 置信度筛选 ↓ NMS 去除重复框 ↓ 最终检测结果这个流程的关键问题在于,模型输出的结果并不是最终结果,而是需要经过 NMS 再处理。比如同一个目标可能会被多个预测框同时检测到,NMS 会根据 IoU 判断这些框是否高度重叠,然后只保留置信度最高的一个框。
我认为这也是传统 YOLO 在部署时比较麻烦的地方:模型本身是一部分,NMS 后处理又是一部分。不同部署平台对 NMS 的支持程度不完全一致,尤其是在 TensorRT、ONNX、CoreML、TFLite 等格式导出时,NMS 可能会带来额外适配成本。
虽然 YOLOv26 去掉了传统 NMS,但模型推理后仍然会产生很多预测结果。只是相比传统 YOLO,YOLOv26 默认使用 One-to-One Head,输出结果更加干净、重复框更少。
但是模型仍然需要从这些预测结果中挑出最可信的一部分,所以就用了 Top-K 置信度筛选。
可以理解为:
模型输出很多预测框 ↓ 计算每个框的类别置信度 ↓ 按照置信度从高到低排序 ↓ 保留前 K 个最高分结果 ↓ 输出最终检测框它和 NMS 有什么区别?
传统 NMS 是这样做的:
看两个框是否重叠 如果 IoU 太高,就删除低分框 也就是说,NMS 需要计算框和框之间的 IoU 重叠程度。而 YOLOv26 的 Top-K 是这样做的:
不计算框和框之间的 IoU 只看类别置信度分数 直接选出分数最高的 K 个预测结果所以区别很明显:
NMS:根据 IoU 去重
Top-K:根据分数排序筛选
举个简单例子
假设模型输出了 5 个预测框,它们的置信度分别是:
框 A:0.95 框 B:0.88 框 C:0.76 框 D:0.40 框 E:0.22如果设置:
K = 3
那么 Top-K 就会保留:
框 A、框 B、框 C
删除:
框 D、框 E
它不会判断 A 和 B 是否重叠,也不会计算它们之间的 IoU,只是单纯按照分数排序。
2.3 移除 DFL
在 YOLOv26 中,我认为另一个非常重要的变化就是 移除 DFL(Distribution Focal Loss)。如果说端到端无 NMS 推理主要是在减少后处理依赖,那么移除 DFL 则更多是在简化检测头中的边界框回归流程,让模型在推理和导出时更加轻量、直接和硬件友好。
2.3.1 DFL解析
在开始之前简单介绍一下DFL
DFL是一个用于对象检测的损失函数模块,主要用于提高边界框回归的精度。它的核心思想是将每个预测的边界框参数( x, y, w, h)分解为多个通道,然后通过 softmax 操作得到一个分布,并计算分布的积分来预测实际值。
下面是部分代码解析!
class DFL(nn.Module): """ Integral module of Distribution Focal Loss (DFL). Proposed in Generalized Focal Loss https://ieeexplore.ieee.org/document/9792391 """ def __init__(self, c1=16): """Initialize a convolutional layer with a given number of input channels.""" super().__init__() self.conv = nn.Conv2d(c1, 1, 1, bias=False).requires_grad_(False) x = torch.arange(c1, dtype=torch.float) self.conv.weight.data[:] = nn.Parameter(x.view(1, c1, 1, 1)) self.c1 = c1c1:表示输入通道数,默认为 16。
self.conv:定义了一个卷积层,输入通道数为 c1,输出通道数为 1,卷积核大小为 1x1。这个卷积层的权重不更新,requires_grad_(False)不更新梯度的意思代表。
self.c1:保存输入通道数。def forward(self, x): """Applies a transformer layer on input tensor 'x' and returns a tensor.""" b, _, a = x.shape # batch, channels, anchors return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1)).view(b, 4, a)输入 x:形状为 [batch_size, channels, anchors]的张量,其中 channels为 4× c1,每个边界框参数(如 x, y, w, h)都有 c1个通道(这里和我们前面的解释一致)。
下面的操作为正向传播中最后一行代码的解析!
1.变形操作:
x.view(b, 4, self.c1, a)将 x变形为 [batch_size, 4, c1, anchors]的张量,其中 4 表示四个边界框参数(x, y, w, h),c1 是每个参数的通道数。
2. 转置操作:
.transpose(2, 1)将张量转置为 [batch_size, c1, 4, anchors],将通道维度 c1 和参数维度 4交换。
3. softmax 操作:
.softmax(1)在通道维度 c1上应用 softmax,得到每个参数的概率分布。
4. 卷积操作:
self.conv(...)使用卷积层 self.conv将分布的积分计算出来。卷积层的权重初始化为 0 到 c1-1,这样卷积操作实际上计算的是这些通道的加权平均值。
5. 变形回原始形状:
.view(b, 4, a)最终将输出变形为 [batch_size, 4, anchors],即每个锚点的四个边界框参数。
具体操作流程举例
1. 输入形状:假设输入张量 x的形状为 [2, 64, 100](batch_size=2,channels=64,anchors=100),其中 64 是 4 × c1(每个参数 16 个通道)。
2. 变形:将输入张量变形为 [2, 4, 16, 100],表示每个锚点的四个参数(x, y, w, h),每个参数有 16 个通道。
3. 转置:将张量转置为 [2, 16, 4, 100]。
4. softmax:在通道维度 16 上应用 softmax,得到每个参数的概率分布。
5. 卷积:使用卷积层计算分布的积分,得到每个参数的加权平均值。
6. 输出形状:将输出变形为 [2, 4, 100],即每个锚点的四个边界框参数。总结:DFL 模块通过将每个边界框参数分解为多个通道,使用 softmax 获得概率分布,然后通过卷积计算分布的积分来预测实际的边界框参数。这样的方法可以提高边界框回归的精度。
2.3.2 make_anchors
下面的代码是我们检测头代码中详解没有解析的内容大家可以去上面找一下!
def make_anchors(feats, strides, grid_cell_offset=0.5): """Generate anchors from features.""" anchor_points, stride_tensor = [], [] assert feats is not None dtype, device = feats[0].dtype, feats[0].device for i, stride in enumerate(strides): _, _, h, w = feats[i].shape sx = torch.arange(end=w, device=device, dtype=dtype) + grid_cell_offset # shift x sy = torch.arange(end=h, device=device, dtype=dtype) + grid_cell_offset # shift y sy, sx = torch.meshgrid(sy, sx, indexing="ij") if TORCH_1_10 else torch.meshgrid(sy, sx) anchor_points.append(torch.stack((sx, sy), -1).view(-1, 2)) stride_tensor.append(torch.full((h * w, 1), stride, dtype=dtype, device=device)) return torch.cat(anchor_points), torch.cat(stride_tensor)好的,让我们具体解释一下每个 anchor 如何表示特征图中每个单元格的位置(需要先理解的概念)。
什么是 Anchor
在对象检测中,anchor 是一个预定义的边界框模板,用于在特征图的每个单元格(即特征图的每个位置)上进行预测。每个 anchor 具有固定的大小和形状,模型通过调整这些 anchors 来拟合实际的物体边界框。
特征图和输入图像
假设我们有一个输入图像和对应的特征图:
- 输入图像大小:640x640
- 特征图大小:80x80
特征图的每个单元格对应于输入图像的一个区域。具体来说,特征图的一个单元格覆盖输入图像的 8x8 像素(假设 stride 为 8,前面讲过如何计算就是放缩比例640 ÷ 80 = 8)。
Anchor 在特征图中的位置
当我们在特征图上生成 anchors 时,每个单元格中心都会有一个 anchor。anchor 的位置用单元格的坐标表示(例如,特征图的第 (i, j) 个单元格,这些其实是图像基础的内容)。### 具体实现
在 make_anchors函数中,我们通过以下步骤生成 anchors(代码中内容的解析):
1. 生成网格点坐标
sx = torch.arange(end=w, device=device, dtype=dtype) + grid_cell_offset # shift x sy = torch.arange(end=h, device=device, dtype=dtype) + grid_cell_offset # shift y
- torch.arange(end=w, device=device, dtype=dtype)生成从 0 到 w-1 的序列,表示特征图宽度方向上的索引。
- torch.arange(end=h, device=device, dtype=dtype)生成从 0 到 h-1 的序列,表示特征图高度方向上的索引。
- grid_cell_offset(默认为 0.5)用于将网格点移动到单元格的中心。
2. 生成网格坐标的所有组合
sy, sx = torch.meshgrid(sy, sx, indexing="ij") if TORCH_1_10 else torch.meshgrid(sy, sx)
- torch.meshgrid(sy, sx)生成网格点的所有组合,即每个单元格的中心坐标。
3. 组合并展平
anchor_points.append(torch.stack((sx, sy), -1).view(-1, 2))
- torch.stack((sx, sy), -1)将 x 和 y 坐标堆叠在一起,形成形状为 (h, w, 2)的张量。
- .view(-1, 2) 将其展平为形状为 (h * w, 2)的二维张量,其中每一行表示一个 anchor 的中心点坐标。
例子说明(用具体例子给大家阐述一下辅助大家理解一下)
假设我们有一个 3x3 的特征图,其单元格的中心坐标如下:
(0.5, 0.5) (1.5, 0.5) (2.5, 0.5)
(0.5, 1.5) (1.5, 1.5) (2.5, 1.5)
(0.5, 2.5) (1.5, 2.5) (2.5, 2.5)使用 make_anchors函数生成的 anchor points 将是一个形状为 (9, 2)的张量 (数学内容),每一行表示一个单元格的中心坐标(其中的.5为偏移量!):
[[0.5, 0.5],
[1.5, 0.5],
[2.5, 0.5],
[0.5, 1.5],
[1.5, 1.5],
[2.5, 1.5],
[0.5, 2.5],
[1.5, 2.5],
[2.5, 2.5]]总结
每个 anchor 表示特征图中每个单元格的位置,这些位置是特征图网格点的中心坐标。通过 make_anchors函数,我们生成了这些 anchors,并将它们用于对象检测模型的边界框预测,同时还有sride为放缩比例同时计算。
2.2.3 dist2bbox
下面的代码为位置坐标信息的解码操作!
def dist2bbox(distance, anchor_points, xywh=True, dim=-1): """Transform distance(ltrb) to box(xywh or xyxy).""" lt, rb = distance.chunk(2, dim) x1y1 = anchor_points - lt x2y2 = anchor_points + rb if xywh: c_xy = (x1y1 + x2y2) / 2 wh = x2y2 - x1y1 return torch.cat((c_xy, wh), dim) # xywh bbox return torch.cat((x1y1, x2y2), dim) # xyxy bbox这段代码的作用是将距离转换为边界框坐标,可以选择转换为中心点坐标和宽高(xywh)或者左上角和右下角坐标(xyxy)。
def dist2bbox(distance, anchor_points, xywh=True, dim=-1): """Transform distance(ltrb) to box(xywh or xyxy)."""
- distance:表示从 anchor points 到边界框的左、上、右、下边界的距离(left, top, right, bottom)。
- anchor_points:anchor points 的坐标。
- xywh:布尔值,决定输出边界框的格式,默认为 `True,表示输出为中心点坐标和宽高(xywh),否则输出为左上角和右下角坐标(xyxy)。
- dim:指明在哪个维度上切分距离张量,默认值为 -1。
核心逻辑
1. 切分距离张量
lt, rb = distance.chunk(2, dim)将 distance 张量分为左右两个部分:
- lt:left, top 距离
- rb:right, bottom 距离
- chunk(2, dim) 表示在指定维度 dim 上将 distance切分为 2 个张量。
2. 计算左上角和右下角坐标
x1y1 = anchor_points - lt x2y2 = anchor_points + rb
- x1y1:左上角坐标,由 anchor points 减去 left 和 top 距离得到。
- x2y2:右下角坐标,由 anchor points 加上 right 和 bottom 距离得到。
3. 转换为指定格式的边界框
如果 xywh 为 True,转换为中心点坐标和宽高:
if xywh: c_xy = (x1y1 + x2y2) / 2 wh = x2y2 - x1y1 return torch.cat((c_xy, wh), dim) # xywh bbox
- c_xy:中心点坐标,由左上角和右下角坐标的平均值计算得到。
- wh:宽高,由右下角坐标减去左上角坐标计算得到。
最终返回中心点坐标和宽高的组合。
如果 xywh为 False,返回左上角和右下角坐标:
return torch.cat((x1y1, x2y2), dim) # xyxy bbox最终返回左上角和右下角坐标的组合。
示例
假设 distance为一个形状为 [N, 4] 的张量,其中 N是 anchor points 的数量,4表示 left, top, right, bottom 距离。anchor_points为一个形状为 [N, 2]的张量,表示每个 anchor 的坐标。
例子:转换为 xywh
distance = torch.tensor([[1, 1, 2, 2], [2, 2, 3, 3]]) anchor_points = torch.tensor([[5, 5], [10, 10]]) xywh = True bbox = dist2bbox(distance, anchor_points, xywh)步骤:
1. 切分 distance为 lt和 rb:
- lt:[[1, 1], [2, 2]]
- rb:[[2, 2], [3, 3]]
2. 计算 x1y1和 x2y2:
- x1y1:[[4, 4], [8, 8]]
- x2y2:[[7, 7], [13, 13]]
3. 转换为中心点坐标和宽高:
- c_xy:[[5.5, 5.5], [10.5, 10.5]]
- wh:[[3, 3], [5, 5]]
4. 返回的 bbox为 [[5.5, 5.5, 3, 3], [10.5, 10.5, 5, 5]]
例子:转换为 xyxy
xywh = False bbox = dist2bbox(distance, anchor_points, xywh)步骤:
1. 切分 distance为 lt 和rb:
- lt:[[1, 1], [2, 2]]
- rb:[[2, 2], [3, 3]]
2. 计算 x1y1和 x2y2:
- x1y1:[[4, 4], [8, 8]]
- x2y2:[[7, 7], [13, 13]]
3. 返回的bbox为 [[4, 4, 7, 7], [8, 8, 13, 13]]
总结:dist2bbox函数将表示 left, top, right, bottom 距离的张量转换为边界框坐标,可以选择转换为中心点坐标和宽高(xywh)或者左上角和右下角坐标(xyxy)。通过这个函数,模型可以从预测的距离值中计算出实际的边界框坐标。
作者:YOLOv26 移除 DFL 的核心目的,是为了减少边界框回归中的额外复杂度,使检测头更加简洁,并提升模型在边缘设备和低功耗设备上的部署兼容性。官方说明中也提到,DFL 虽然有效,但它会增加模型导出复杂度,并限制部分硬件平台的兼容性,因此 YOLOv26 选择将其移除,以简化推理流程。
我认为这里的重点不是“DFL 没有用”,而是 YOLOv26 的设计目标变了。
在以前的 YOLO 版本中,模型更关注如何通过更精细的回归方式提升定位精度;而 YOLOv26 更强调的是:模型结构更简单
推理流程更直接
部署图更干净
硬件兼容性更好
CPU 和边缘设备运行更快也就是说,YOLOv26 并不是单纯为了追求实验室环境下的精度,而是更加面向真实部署场景,YOLO其实之前偏学术这次更新之后很多工业场景也是可以使用了。
2.4 ProgLoss + STAL 小目标优化
在我看来,YOLOv26 的 ProgLoss + STAL 主要解决的是训练阶段的问题。它们不是新的卷积模块,也不是新的 Backbone 结构,而是围绕 端到端检测训练稳定性 和 小目标正样本分配不足 这两个问题进行优化。
简单来说:
ProgLoss 解决 one-to-many 和 one-to-one 双分支训练如何平衡的问题;STAL 解决小目标在标签分配阶段容易被忽略的问题(其实很简单就是两个动态系数来类似于大家熟知BiFPN那种思想)。
2.4.1 ProgLoss:渐进式损失平衡
ProgLoss核心代码(项目文件的ultralytics/utils/loss.py路径下)
class E2ELoss: """Criterion class for computing training losses for end-to-end detection.""" def __init__(self, model, loss_fn=v8DetectionLoss): """Initialize E2ELoss with one-to-many and one-to-one detection losses using the provided model.""" self.one2many = loss_fn(model, tal_topk=10) self.one2one = loss_fn(model, tal_topk=7, tal_topk2=1) self.updates = 0 self.total = 1.0 # init gain self.o2m = 0.8 self.o2o = self.total - self.o2m self.o2m_copy = self.o2m # final gain self.final_o2m = 0.1 def __call__(self, preds: Any, batch: dict[str, torch.Tensor]) -> tuple[torch.Tensor, torch.Tensor]: """Calculate the sum of the loss for box, cls and dfl multiplied by batch size.""" preds = self.one2many.parse_output(preds) one2many, one2one = preds["one2many"], preds["one2one"] loss_one2many = self.one2many.loss(one2many, batch) loss_one2one = self.one2one.loss(one2one, batch) return loss_one2many[0] * self.o2m + loss_one2one[0] * self.o2o, loss_one2one[1] def update(self) -> None: """Update the weights for one-to-many and one-to-one losses based on the decay schedule.""" self.updates += 1 self.o2m = self.decay(self.updates) self.o2o = max(self.total - self.o2m, 0) def decay(self, x) -> float: """Calculate the decayed weight for one-to-many loss based on the current update step.""" return max(1 - x / max(self.one2one.hyp.epochs - 1, 1), 0) * (self.o2m_copy - self.final_o2m) + self.final_o2m作者:YOLOv26 在训练阶段同时使用 One-to-Many Head 和 One-to-One Head。其中,One-to-Many 分支可以提供更密集的正样本监督,有利于训练早期稳定收敛;One-to-One 分支则更接近最终端到端无 NMS 推理方式(前面已经仔细介绍过了)。
但是如果两个分支的损失一直简单相加,就没有体现训练过程的阶段性需求。因此 YOLOv26 引入了 ProgLoss,也就是渐进式损失平衡。从源码逻辑来看,ProgLoss 会为两个分支设置动态权重:
loss = loss_one2many[0] * self.o2m + loss_one2one[0] * self.o2o训练初期:
one-to-many 权重较大,例如 0.8
one-to-one 权重较小,例如 0.2训练后期:
one-to-many 权重逐渐降低,例如降到 0.1
one-to-one 权重逐渐升高,例如升到 0.9我理解这个设计的核心是:
训练前期:依赖 one-to-many 提供密集监督,让模型更容易学起来
训练后期:逐渐强化 one-to-one,使训练目标更接近最终推理方式也就是说,ProgLoss 并不是一个损失函数(类似GIoU,CIoU那样的损失函数),这里后面如果可以大家是可以进行改进的,代码改进也很简单.
2.4.2 STAL:小目标感知标签分配
YOLOv26 引入 STAL,Small-Target-Aware Label Assignment,也就是小目标感知标签分配策略。它主要解决小目标在标签分配阶段容易获得过少正样本的问题。
在普通 TAL 标签分配中,模型会判断 anchor center 是否落在 GT 框内部。如果目标尺寸很小,比如宽高小于最小 stride,那么可能几乎没有 anchor center 落入目标框内部。这样一来,小目标就很难被分配到足够的正样本,训练时对 loss 的贡献也会变弱。
核心代码(项目文件的:ultralytics/utils/tal.py路径下)
def select_candidates_in_gts(self, xy_centers, gt_bboxes, mask_gt, eps=1e-9): """Select positive anchor centers within ground truth bounding boxes. Args: xy_centers (torch.Tensor): Anchor center coordinates, shape (h*w, 2). gt_bboxes (torch.Tensor): Ground truth bounding boxes, shape (b, n_boxes, 4). mask_gt (torch.Tensor): Mask for valid ground truth boxes, shape (b, n_boxes, 1). eps (float, optional): Small value for numerical stability. Returns: (torch.Tensor): Boolean mask of positive anchors, shape (b, n_boxes, h*w). Notes: - b: batch size, n_boxes: number of ground truth boxes, h: height, w: width. - Bounding box format: [x_min, y_min, x_max, y_max]. """ gt_bboxes_xywh = xyxy2xywh(gt_bboxes) wh_mask = gt_bboxes_xywh[..., 2:] < self.stride[0] # the smallest stride gt_bboxes_xywh[..., 2:] = torch.where( (wh_mask * mask_gt).bool(), torch.tensor(self.stride_val, dtype=gt_bboxes_xywh.dtype, device=gt_bboxes_xywh.device), gt_bboxes_xywh[..., 2:], ) gt_bboxes = xywh2xyxy(gt_bboxes_xywh) n_anchors = xy_centers.shape[0] bs, n_boxes, _ = gt_bboxes.shape lt, rb = gt_bboxes.view(-1, 1, 4).chunk(2, 2) # left-top, right-bottom bbox_deltas = torch.cat((xy_centers[None] - lt, rb - xy_centers[None]), dim=2).view(bs, n_boxes, n_anchors, -1) return bbox_deltas.amin(3).gt_(eps)从源码逻辑来看,STAL 的关键做法是:
wh_mask = gt_bboxes_xywh[..., 2:] < self.stride[0]也就是判断 GT 框的宽高是否小于最小 stride。通常最小 stride 是 8,如果目标宽或高小于 8,就会被视为极小目标。
然后代码会在标签分配阶段临时扩大这个小目标的候选区域:
gt_bboxes_xywh[..., 2:] = torch.where( (wh_mask * mask_gt).bool(), torch.tensor(self.stride_val, dtype=gt_bboxes_xywh.dtype, device=gt_bboxes_xywh.device), gt_bboxes_xywh[..., 2:], )这里需要注意:它不是修改真实标注框,也不是改变最终训练的 GT 框,而是在正样本分配阶段临时扩大候选范围(也就是临时扩大一下,其实这样的做法虽然会增加小目标,但也有可能会增加负样本损失)。
我理解它的作用是:
小目标原本太小,可能匹配不到足够 anchor point ↓ STAL 在标签分配阶段临时扩大候选区域 ↓ 让更多 anchor center 有机会落入目标范围 ↓ 小目标获得更多正样本监督 ↓ 提升小目标召回和检测稳定性如果最小 stride 是 8,临时扩展到 stride_val=16,那么在 stride=8 的特征层上,大约可以覆盖:
2 × 2 = 4 个候选点
这也可以理解为 STAL 对极小目标的一种“正样本保护机制”。
Ultralytics作者:ProgLoss 通过动态调整 one-to-many 和 one-to-one 分支的损失权重,使模型在训练早期更加稳定,在训练后期更加贴近端到端无 NMS 推理;STAL 则通过小目标感知标签分配,让极小目标在训练阶段获得更多正样本监督,避免小目标被普通 TAL 分配机制忽略。
2.5 MuSGD 优化器
在我看来,YOLOv26 的 MuSGD 优化器 属于训练策略层面的改进。前面介绍的 NMS-Free、双检测头、移除 DFL 更多是在优化模型结构和推理流程,而 MuSGD 主要解决的是:模型训练过程能不能更稳定、更快收敛,以及不同模型规模下训练表现是否更可控。
Ultralytics作者:YOLOv26 引入 MuSGD,是将传统 SGD 与 Muon 优化思想结合起来的一种混合优化器。官方资料中也明确提到,MuSGD 是 SGD 和 Muon 的 hybrid optimizer,目的是提升训练稳定性并加快收敛速度。
2.5.1 为什么还要改优化器?
在目标检测模型中,优化器看起来不像 Backbone、Neck、Head 那样直观,但它对最终训练效果影响很大。比如同样一个模型,如果优化器不稳定,可能会出现:
训练初期 loss 波动大;
不同 batch 或不同数据集下收敛不稳定;
训练后期精度提升缓慢;
需要反复调学习率、动量、权重衰减等参数。
传统 YOLO 模型中常用的优化器主要是 SGD (项目文件之前用的都说SGD)或 AdamW。SGD 的优势是泛化能力比较好,训练结果相对稳健;但它的问题是收敛速度可能偏慢,对学习率和动量等参数比较敏感。AdamW 收敛通常更快,但在一些检测任务中也可能需要更细致的超参数设置。
所以我理解 YOLOv26 引入 MuSGD 的目的,是想在 SGD 的稳定泛化能力 和 Muon 风格更新的快速收敛能力 之间做一个折中。
2.5.2 MuSGD 的核心思想
Ultralytics作者:MuSGD 采用的是一种混合更新策略。部分参数会结合 Muon-style update 和 SGD 进行更新,而另一部分参数仍然保持纯 SGD 更新。这样既保留了 SGD 在视觉任务中的稳定性,又引入了 Muon 在优化过程中的平滑性和加速收敛能力。资料中也提到,MuSGD 可以带来更平滑的优化过程、更快的收敛速度,以及在不同模型尺寸下更可预测的训练行为。
可以简单理解为:
SGD: 稳定、泛化好,但有时收敛慢。 Muon: 优化更新更激进,更强调稳定快速收敛,最初更多受到大模型训练经验启发。 MuSGD: 把两者结合起来,让模型既保持稳定,又能更快收敛。我认为这里比较重要的一点是,MuSGD 并不是单纯把优化器名字换掉,而是对应了 YOLOv26 整体的训练优化思路。因为 YOLOv26 引入了 one-to-one / one-to-many 双检测头、ProgLoss 动态权重、STAL 小目标标签分配,这些都会让训练过程比传统单检测头更复杂。因此,一个更稳定的优化器可以帮助模型更平滑地完成训练。
2.6 网络结构变动
YOLOv26 的网络结构没啥大的变化他的优化点基本上都在上面内容介绍了。YOLOv26 结构的变化就两点变化1 SPPF 增加 shortcut、变化2最后的 C3k2 引入 PSABlock,具体内容在下面的图片中可以看到。
左图为YOLOv11网络结构、右图为YOLOv26网络结构图
变化1:SPPF变化
Ultralytics作者:YOLOv26 在 SPPF(Spatial Pyramid Pooling Fast) 模块中加入了 shortcut 连接。SPPF 原本的作用是通过不同尺度的池化操作扩大感受野,让模型能够获取更丰富的上下文信息。而加入 shortcut 后,在池化增强的基础上,进一步保留原始语义信息(其实在SPPF里加入Shortcut由此可以看出这个模块其实不是很必要的,SPPF最主要作用还是加速模型的训练速度)。
变化2:C3k2 引入 PSABlock
Ultralytics作者:YOLOv26 在靠近检测头的最后一个 C3k2 模块中加入了 PSABlock 注意力结构。
我认为这个设计比较关键。因为越靠近检测头的特征,越直接影响最终分类和定位结果。如果在这个位置加入注意力机制,可以帮助模型更好地关注关键目标区域,同时增强全局上下文建模能力。
可以简单理解为:
普通 C3k2: 主要依靠卷积提取局部特征; 加入 PSABlock 后: 在局部特征基础上增强全局信息交互(就是加了一个注意力机制模块)。但是 YOLOv26 并没有在大量位置堆叠复杂注意力,而是在靠近检测头的关键位置使用 PSABlock。我认为这种做法比较符合它的设计思路:尽量用较小的计算代价换取有效的特征增强(这里其实衍生一个思路比如你做的是小目标的是否可以把这里放在小目标的检测头前)。
C3k2的三种不同情况,最上面为改进后的,中间的为标准的C3k2,最后的为C2f.
C3k2的核心代码,对应上面的三种情况!
class C3k2(C2f): """Faster Implementation of CSP Bottleneck with 2 convolutions.""" def __init__( self, c1: int, c2: int, n: int = 1, c3k: bool = False, e: float = 0.5, attn: bool = False, g: int = 1, shortcut: bool = True, ): """Initialize C3k2 module. Args: c1 (int): Input channels. c2 (int): Output channels. n (int): Number of blocks. c3k (bool): Whether to use C3k blocks. e (float): Expansion ratio. attn (bool): Whether to use attention blocks. g (int): Groups for convolutions. shortcut (bool): Whether to use shortcut connections. """ super().__init__(c1, c2, n, shortcut, g, e) self.m = nn.ModuleList( nn.Sequential( Bottleneck(self.c, self.c, shortcut, g), PSABlock(self.c, attn_ratio=0.5, num_heads=max(self.c // 64, 1)), ) if attn else C3k(self.c, self.c, 2, shortcut, g) if c3k else Bottleneck(self.c, self.c, shortcut, g) for _ in range(n) )
三、YOLOv11下载、环境安装、数据集获取
YOLOv26的下载大家可以通过点击下面的链接进行下载->
官方下载地址:YOLOv26官方Github下载地址点击此处即可跳转.
点进去之后按照如下图所示的操作即可下载ultralytics仓库到本地.
下载到本地之后大家解压缩利用自己的IDEA打开即可了,环境搭建大家可以看我另一篇文章,这里由于篇幅原因就不多介绍了,如果你自己有环境了跳过此步即可.
环境安装下载:环境安装教程点击此处即可跳转.
如果你无法安装环境,作者提供无需搭建环境的运行方法,可联系作者.
数据集获取方法可以看以下文章内容,利用roboflow获取大量数据集(1000w+数据集任你挑选)
四、模型训练
上面给大家讲完了网络的创新下面给大家讲一下YOLOv26如何进行训练预测验证等操作。
我门打开ultralytics/cfg/default.yaml文件可以配置模型的参数,在其中和模型训练有关的参数及其解释如下:
| 参数名 | 输入类型 | 参数解释 | |
|---|---|---|---|
| 0 | task | str | YOLO模型的任务选择,选择你是要进行检测、分类等操作 |
| 1 | mode | str | YOLO模式的选择,选择要进行训练、推理、输出、验证等操作 |
| 2 | model | str/optional | 模型的文件,可以是官方的预训练模型,也可以是训练自己模型的yaml文件 |
| 3 | data | str/optional | 模型的地址,可以是文件的地址,也可以是配置好地址的yaml文件 |
| 4 | epochs | int | 训练的轮次,将你的数据输入到模型里进行训练的次数 |
| 5 | patience | int | 早停机制,当你的模型精度没有改进了就提前停止训练 |
| 6 | batch | int | 我们输入的数据集会分解为多个子集,一次向模型里输入多少个子集 |
| 7 | imgsz | int/list | 输入的图片的大小,可以是整数就代表图片尺寸为int*int,或者list分别代表宽和高[w,h] |
| 8 | save | bool | 是否保存模型以及预测结果 |
| 9 | save_period | int | 在训练过程中多少次保存一次模型文件,就是生成的pt文件 |
| 10 | cache | bool | 参数cache用于控制是否启用缓存机制。 |
| 11 | device | int/str/list/optional | GPU设备的选择:cuda device=0 or device=0,1,2,3 or device=cpu |
| 12 | workers | int | 工作的线程,Windows系统一定要设置为0否则很可能会引起线程报错 |
| 13 | name | str/optional | 模型保存的名字,结果会保存到'project/name' 目录下 |
| 14 | exist_ok | bool | 如果模型存在的时候是否进行覆盖操作 |
| 15 | prepetrained |
bool |
参数pretrained用于控制是否使用预训练模型。 |
| 16 | optimizer | str | 优化器的选择choices=[SGD, Adam, Adamax, AdamW, NAdam, RAdam, RMSProp, auto] |
| 17 | verbose | bool | 用于控制在执行过程中是否输出详细的信息和日志。 |
| 18 | seed | int | 随机数种子,模型中涉及到随机的时候,根据随机数种子进行生成 |
| 19 | deterministic | bool | 用于控制是否启用确定性模式,在确定性模式下,算法的执行将变得可重复,即相同的输入将产生相同的输出 |
| 20 | single_cls | bool | 是否是单标签训练 |
| 21 | rect | bool | 当 rect 设置为 True 时,表示启用矩形训练或验证。矩形训练或验证是一种数据处理技术,其中在训练或验证过程中,输入数据会被调整为具有相同宽高比的矩形形状。 |
| 22 |
cos_lr |
bool | 控制是否使用余弦学习率调度器 |
| 23 | close_mosaic | int | 控制在最后几个 epochs 中是否禁用马赛克数据增强 |
| 24 | resume | bool | 用于从先前的训练检查点(checkpoint)中恢复模型的训练。 |
| 25 | amp | bool | 用于控制是否进行自动混合精度 |
| 26 | fraction | float | 用于指定训练数据集的一部分进行训练的比例。默认值为 1.0 |
| 27 | profile | bool | 用于控制是否在训练过程中启用 ONNX 和 TensorRT 的性能分析 |
| 28 | freeze | int/list/optinal | 用于指定在训练过程中冻结前 n 层或指定层索引的列表,以防止它们的权重更新。这对于迁移学习或特定层的微调很有用。 |
4.1 训练的三种方式
4.1.1 方式一
我们可以通过命令直接进行训练在其中指定参数,但是这样的方式,我们每个参数都要在其中打出来。命令如下:
yolo task=detect mode=train model=yolov26.pt data=data.yaml batch=16 epochs=100 imgsz=640 workers=0 device=0
需要注意的是如果你是Windows系统的电脑其中的Workers最好设置成0否则容易报线程的错误。
5.1.2 方式二
通过指定cfg直接进行训练,我们配置好ultralytics/cfg/default.yaml这个文件之后,可以直接执行这个文件进行训练,这样就不用在命令行输入其它的参数了。
yolo cfg=ultralytics/cfg/default.yaml
5.1.3 方式三 (推荐,避免keyError错误.)
我们可以通过创建py文件来进行训练,这样的好处就是不用在终端上打命令,这也能省去一些工作量,我们在根目录下创建一个名字为train.py的文件,在其中输入代码
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
model = YOLO('yolo26.yaml')
# 如何切换模型版本, 上面的ymal文件可以改为 yolov11s.yaml就是使用的v11s,
# 类似某个改进的yaml文件名称为yolov11-XXX.yaml那么如果想使用其它版本就把上面的名称改为yolov11l-XXX.yaml即可(改的是上面YOLO中间的名字不是配置文件的)!
# model.load('yolov11n.pt') # 是否加载预训练权重,科研不建议大家加载否则很难提升精度
model.train(data=r"填写你数据集data.yaml文件的地址",
# 如果大家任务是其它的'ultralytics/cfg/default.yaml'找到这里修改task可以改成detect, segment, classify, pose
cache=False,
imgsz=640,
epochs=100,
single_cls=False, # 是否是单类别检测
batch=4,
close_mosaic=0,
workers=0,
device='0',
optimizer='SGD', # using SGD 优化器 默认为auto建议大家使用固定的.
# resume=, # 续训的话这里填写True, yaml文件的地方改为lats.pt的地址,需要注意的是如果你设置训练200轮次模型训练了200轮次是没有办法进行续训的.
amp=True, # 如果出现训练损失为Nan可以关闭amp
project='runs/train',
name='exp',
)
无论通过上述的哪一种方式在控制台输出如下图片的内容就代表着开始训练成功了!
六、模型验证/测试
| 参数名 | 类型 | 参数讲解 | |
|---|---|---|---|
| 1 | val | bool | 用于控制是否在训练过程中进行验证/测试。 |
| 2 | split | str | 用于指定用于验证/测试的数据集划分。可以选择 'val'、'test' 或 'train' 中的一个作为验证/测试数据集 |
| 3 | save_json | bool | 用于控制是否将结果保存为 JSON 文件 |
| 4 | save_hybird | bool | 用于控制是否保存标签和附加预测结果的混合版本 |
| 5 | conf | float/optional | 用于设置检测时的目标置信度阈值 |
| 6 | iou | float | 用于设置非极大值抑制(NMS)的交并比(IoU)阈值。 |
| 7 | max_det | int | 用于设置每张图像的最大检测数。 |
| 8 | half | bool | 用于控制是否使用半精度(FP16)进行推断。 |
| 9 | dnn | bool | ,用于控制是否使用 OpenCV DNN 进行 ONNX 推断。 |
| 10 | plots | bool | 用于控制在训练/验证过程中是否保存绘图结果。 |
验证我们划分的验证集/测试集的情况,也就是评估我们训练出来的best.pt模型好与坏
命令行命令如下:
yolo task=detect mode=val model=模型地址 data=数据集地址 device=0
代码运行方式如下:
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
model = YOLO('替换大家想要验证的模型地址')
model.val(data=r'替换大家自己的数据集文件的yaml文件地址',\
split='val',
imgsz=640,
batch=16,
# rect=False,
# save_json=True, # 这个保存coco精度指标的开关
project='runs/val',
name='exp',
)
七、模型推理
我们训练好自己的模型之后,都会生成一个模型文件,保存在你设置的目录下,当我们再次想要实验该模型的效果之后就可以调用该模型进行推理了,我们也可以用官方的预训练权重来进行推理。
推理的方式和训练一样我们这里就选一种来进行举例其它的两种方式都是一样的操作只是需要改一下其中的一些参数即可:
参数讲解
| 参数名 | 类型 | 参数讲解 | |
|---|---|---|---|
| 0 | source | str/optinal | 用于指定图像或视频的目录 |
| 1 | show | bool | 用于控制是否在可能的情况下显示结果 |
| 2 | save_txt | bool | 用于控制是否将结果保存为 .txt 文件 |
| 3 | save_conf | bool | 用于控制是否在保存结果时包含置信度分数 |
| 4 | save_crop | bool | 用于控制是否将带有结果的裁剪图像保存下来 |
| 5 | show_labels | bool | 用于控制在绘图结果中是否显示目标标签 |
| 6 | show_conf | bool | 用于控制在绘图结果中是否显示目标置信度分数 |
| 7 | vid_stride | int/optional | 用于设置视频的帧率步长 |
| 8 | stream_buffer | bool | 用于控制是否缓冲所有流式帧(True)或返回最新的帧(False) |
| 9 | line_width | int/list[int]/optional | 用于设置边界框的线宽度,如果缺失则自动设置 |
| 10 | visualize | bool | 用于控制是否可视化模型的特征 |
| 11 | augment | bool | 用于控制是否对预测源应用图像增强 |
| 12 | agnostic_nms | bool | 用于控制是否使用无关类别的非极大值抑制(NMS) |
| 13 | classes | int/list[int]/optional | 用于按类别筛选结果 |
| 14 | retina_masks | bool | 用于控制是否使用高分辨率分割掩码 |
| 15 | boxes | bool | 用于控制是否在分割预测中显示边界框。 |
命令行命令如下:
yolo task=detect mode=predict model=模型地址 source=推理的视频图片地址 device=0
这里需要需要注意的是我们用模型进行推理的时候可以选择照片也可以选择一个视频的格式都可以。支持的视频格式有
MP4(.mp4):这是一种常见的视频文件格式,通常具有较高的压缩率和良好的视频质量
AVI(.avi):这是一种较旧但仍广泛使用的视频文件格式。它通常具有较大的文件大小
MOV(.mov):这是一种常见的视频文件格式,通常与苹果设备和QuickTime播放器相关
MKV(.mkv):这是一种开放的多媒体容器格式,可以容纳多个视频、音频和字幕轨道
FLV(.flv):这是一种用于在线视频传输的流式视频文件格式
代码推理方式如下:
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
model = YOLO('模型地址') # select your model.pt path
model.predict(source='想要推理的视频或者文件地址',
imgsz=640,
project='runs/detect',
name='exp',
save=True,
show=False,
# classes='all', # 是否指定检测某个类别.
)
八、模型输出
当我们进行部署的时候可以进行文件导出,然后在进行部署。
YOLOv8支持的输出格式有如下
1. ONNX(Open Neural Network Exchange):ONNX 是一个开放的深度学习模型表示和转换的标准。它允许在不同的深度学习框架之间共享模型,并支持跨平台部署。导出为 ONNX 格式的模型可以在支持 ONNX 的推理引擎中进行部署和推理。
2. TensorFlow SavedModel:TensorFlow SavedModel 是 TensorFlow 框架的标准模型保存格式。它包含了模型的网络结构和参数,可以方便地在 TensorFlow 的推理环境中加载和使用。
3. PyTorch JIT(Just-In-Time):PyTorch JIT 是 PyTorch 的即时编译器,可以将 PyTorch 模型导出为优化的 Torch 脚本或 Torch 脚本模型。这种格式可以在没有 PyTorch 环境的情况下进行推理,并且具有更高的性能。
4. Caffe Model:Caffe 是一个流行的深度学习框架,它使用自己的模型表示格式。导出为 Caffe 模型的文件可以在 Caffe 框架中进行部署和推理。
5. TFLite(TensorFlow Lite):TFLite 是 TensorFlow 的移动和嵌入式设备推理框架,支持在资源受限的设备上进行高效推理。模型可以导出为 TFLite 格式,以便在移动设备或嵌入式系统中进行部署。
6. Core ML(Core Machine Learning):Core ML 是苹果的机器学习框架,用于在 iOS 和 macOS 上进行推理。模型可以导出为 Core ML 格式,以便在苹果设备上进行部署。
这些格式都提供了不同的优势和适用场景。选择合适的导出格式应该考虑到目标平台和部署环境的要求,以及所使用的深度学习框架的支持情况。
模型输出的参数有如下
| 参数名 | 类型 | 参数解释 | |
|---|---|---|---|
| 0 | format | str | 导出模型的格式 |
| 1 | keras | bool | 表示是否使用Keras |
| 2 | optimize | bool | 用于在导出TorchScript模型时进行优化,以便在移动设备上获得更好的性能 |
| 3 | int8 | bool | 用于在导出CoreML或TensorFlow模型时进行INT8量化 |
| 4 | dynamic | bool | 用于在导出CoreML或TensorFlow模型时进行INT8量化 |
| 5 | simplify | bool | 用于在导出ONNX模型时进行模型简化 |
| 6 | opset | int/optional | 用于指定导出ONNX模型时的opset版本 |
| 7 | workspace | int | 用于指定TensorRT模型的工作空间大小,以GB为单位 |
| 8 | nms | bool | 用于在导出CoreML模型时添加非极大值抑制(NMS) |
命令行命令如下:
yolo task=detect mode=export model=best.pt format=onnx
代码模型输出如下:
from ultralytics import YOLO
# 加载一个模型,路径为 YOLO 模型的 .pt 文件
model = YOLO("想要导出的模型地址")
# 导出模型,格式为 ONNX
model.export(format="onnx")
九、本文总结
到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv26有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~
专栏回顾:YOLOv26有效涨点专栏包含:Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐







所有评论(0)