YOLOv8(6)版本更迭 改进策略
核心定位:首次提出将目标检测视为「回归问题」,摆脱传统两阶段检测(如 R-CNN)的繁琐流程。关键改进 / 特点:用单一 CNN 网络完成「目标定位 + 类别预测」,无需分区域提取候选框,端到端训练 / 推理;将输入图片划分为 7×7 网格,每个网格预测 2 个边界框 + 类别概率;速度极快(45 FPS),但缺点明显:小目标检测差、定位精度低、召回率(漏检率)高。
YOLO 版本简介
YOLOv1(2016):开创「端到端实时检测」先河
核心定位:首次提出将目标检测视为「回归问题」,摆脱传统两阶段检测(如 R-CNN)的繁琐流程。
关键改进 / 特点:
用单一 CNN 网络完成「目标定位 + 类别预测」,无需分区域提取候选框,端到端训练 / 推理;
将输入图片划分为 7×7 网格,每个网格预测 2 个边界框 + 类别概率;
速度极快(45 FPS),但缺点明显:小目标检测差、定位精度低、召回率(漏检率)高。
YOLOv2(2017,YOLO9000):精度 + 速度双提升
核心定位:解决 v1 精度差的问题,支持更多类别检测(命名 YOLO9000)。
关键改进:
引入锚框(Anchor Boxes):不再依赖网格直接回归,而是基于预定义的锚框预测偏移,大幅提升定位精度;
新增Batch Normalization:稳定训练,提升模型泛化能力;
高分辨率预训练:先用 224×224 训练,再用 448×448 微调,适配检测任务的高分辨率输入;
维度聚类:用数据集自动聚类锚框尺寸,而非人工设定;
速度提升至 67 FPS,支持 9000 + 类别的联合训练。
YOLOv3(2018):多尺度检测 + 更强骨干网
核心定位:结合当时先进的特征融合技术,平衡精度和速度。
关键改进:
替换骨干网为Darknet-53:比 v2 的 Darknet-19 更深,引入残差连接,特征提取能力更强;
引入特征金字塔(FPN):多尺度预测(3 个尺度),解决小目标检测差的问题;
用逻辑回归替代 Softmax:支持多标签分类(如一个目标同时属于「人」+「戴帽子」);
精度显著提升,速度仍保持实时(30+ FPS),成为当时工业界主流。
YOLOv4(2020):集大成的工业级方案
核心定位:整合当时所有主流优化技巧,打造「开箱即用」的高性能检测模型。
关键改进:
骨干网升级为CSPDarknet53:引入 CSP 结构,减少计算量同时保持特征能力;
Neck 部分新增SPP 模块(空间金字塔池化) + PANet:增强多尺度特征融合;
训练策略优化:Mosaic 数据增强(4 张图拼接)、CIoU 损失(更精准的边界框回归)、DropBlock 正则化;
精度远超 v3,且无需额外算力,适合工业部署。
YOLOv5(2020,Ultralytics):易用性 + 轻量化的天花板
核心定位:非官方版本(原作者停更 YOLO),但生态最火,主打「易用、轻量化、部署友好」。
关键改进:
多尺度模型设计:提供 n/s/m/l/x 5 个版本(从超轻量到高性能),适配不同硬件;
新增Focus 结构:切片操作减少计算量,提升特征提取效率;
自适应优化:自适应锚框计算、自适应图片缩放(避免黑边);
全流程工具链:PyTorch 原生实现,支持一键导出 ONNX/TensorRT 等格式,部署成本极低;
速度和精度平衡极佳(n 版本可达 140 FPS),成为新手入门首选。
YOLOv6(2022,美团):工业级实时检测优化
核心定位:针对美团业务场景,优化「实时性 + 部署效率」。
关键改进:
骨干网用RepVGG:训练时多分支,推理时重参数化为单分支,速度大幅提升;
改用Anchor-free 检测头:摆脱锚框依赖,减少人工调参;
新增 SimOTA 标签分配策略:更精准地匹配预测框和真实框;
速度比 YOLOv5 更快,适合高并发的工业场景(如外卖配送视觉检测)。
YOLOv7(2022,原 YOLOv4 团队):精度优先的实时检测
核心定位:在相同速度下,把检测精度做到极致。
关键改进:
引入ELAN/E-ELAN 模块:高效特征融合,提升模型表达能力;
模型重参数化:训练和推理阶段的网络结构解耦,兼顾训练效果和推理速度;
辅助头训练:新增分类辅助头,提升检测精度;
动态标签分配:自适应分配正负样本,减少漏检;
相同速度下,精度远超 YOLOv5/v6,支持 5~160 FPS 的全范围速度需求。
YOLOv8(2023,Ultralytics):统一多任务的终极版本
核心定位:YOLOv5 的全面升级,统一「检测 / 分割 / 分类 / 姿态估计」多任务。
关键改进:
骨干网替换为C2f 模块:替代 v5 的 C3 模块,特征融合更高效;
Neck 部分简化:去除冗余的上采样步骤,计算量降低;
全面改用Anchor-free 检测头:无需预计算锚框,适配更多场景;
新标签分配策略(Task-Aligned Assigner):更精准匹配任务目标;
损失函数优化:分类用 CE 损失,回归用 DFL+CIoU 损失;
支持多任务:一套框架搞定检测、实例分割、图像分类、人体姿态估计,训练效率提升 30
%+。
通俗解释
我用「打靶」这个生活化的比喻,把 YOLOv1 到 v8 的进步讲得明明白白,全程不用专业术语,只说核心变化:
YOLOv1(2016):第一次用「一杆枪」打完所有靶
原来的做法:先在靶纸上画一堆候选框(找可能有靶的区域),再逐个判断是不是靶、是啥靶(两阶段),又慢又麻烦。
v1 的进步:直接用「一杆枪」瞄准整张靶纸,把靶纸分成 49 个小格子,每个格子直接说「有没有靶、靶在哪、是啥靶」。
缺点:枪法糙 —— 小靶打不准、靶的位置估不准,还容易漏靶,但胜在快(45 发 / 秒)。
YOLOv2(2017):给枪装了「瞄准镜」
核心进步:不再瞎猜靶的位置,而是先定几个常用的「靶框尺寸」(锚框),就像给枪装了瞄准镜,先对准大概率有靶的位置再微调,打靶精度直接提上来。
额外优化:提前用高清靶纸练枪(高分辨率预训练),还能同时打 9000 种不同的靶(YOLO9000),又快(67 发 / 秒)又准了点。
YOLOv3(2018):换了「更稳的枪身」+「多倍镜」
核心进步 1:把原来的「普通枪身」换成「Darknet-53」(带残差的深网络),枪更稳,能看清靶纸上的细节;
核心进步 2:加了「多倍镜」(特征金字塔),远的小靶(小目标)、近的大靶都能看清,不会再漏小靶;
额外优化:一个靶上有多个标签(比如「人 + 戴帽子」)也能认出来,精度又上一个台阶。
YOLOv4(2020):把所有「打靶技巧」都整合了
核心进步:相当于找了 10 个顶尖射手,把他们的技巧全学了 —— 换了「更轻但更硬的枪身」(CSPDarknet53)、加了「多角度瞄准器」(SPP+PANet)、练枪时故意混着不同靶纸练(Mosaic 增强);
结果:不用换更贵的枪(额外算力),但打靶精度远超 v3,工业上直接能用。
YOLOv5(2020):做了「多款定制枪」+「傻瓜式操作」
核心进步 1:不再只做一把枪,而是做了 5 款(n/s/m/l/x)—— 小枪(n 版)轻便,适合手机 / 边缘设备(140 发 / 秒),长枪(x 版)精准,适合服务器;
核心进步 2:枪上装了「自动校准」—— 不用手动调靶框尺寸、不用手动裁靶纸,新手拿过来就能用,还能一键把枪改成适配不同场地的版本(导出 ONNX/TensorRT);
结果:生态最火,新手入门首选,又好用又省心。
YOLOv6(2022):把枪改成「一键连发」
核心进步:枪的内部结构优化(RepVGG 重参数化)—— 练枪时是复杂结构(打得准),实际用的时候变成简单结构(打得快),像「一键连发」,美团外卖场景下高并发打靶也不卡;
额外优化:不用再提前定靶框尺寸(Anchor-free),不管靶是大是小,都能直接打,少了调参的麻烦。
YOLOv7(2022):同款枪,打得更准
核心进步:在和 v5/v6 同款射速的情况下,把「瞄准算法」做到极致 —— 练枪时加了「辅助瞄准」(分类辅助头)、自动选该打哪些靶(动态标签分配);
结果:同样是 100 发 / 秒,v7 能打中 95 个靶,v5 只能打中 88 个,精度拉满。
YOLOv8(2023):一把枪能打「所有类型的靶」
核心进步 1:枪身再升级(C2f 模块),更轻更准,还去掉了没用的瞄准步骤,效率更高;
核心进步 2:彻底不用提前定靶框(全 Anchor-free),不管啥靶都能打;
核心进步 3:一把枪适配所有场景 —— 既能打普通靶(检测)、又能打带分割线的靶(分割)、还能打人体姿态靶(姿态估计),不用换枪,一套搞定。
总结
用「打靶」总结 YOLO 的核心进步:
从「瞎打」到「精准打」:v1 瞎猜位置,v2-v7 不断优化瞄准方式,v8 直接不用预设靶框;
从「一把枪」到「多用途枪」:v1-v4 只打检测靶,v8 能打检测、分割、姿态等多种靶;
从「专业选手用」到「普通人用」:v1-v4 需要调参,v5-v8 越来越傻瓜化,新手也能上手,还适配各种设备。
基础的四种改进方向
加注意力机制
- 加注意力机制:给枪装「智能瞄准镜」
原来的问题:YOLO 看图片时,把背景和目标平分注意力(比如打「行人靶」时,还盯着路边的树),小靶 / 模糊靶容易漏。
优化逻辑:给模型加「注意力模块」,就像瞄准镜自动聚焦靶的核心区域(比如行人的躯干 / 头部),忽略无关背景,把算力和注意力都用在关键地方。
效果:小目标、遮挡目标的命中率显著提升,大目标定位更准
做堆叠
多把枪「层层瞄准 / 投票」
原来的问题:单把枪打靶,偶尔会看错(比如把「猫靶」认成「狗靶」),精度有上限。
优化逻辑:两种堆叠思路(新手优先选简单的):
「模块堆叠」:把 YOLO 的核心模块(比如特征融合的 C2f 模块)多叠一层,就像第一把枪粗瞄找靶的大致位置,第二把枪精瞄靶框,层层递进;
「模型堆叠」:训练 3 个不同版本的 YOLO 模型,打靶时取 3 个模型的投票结果(2 个以上说「是行人」才算中),减少误判。
效果:精度提升 3%~8%(代价是速度略降,可按需取舍)
优化损失函数
调整「打靶评分规则」
原来的问题:YOLOv8 的损失函数对「小靶打偏」「难识别靶(比如模糊靶)」的惩罚不够,练枪时进步慢。
优化逻辑:重新设计评分规则:
原来只看「靶框和实际位置差多少」(CIoU),现在升级为「EIoU」—— 不仅看位置,还看靶框的长宽是否匹配(比如把「瘦长的行人框」打成「矮胖的框」,惩罚更重);
加「Focal Loss」—— 对难打的靶(小靶、遮挡靶)多练,对容易打的靶(清晰大靶)少练,避免模型「偏科」。
效果:练枪效率更高,难检测的目标(小 / 遮挡 / 模糊)命中率提升。
结合多模态
给枪加「多感官辅助」
原来的问题:YOLO 只靠「视觉(图片)」找靶,比如图片里有「红色车」和「白色车」,想只打「红色车」时,单靠视觉容易漏 / 错;或者图片模糊时,完全认不出靶。
优化逻辑:给模型加「多模态输入」—— 除了图片,还喂文本(比如「找红色的轿车」)、雷达数据、语音等,就像打靶时有人告诉你「靶的特征」,模型能精准锁定目标。
效果:复杂场景(模糊、多同类目标、跨场景)下的检测准确率大幅提升,还能实现「指定类别 / 特征检测」。
具体改进措施
1. 加注意力机制(CBAM,轻量易落地)
CBAM 是最适合 YOLO 的轻量注意力模块(计算量小,不影响速度),加到 YOLOv8 的 C2f 模块里
import torch
import torch.nn as nn
# 第一步:定义CBAM注意力模块(新手不用深究细节,直接用)
class CBAM(nn.Module):
def __init__(self, channels, reduction=16):
super().__init__()
# 通道注意力:关注哪些通道的特征重要(比如「行人通道」比「背景通道」重要)
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.max_pool = nn.AdaptiveMaxPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channels, channels//reduction),
nn.ReLU(),
nn.Linear(channels//reduction, channels)
)
# 空间注意力:关注哪些位置的特征重要(比如行人的躯干位置)
self.spatial = nn.Sequential(
nn.Conv2d(2, 1, 7, padding=3, bias=False),
nn.Sigmoid()
)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
# 通道注意力计算
avg_out = self.fc(self.avg_pool(x).view(x.size(0), -1)).view(x.size(0), -1, 1, 1)
max_out = self.fc(self.max_pool(x).view(x.size(0), -1)).view(x.size(0), -1, 1, 1)
channel_att = self.sigmoid(avg_out + max_out)
x = x * channel_att # 给重要通道加权
# 空间注意力计算
avg_out = torch.mean(x, dim=1, keepdim=True)
max_out, _ = torch.max(x, dim=1, keepdim=True)
spatial_att = self.spatial(torch.cat([avg_out, max_out], dim=1))
x = x * spatial_att # 给重要位置加权
return x
# 第二步:把CBAM加到YOLOv8的C2f模块(核心特征融合模块)
class C2fWithAttention(nn.Module):
def __init__(self, c1, c2, n=1): # c1=输入通道, c2=输出通道, n=模块数
super().__init__()
self.cv1 = nn.Conv2d(c1, c2, 1) # 基础卷积
self.cv2 = nn.Conv2d(c1, c2, 1) # 基础卷积
self.attention = CBAM(c2) # 加入注意力模块
self.relu = nn.ReLU()
def forward(self, x):
x1 = self.cv1(x)
x2 = self.cv2(x)
out = x1 + x2 # 特征融合
out = self.attention(out) # 注意力加权
out = self.relu(out)
return out
# 测试:模拟输入(batch=1, 通道=64, 尺寸=32×32)
x = torch.randn(1, 64, 32, 32)
model = C2fWithAttention(64, 64)
print(model(x).shape) # 输出:torch.Size([1, 64, 32, 32]) → 注意力模块生效
2.做堆叠:模块堆叠(最简单的落地方式)
直接把上面带注意力的 C2f 模块多叠几层,实现「层层特征细化」
# 堆叠3层带注意力的C2f模块
class StackedC2f(nn.Module):
def __init__(self, c1, c2):
super().__init__()
self.layer1 = C2fWithAttention(c1, c2)
self.layer2 = C2fWithAttention(c2, c2)
self.layer3 = C2fWithAttention(c2, c2) # 多叠一层,层层精瞄
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
return x
# 测试
x = torch.randn(1, 64, 32, 32)
stack_model = StackedC2f(64, 64)
print(stack_model(x).shape) # 输出:torch.Size([1, 64, 32, 32]) → 堆叠生效
3. 优化损失函数:EIoU + Focal Loss
替换 YOLOv8 原有的损失函数,重点优化「框回归」和「类别预测」
import torch.nn.functional as F
# 第一步:EIoU损失(比CIoU更关注框的长宽匹配)
def eiou_loss(box_pred, box_true):
# box_pred:预测框 [x1,y1,x2,y2],box_true:真实框 [x1,y1,x2,y2]
# 1. 计算框的中心、长宽
pred_xy = (box_pred[..., :2] + box_pred[..., 2:]) / 2
pred_wh = box_pred[..., 2:] - box_pred[..., :2]
true_xy = (box_true[..., :2] + box_true[..., 2:]) / 2
true_wh = box_true[..., 2:] - box_true[..., :2]
# 2. 计算中心距离、长宽损失
xy_loss = torch.sum((pred_xy - true_xy)**2, dim=-1)
wh_loss = torch.sum((torch.log(pred_wh) - torch.log(true_wh))**2, dim=-1)
# 3. EIoU = 中心损失 + 长宽损失 + 交并比损失(简化版,新手够用)
iou = box_iou(box_pred, box_true) # YOLO自带的IoU计算函数
eiou = 1 - iou + xy_loss + wh_loss
return eiou.mean()
# 第二步:Focal Loss(解决小目标/难样本漏检)
def focal_loss(pred, target, alpha=0.25, gamma=2):
# alpha:平衡正负样本,gamma:惩罚易分样本
ce_loss = F.cross_entropy(pred, target, reduction='none')
pt = torch.exp(-ce_loss) # 预测正确的概率
focal = alpha * (1 - pt) ** gamma * ce_loss
return focal.mean()
# 第三步:组合损失函数(框回归+类别预测)
def custom_loss(box_pred, box_true, cls_pred, cls_true):
box_loss = eiou_loss(box_pred, box_true) # 框回归用EIoU
cls_loss = focal_loss(cls_pred, cls_true) # 类别预测用Focal Loss
total_loss = box_loss + cls_loss # 总损失
return total_loss
# 测试:模拟输入
box_pred = torch.randn(1, 4) # 预测框
box_true = torch.tensor([[10,10,50,50]], dtype=torch.float32) # 真实框
cls_pred = torch.randn(1, 80) # 预测类别(80类是COCO数据集)
cls_true = torch.tensor([0]) # 真实类别(0=行人)
print(custom_loss(box_pred, box_true, cls_pred, cls_true)) # 输出损失值
4 结合多模态:图像 + 文本(最易落地的多模态)
from transformers import CLIPModel, CLIPProcessor
# 第一步:加载CLIP(文本+图像特征提取)
clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
# 第二步:融合CLIP文本特征和YOLO图像特征
class MultiModalYOLO(nn.Module):
def __init__(self, yolo_backbone, num_classes=80):
super().__init__()
self.yolo_backbone = yolo_backbone # YOLOv8的图像特征提取骨干
self.clip_text_proj = nn.Linear(512, 256) # CLIP文本特征降维
self.yolo_img_proj = nn.Linear(256, 256) # YOLO图像特征降维
self.fusion = nn.Conv2d(256*2, 256, 1) # 特征融合
self.head = nn.Conv2d(256, num_classes+4, 1) # 检测头(类别+框)
def forward(self, img, text):
# 1. 提取YOLO图像特征
img_feat = self.yolo_backbone(img) # [1,256,32,32]
# 2. 提取CLIP文本特征(比如「红色轿车」)
text_inputs = clip_processor(text=text, return_tensors="pt", padding=True)
text_feat = clip_model.get_text_features(**text_inputs) # [1,512]
text_feat = self.clip_text_proj(text_feat) # [1,256]
# 3. 文本特征扩维,和图像特征匹配
text_feat = text_feat.unsqueeze(-1).unsqueeze(-1) # [1,256,1,1]
text_feat = text_feat.expand_as(img_feat) # [1,256,32,32]
# 4. 特征融合
img_feat = self.yolo_img_proj(img_feat.permute(0,2,3,1)).permute(0,3,1,2)
fusion_feat = torch.cat([img_feat, text_feat], dim=1) # 拼接
fusion_feat = self.fusion(fusion_feat)
# 5. 检测输出
out = self.head(fusion_feat)
return out
# 测试:模拟输入
yolo_backbone = nn.Conv2d(3, 256, 3, padding=1) # 简化YOLO骨干
mm_yolo = MultiModalYOLO(yolo_backbone)
img = torch.randn(1, 3, 640, 640) # 输入图像
text = ["red car"] # 文本提示:找红色轿车
print(mm_yolo(img, text).shape) # 输出:torch.Size([1, 84, 320, 320]) → 多模态生效
总结:
注意力机制:给 YOLO 加「智能瞄准镜」,聚焦目标核心区域,优先选 CBAM(轻量)加到 C2f 模块;
堆叠:新手优先选「模块堆叠」(多叠几层核心模块),精度提升且易落地,模型堆叠适合追求更高精度的场景;
优化损失函数:用 EIoU 替代 CIoU(框回归更准),加 Focal Loss(解决小目标漏检),训练效率更高;
多模态:新手先试「图像 + 文本」(CLIP+YOLO),实现指定类别检测,适配复杂场景
代码的具体使用方法
YOLOv8 核心目录结构(基础参考)
ultralytics/ # 根目录
├── nn/ # 网络结构核心目录(重点修改区)
│ ├── backbones/ # 骨干网(C2f、CBAM等模块)
│ │ └── __init__.py
│ ├── head.py # 检测头(损失函数、输出层)
│ ├── modules.py # 基础模块(注意力、C2f等)
│ └── tasks.py # 任务封装(多模态可加这里)
├── engine/ # 训练引擎
│ └── trainer.py # 训练逻辑(损失函数调用)
├── models/ # 模型配置
│ └── yolo/
│ ├── detect/
│ │ ├── model.py # 检测模型封装(多模态融合)
│ │ └── loss.py # 损失函数定义(重点)
│ └── __init__.py
└── __init__.py
1.注意力机制(CBAM + C2fWithAttention)
- 存放文件:ultralytics/nn/modules.py
- 具体位置:
打开modules.py,找到C2f类的定义处(约 200-300 行,不同版本行数略有差异);
在C2f类上方添加CBAM类的代码;
在C2f类下方添加C2fWithAttention类的代码;
3 关键修改:
# 在modules.py中新增(示例片段)
class CBAM(nn.Module): # 直接复制之前的CBAM代码
def __init__(self, channels, reduction=16):
super().__init__()
# ... 省略完整代码(和之前一致)
class C2f(nn.Module): # 官方原有C2f类
# ... 官方原有代码
class C2fWithAttention(nn.Module): # 新增带注意力的C2f
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # 对齐官方C2f参数
super().__init__()
self.c = int(c2 * e) # 对齐官方C2f的通道计算
self.cv1 = Conv(c1, 2 * self.c, 1, 1) # 改用官方Conv类(避免报错)
self.cv2 = Conv((2 + n) * self.c, c2, 1) # 对齐官方
self.attention = CBAM(2 * self.c) # 加入注意力
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):
y = list(self.cv1(x).chunk(2, 1))
y.extend(m(y[-1]) for m in self.m)
y = torch.cat(y, 1)
y = self.attention(y) # 注意力加权(核心修改)
return self.cv2(y)
- 生效方式:
修改ultralytics/nn/tasks.py中DetectionModel的__init__,把原有C2f替换为C2fWithAttention即可。
2.堆叠(StackedC2f)
- 存放文件:ultralytics/nn/modules.py
- 具体位置:
在C2fWithAttention类下方添加StackedC2f类的代码; - 关键修改:
# 在modules.py中C2fWithAttention下方新增
class StackedC2f(nn.Module):
def __init__(self, c1, c2, n=3): # n=堆叠层数
super().__init__()
self.layer1 = C2fWithAttention(c1, c2)
self.layer2 = C2fWithAttention(c2, c2)
self.layer3 = C2fWithAttention(c2, c2)
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
return x
- 生效方式:
在tasks.py中把需要堆叠的C2f模块替换为StackedC2f(比如 Neck 部分的 C2f)
3.优化损失函数(EIoU + Focal Loss)
-
存放文件:ultralytics/models/yolo/detect/loss.py
-
具体位置:
打开loss.py,找到DetectionLoss类(官方损失类);
在DetectionLoss类内部新增eiou_loss和focal_loss方法;
重写__call__方法(损失计算核心); -
关键修改:
# 在loss.py的DetectionLoss类中修改
class DetectionLoss(nn.Module):
def __init__(self, model):
super().__init__()
# ... 官方原有初始化代码
# 新增EIoU损失方法
def eiou_loss(self, box_pred, box_true):
# 复制之前的eiou_loss代码,注意适配官方张量格式
pred_xy = (box_pred[..., :2] + box_pred[..., 2:]) / 2
pred_wh = box_pred[..., 2:] - box_pred[..., :2]
true_xy = (box_true[..., :2] + box_true[..., 2:]) / 2
true_wh = box_true[..., 2:] - box_true[..., :2]
xy_loss = torch.sum((pred_xy - true_xy)**2, dim=-1)
wh_loss = torch.sum((torch.log(pred_wh + 1e-8) - torch.log(true_wh + 1e-8))**2, dim=-1) # 加1e-8防除0
iou = self.bbox_iou(box_pred, box_true, CIoU=False) # 调用官方IoU函数
eiou = 1 - iou + xy_loss + wh_loss
return eiou.mean()
# 新增Focal Loss方法
def focal_loss(self, pred, target, alpha=0.25, gamma=2):
ce_loss = F.cross_entropy(pred, target, reduction='none')
pt = torch.exp(-ce_loss)
focal = alpha * (1 - pt) ** gamma * ce_loss
return focal.mean()
# 重写损失计算核心方法
def __call__(self, preds, batch):
# ... 省略官方原有前处理代码(获取box_pred, box_true, cls_pred, cls_true)
# 替换原有损失计算
box_loss = self.eiou_loss(box_pred, box_true) # 用EIoU替代CIoU
cls_loss = self.focal_loss(cls_pred, cls_true) # 用Focal Loss替代普通CE
total_loss = box_loss + cls_loss * self.hyp['cls'] # 对齐官方权重
return total_loss * batch['batch_size'], torch.cat((box_loss, cls_loss)).detach()
4.结合多模态(CLIP + YOLO)
- 核心文件 1:ultralytics/nn/modules.py(多模态融合模块)
在modules.py末尾新增MultiModalFusion类:
class MultiModalFusion(nn.Module):
def __init__(self, img_dim=256, text_dim=512, out_dim=256):
super().__init__()
self.img_proj = nn.Linear(img_dim, out_dim)
self.text_proj = nn.Linear(text_dim, out_dim)
self.fusion_conv = nn.Conv2d(out_dim*2, out_dim, 1)
# 加载CLIP(仅初始化一次)
self.clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
self.clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
# 冻结CLIP(避免训练时更新)
for param in self.clip_model.parameters():
param.requires_grad = False
def get_text_feat(self, text):
text_inputs = self.clip_processor(text=text, return_tensors="pt", padding=True)
text_feat = self.clip_model.get_text_features(**text_inputs)
return self.text_proj(text_feat)
def forward(self, img_feat, text):
# img_feat: [B, C, H, W](YOLO图像特征)
# text: list[str](文本提示,如["red car"])
text_feat = self.get_text_feat(text) # [B, out_dim]
# 扩维匹配图像特征
text_feat = text_feat.unsqueeze(-1).unsqueeze(-1).expand_as(img_feat)
# 图像特征投影
img_feat = self.img_proj(img_feat.permute(0,2,3,1)).permute(0,3,1,2)
# 融合
fusion_feat = torch.cat([img_feat, text_feat], dim=1)
return self.fusion_conv(fusion_feat)
- 核心文件 2:ultralytics/models/yolo/detect/model.py(多模态模型封装)
打开model.py,找到DetectionModel类,修改forward方法:
class DetectionModel(BaseModel):
def __init__(self, cfg='yolov8n.yaml', ch=3, nc=None, verbose=True):
super().__init__()
# 新增多模态融合模块
self.multi_modal_fusion = MultiModalFusion()
def forward(self, x, text=None, *args, **kwargs):
# x: 图像输入, text: 文本提示(新增)
img_feat = self.backbone(x) # 提取YOLO图像特征
if text is not None:
img_feat = self.multi_modal_fusion(img_feat, text) # 多模态融合
out = self.neck(img_feat)
out = self.head(out)
return out
- 辅助文件:ultralytics/engine/trainer.py(训练时传入文本)
在trainer.py的train_step方法中,新增文本输入的传递逻辑(比如从数据集读取文本标签)
更多推荐
所有评论(0)