yolov12:YOLOv12: 以注意力为中心的实时目标检测器 - GitCode --- yolov12:YOLOv12: Attention-Centric Real-Time Object Detectors - GitCode

一、环境

环境配置

YOLOv12保姆级教程(win系统和ubuntu系统均可使用)_yolov12代码-CSDN博客

git clone 代码

v12/yolo12n-seg.yaml的下载(也可以不写)

ultralytics/ultralytics/cfg/models/12 at main · ultralytics/ultralytics

权重下载: 此处直接选择权重文件【YOLO12n-seg】下载即可

wget https://github.com/Dao-AILab/flash-attention/releases/download/v2.7.3/flash_attn-2.7.3+cu11torch2.2cxx11abiFALSE-cp311-cp311-linux_x86_64.whl

conda create -n yolov12 python=3.11

conda activate yolov12

在pycharm中添加解释器

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

----------安装requirements.txt文件,后面加清华的镜像源效果更好。

pip install -e .

先检查下torch的版本以及cuda是否可用

搭建YOLOv8实现裂缝缺陷识别全流程教程:从源码下载到模型测试_python 3.12.3 yolo8-CSDN博客

根目录创建一个py文件,名为cuda检查.py,放置如下代码并且执行:

import torch
import numpy as np
 
# 检查GPU、Cuda
 
print("numpy版本:"+np.__version__)
print("CUDA是否可用=>", torch.cuda.is_available())
print("pytorch版本=>"+torch.__version__)
print("是否支持CUDA=>"+str(torch.cuda.is_available()))
# 如果有 GPU 可用,打印 GPU 数量和名称
if torch.cuda.is_available():
    print("GPU设备数量=>", torch.cuda.device_count())
    print("当前使用GPU=>", torch.cuda.get_device_name(torch.cuda.current_device()))
else:
    print("没有GPU可用,当前将运行在CPU上")

如果没问题,会显示如下

如果你的pytorch显示cpu版本的,且numpy提示报错了,看博客解决

二、数据集

【免费下载】 YOLO地面裂缝数据集-CSDN博客

搭建YOLOv8实现裂缝缺陷识别全流程教程:从源码下载到模型测试_python 3.12.3 yolo8-CSDN博客

我选择第二个链接的数据集,下载splitata,换成下图的结构

想将 project_old目录移动到 /home/username/workspace/目录下,并重命名为 project_new

mv project_old  /home/username/workspace/project_new

mv /home/你的用户名/package.tar.gz /mnt/d/Downloads/

mv /home/你的用户名/data/* /mnt/c/Users/QQ342/Downloads(复制,不会带data目录)

复制文件夹下的文件到另一个文件夹

 cp -r datasets/crack-seg/images/train/* datasets/crack-seg-721/total-image

/yolov12/datasets$     tar -zcvf crack-seg-721_split.tar.gz crack-seg-721_split

删除windows下以 "noncrack" 开头的 .jpg 图片

ls -r "C:\Users\QQ342\Downloads\crack_segmentation_dataset\masks\noncrack*.jpg"

Remove-Item -Path "C:\Users\QQ342\Downloads\crack_segmentation_dataset\masks\noncrack*.jpg"

压缩这个目录本身以及它包含的所有内容

 tar -cvzf crack_segmentation_dataset.tar.gz /home/qsl/3dgs-code/yolov12/datasets/crack_segmentation_dataset

解压

tar -xzvf crack_segmentation_dataset.tar.gz


查看生成的 .tar.gz文件内部包含了哪些文件和路径结构

tar -tzvf crack_segmentation_dataset.tar.gz

压缩文件夹(排除一些文件)

# 在 yolov12 目录下执行
tar -zcvf splitData_3.tar.gz --exclude='splitData(3)/box_labels' --exclude='splitData(3)/splitData' --exclude='splitData(3)/crack_segmentation.yaml' 'splitData(3)'

mv splitData_3.tar.gz /mnt/c/Users/QQ342/Downloads/

crack_segmentation.yaml

# 数据集路径
path: /home/qsl/3dgs-code/yolov12/splitData(3)  # 数据集根目录
train: images/train    # 训练集图像相对路径
val: images/val        # 验证集图像相对路径
test: images/test      # 测试集图像相对路径 (可选)

# 类别信息
names:
  0: crack  # 类别名称,索引从0开始

制作数据集

YOLOV9保姆级教程-CSDN博客

YOLOv5图像识别教程:成功识别桥墩缺陷详细步骤分享

一篇文章快速认识 YOLO11 | 实例分割 | 模型训练 | 自定义数据集_yolo11实例分割-CSDN博客

修改输出标注信息的路径是和images同级的labels文件夹。最好取消同时保存图像数据按钮,并点击自动保存按钮。

三、运行

train.py

from ultralytics import YOLO


if __name__ == '__main__':

    model = YOLO('/home/qsl/3dgs-code/yolov12/ultralytics/cfg/models/v12/yolov12n-seg.yaml')
    # 代表使用yolov12n的神经网络结构
    model.load('/home/qsl/3dgs-code/yolov12/yolov12n-seg.pt')
    # 代表使用yolov12n的预训练权重
    model.train(data='/home/qsl/3dgs-code/yolov12/splitData(3)/splitData/crack_segmentation.yaml', # 数据集yaml路径
                imgsz=640, # 输入图像的尺寸为 640x640 像素
                epochs=200, # 训练300轮
                batch=16, # 每一批样本的数量
                workers=8, # 同时32个线程
                device="0", #只使用第一张显卡进行训练
                project='/home/qsl/3dgs-code/yolov12/train',
                name='yolov12n',
                )

 结果分析参考一条视频讲清楚yolo训练结果的含义 - Coding茶水间 - 博客园 - 愿你无忧yyyyyyy - 博客园

cmd打开终端,在终端输入nvidia-smi,回车执行命令就能看到GPU利用率66%:

如果电脑不太好的话,会出现GPU容量不足的情况,尽量调整数据输入量和线程,减少电脑负担,在train文件里调小workers、batch

报错Could not load library libcudnn_cnn_train.so.8

您遇到的错误,核心问题在于环境中的CUDA和cuDNN库版本存在冲突。从您提供的版本信息来看,PyTorch期望使用CUDA 12.1,但系统却错误地加载了来自CUDA 11.8路径下的旧版cuDNN库。

echo $LD_LIBRARY_PATH

echo $CUDA_HOME

  • conda activate yolov12
  • unset CUDA_HOME
  • export LD_LIBRARY_PATH=

更稳妥的做法(每次激活环境自动生效)(可以试试):

  • mkdir -p $CONDA_PREFIX/etc/conda/activate.d
  • 创建文件 vim  $CONDA_PREFIX/etc/conda/activate.d/cuda_clean.sh,内容:
    • unset CUDA_HOME
    • export LD_LIBRARY_PATH=

验证:

  • python -c "import torch, os; print(torch.version, torch.version.cuda); print('CUDA_HOME=', os.environ.get('CUDA_HOME')); print('LD_LIBRARY_PATH=', os.environ.get('LD_LIBRARY_PATH'))"

predict.py

from ultralytics import YOLO
from PIL import Image
import cv2


model = YOLO('train/yolov12n5/weights/best.pt')
results = model.predict(source='splitData(3)/images/test/1-59-_jpg.rf.88757267d8830b549d4b2eadb5febba9.jpg', save=True,save_txt=True, imgsz=640, conf=0.25)#单张图片,source也可以是文件夹

# 结果可视化
for r in results:

    # 方法1: 使用Ultralytics内置的plot方法 (快速显示)
    im_array = r.plot()  # 绘制边界框、分割掩码、标签
    im = Image.fromarray(im_array[..., ::-1])  # BGR to RGB
    cv2.imshow('result', im_array) # 显示图像
    # 等待按键,0表示无限等待直到按键
    key = cv2.waitKey(0)  # 程序会停在这里等待用户按键
    # 关闭所有OpenCV窗口
    cv2.destroyAllWindows()

控制台提示识别的结果在runs\detect\predict目录下,发现图中出现多个裂缝时不能准确识别

【图像算法 - 14】精准识别路面墙体裂缝:基于YOLO12与OpenCV的实例分割智能检测实战(附完整代码)_crack500数据集宽度测量-CSDN博客

app.py

可选多种模型,但是这是没有经过裂缝数据集训练的权重,直接传入图片的话,输出的图片并不会标注,所以经过修改app.py做了一个可以上传pt权重文件的程序,代码如下demo.py:

demo.py(可上传pt权重文件)

# --------------------------------------------------------
# Based on yolov10
# https://github.com/THU-MIG/yolov10/app.py
# --------------------------------------------------------'

import gradio as gr
import cv2
import tempfile
from ultralytics import YOLO


def yolov12_inference(image, video, model_path, image_size, conf_threshold):
    print("model_path: ", model_path)
    model = YOLO(model_path)
    if image:
        results = model.predict(source=image, imgsz=image_size, conf=conf_threshold)
        annotated_image = results[0].plot()
        return annotated_image[:, :, ::-1], None
    else:
        video_path = tempfile.mktemp(suffix=".webm")
        with open(video_path, "wb") as f:
            with open(video, "rb") as g:
                f.write(g.read())

        cap = cv2.VideoCapture(video_path)
        fps = cap.get(cv2.CAP_PROP_FPS)
        frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

        output_video_path = tempfile.mktemp(suffix=".webm")
        out = cv2.VideoWriter(output_video_path, cv2.VideoWriter_fourcc(*'vp80'), fps, (frame_width, frame_height))

        while cap.isOpened():
            ret, frame = cap.read()
            if not ret:
                break

            results = model.predict(source=frame, imgsz=image_size, conf=conf_threshold)
            annotated_frame = results[0].plot()
            out.write(annotated_frame)

        cap.release()
        out.release()

        return None, output_video_path


def yolov12_inference_for_examples(image, model_path, image_size, conf_threshold):
    annotated_image, _ = yolov12_inference(image, None, model_path, image_size, conf_threshold)
    return annotated_image


def app():
    with gr.Blocks():
        with gr.Row():
            with gr.Column():
                image = gr.Image(type="pil", label="Image", visible=True)
                video = gr.Video(label="Video", visible=False)
                input_type = gr.Radio(
                    choices=["Image", "Video"],
                    value="Image",
                    label="Input Type",
                )
                model_file = gr.File(label="Upload Model (.pt file)", file_types=[".pt"])
                image_size = gr.Slider(
                    label="Image Size",
                    minimum=320,
                    maximum=1280,
                    step=32,
                    value=640,
                )
                conf_threshold = gr.Slider(
                    label="Confidence Threshold",
                    minimum=0.0,
                    maximum=1.0,
                    step=0.05,
                    value=0.25,
                )
                yolov12_infer = gr.Button(value="Detect Objects")

            with gr.Column():
                output_image = gr.Image(type="numpy", label="Annotated Image", visible=True)
                output_video = gr.Video(label="Annotated Video", visible=False)

        def update_visibility(input_type):
            image = gr.update(visible=True) if input_type == "Image" else gr.update(visible=False)
            video = gr.update(visible=False) if input_type == "Image" else gr.update(visible=True)
            output_image = gr.update(visible=True) if input_type == "Image" else gr.update(visible=False)
            output_video = gr.update(visible=False) if input_type == "Image" else gr.update(visible=True)

            return image, video, output_image, output_video

        input_type.change(
            fn=update_visibility,
            inputs=[input_type],
            outputs=[image, video, output_image, output_video],
        )

        def run_inference(image, video, model_file, image_size, conf_threshold, input_type):
            if input_type == "Image":
                return yolov12_inference(image, None, model_file, image_size, conf_threshold)
            else:
                return yolov12_inference(None, video, model_file, image_size, conf_threshold)


        yolov12_infer.click(
            fn=run_inference,
            inputs=[image, video, model_file, image_size, conf_threshold, input_type],
            outputs=[output_image, output_video],
        )

        # gr.Examples(
        #     examples=[
        #         [
        #             "ultralytics/assets/bus.jpg",
        #             "yolov12s.pt",
        #             640,
        #             0.25,
        #         ],
        #         [
        #             "ultralytics/assets/zidane.jpg",
        #             "yolov12x.pt",
        #             640,
        #             0.25,
        #         ],
        #     ],
        #     fn=yolov12_inference_for_examples,
        #     inputs=[
        #         image,
        #         model_id,
        #         image_size,
        #         conf_threshold,
        #     ],
        #     outputs=[output_image],
        #     cache_examples='lazy',
        # )

gradio_app = gr.Blocks()
with gradio_app:
    gr.HTML(
        """
    <h1 style='text-align: center'>
    YOLOv12: Attention-Centric Real-Time Object Detectors
    </h1>
    """)
    gr.HTML(
        """
        <h3 style='text-align: center'>
        <a href='https://arxiv.org/abs/2502.12524' target='_blank'>arXiv</a> | <a href='https://github.com/sunsmarterjie/yolov12' target='_blank'>github</a>
        </h3>
        """)
    with gr.Row():
        with gr.Column():
            app()
if __name__ == '__main__':
    gradio_app.launch()

四、autodl部署

可尝试参考

YOLOv12快速复现部署&训练测试 - 知乎

01 YOLOv12快速复现部署&训练测试_哔哩哔哩_bilibili

选择gpu

马上关机,无卡模式开机

安装xshell

家庭/学校免费 – NetSarang Website

XShell初始化界面

参考XShell免费版的安装配置教程以及使用教程(超级详细、保姆级)-CSDN博客

安装xftp

可以先传数据集

连接pycharm

https://blog.csdn.net/weixin_45325693/article/details/130914217?fromshare=blogdetail&sharetype=blogdetail&sharerId=130914217&sharerefer=PC&sharesource=weixin_58247017&sharefrom=from_link

https://blog.csdn.net/m0_66418976/article/details/145999949?fromshare=blogdetail&sharetype=blogdetail&sharerId=145999949&sharerefer=PC&sharesource=weixin_58247017&sharefrom=from_link

【【手把手带你实战YOLOv5-拓展篇】Pycharm基本使用与AutoDL服务器连接】https://www.bilibili.com/video/BV1Ns4y1p7Ry?vd_source=f20f48824daff7c1a90e1d79ca71e468

 先在服务器终端安装环境

使用PyCharm连接AutoDL远程服务器 - 知乎

source /etc/network_turbo
git clone https://github.com/sunsmarterjie/yolov12.git
cd yolov12/
wget https://github.com/Dao-AILab/flash-attention/releases/download/v2.7.3/flash_attn-2.7.3+cu11torch2.2cxx11abiFALSE-cp311-cp311-linux_x86_64.whl
conda create -n yolov12 python=3.11


报错:conda 在读取清华镜像的 repodata.json 失败,指向的 URL 为 pkgs/free/noarch

RuntimeError: Unable to read repodata JSON file 'https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/noarch', error was: repository does not start with an object

`$ /root/miniconda3/bin/conda create -n yolov12 python=3.11`

conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda clean -i -y
conda create -n yolov12 python=3.11 -y
conda init 
source ~/.bashrc
conda activate yolov12

pycharm配置

这一步可以不配,当做检验

输入ssh配置

大家打开PyCharm专业版(一定是专业版!!!)

 大家点击第二步的时候点击:add...这个选项

这里的登陆指令复制到上一张图的内容中:

这种形式的:ssh -p xxxx root@xxxxx.com

Host:root@xxxxx.com

Port:我们只要ssh -p后面的数字xxxx

Username:root

接下来会有一个输入密码password,就是上一张图的密码
参考https://blog.csdn.net/weixin_45325693/article/details/130914217?fromshare=blogdetail&sharetype=blogdetail&sharerId=130914217&sharerefer=PC&sharesource=weixin_58247017&sharefrom=from_link

路径选择,一般是root/miniconda3/envs/yolov12/bin/python3.11,也可以查询whereis python

不创建虚拟环境直接用服务器的base环境的话就是/root/miniconda3/bin/python

修改项目或者数据集上传到服务器的路径(可以新建)

一般传在(autodl-tmp-(自己的创建存放代码的文件夹))

这就链接成功了,可以在pycharm开启中断继续(当然也可以在JupyterLab终端上继续)

pip install -r requirements.txt
pip install -e .

上传权重以及数据集,数据yaml文件

注意修改数据集的yaml文件以及train.py文件(!!!!路径)

# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: /root/autodl-tmp/project/datasets/deep_crack # dataset root dir
train: images/train # train images (relative to 'path') 3717 images
val: images/val # val images (relative to 'path') 112 images
test: images/test # test images (relative to 'path') 200 images

# Classes
names:
  0: crack

from ultralytics import YOLO


if __name__ == '__main__':

    model = YOLO('/root/autodl-tmp/project/yolov12/ultralytics/cfg/models/v12/yolo12n-seg.yaml')
    # 代表使用yolov12n的神经网络结构
    model.load('/root/autodl-tmp/project/yolov12/yolov12n-seg.pt')
    # 代表使用yolov12n的预训练权重
    model.train(data='/root/autodl-tmp/project/datasets/deep_crack/crack-seg.yaml', # 数据集yaml路径
                imgsz=640, # 输入图像的尺寸为 640x640 像素
                epochs=200, # 训练300轮
                batch=16, # 每一批样本的数量
                workers=8, # 同时32个线程
                #copy_paste=0.3,
                #patience=50,
                device="0", #只使用第一张显卡进行训练
                project='/root/autodl-tmp/project/yolov12/train_seg_n',
                name='yolov12n-deep_crack',
                )

关机,重新开机

注意:修改服务器的文件后,pycharm可能不能及时同步导致训练错误,保存服务器上修改的文件,在pycharm端刷新即可

screen
conda activate yolov12
cd autodl-tmp/project/yolov12/
python train.py && /usr/bin/shutdown

.修改screen的配置文件,添加滚动条

vim ~/.screenrc
termcapinfo xterm* ti@:te@

当我在AutoDL(SSH)跑模型时,如何用screen来守护进程,防止因断网导致程序终止_screen守护进程-CSDN博客

程序出现变化会自动上传

拉取服务器结果,上面的浏览远程主机

新建py文件不想在服务器运行(修改python解释器)

五、代码

ultralytics/models/yolo/model.py

ultralytics/models/yolo/segment/train.py

# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license

from copy import copy

from ultralytics.models import yolo
from ultralytics.nn.tasks import SegmentationModel
from ultralytics.utils import DEFAULT_CFG, RANK
from ultralytics.utils.plotting import plot_images, plot_results


class SegmentationTrainer(yolo.detect.DetectionTrainer):
    """
    A class extending the DetectionTrainer class for training based on a segmentation model.

    Example:
        ```python
        from ultralytics.models.yolo.segment import SegmentationTrainer

        args = dict(model="yolov8n-seg.pt", data="coco8-seg.yaml", epochs=3)
        trainer = SegmentationTrainer(overrides=args)
        trainer.train()
        ```
    """

    def __init__(self, cfg=DEFAULT_CFG, overrides=None, _callbacks=None):
        """Initialize a SegmentationTrainer object with given arguments."""
        if overrides is None:
            overrides = {}
        overrides["task"] = "segment"
        super().__init__(cfg, overrides, _callbacks)

    def get_model(self, cfg=None, weights=None, verbose=True):
        """Return SegmentationModel initialized with specified config and weights."""
        model = SegmentationModel(cfg, ch=3, nc=self.data["nc"], verbose=verbose and RANK == -1)
        if weights:
            model.load(weights)

        return model

    def get_validator(self):
        """Return an instance of SegmentationValidator for validation of YOLO model."""
        self.loss_names = "box_loss", "seg_loss", "cls_loss", "dfl_loss"
        return yolo.segment.SegmentationValidator(
            self.test_loader, save_dir=self.save_dir, args=copy(self.args), _callbacks=self.callbacks
        )

    def plot_training_samples(self, batch, ni):
        """Creates a plot of training sample images with labels and box coordinates."""
        plot_images(
            batch["img"],
            batch["batch_idx"],
            batch["cls"].squeeze(-1),
            batch["bboxes"],
            masks=batch["masks"],
            paths=batch["im_file"],
            fname=self.save_dir / f"train_batch{ni}.jpg",
            on_plot=self.on_plot,
        )

    def plot_metrics(self):
        """Plots training/val metrics."""
        plot_results(file=self.csv, segment=True, on_plot=self.on_plot)  # save results.png

讲了一些BaseTrainer

【25k字】图文解读YOLOv8及实例分割(附python代码)_yolov8实例分割-CSDN博客

loss

ultralytics/nn/tasks.py

class SegmentationModel(DetectionModel):
    """YOLOv8 segmentation model."""

    def __init__(self, cfg="yolov8n-seg.yaml", ch=3, nc=None, verbose=True):
        """Initialize YOLOv8 segmentation model with given config and parameters."""
        super().__init__(cfg=cfg, ch=ch, nc=nc, verbose=verbose)

    def init_criterion(self):
        """Initialize the loss criterion for the SegmentationModel."""
        return v8SegmentationLoss(self)

ultralytics/engine/trainer.py中loss,self.model(batch) 调用模型的forward方法

这里的model是SegmentationTrainer 调用的get_model,就是SegmentationModel

(ultralytics/models/yolo/segment/train.py)

loss函数中preds

当创建YOLO实例并选择segment任务时,在yolo/model.py中task_map,segment对应SegmentationModel。然后DetectionModel的初始化parse_model函数会加载yaml配置并解析模型结构(包含backbone(特征提取)、neck(特征融合)和head(分割头)三个主要部分)

训练模式下,调用loss

1.损失函数初始化

2.获取预测结果

ultralytics/nn/tasks.py

ultralytics/nn/modules/head.py Segment的forward

3.计算loss

loss在ultralytics/utils/loss.py中的v8SegmentationLoss类

损失包括:

  • 分割相关的损失seg
  • 继承了目标检测任务的分类损失cls,和矩形框回归损失(iou loss,DFL loss)

loss对应的权重,在ultralytics/cfg/default.yaml中

分割损失计算单张图像的损失再求和平均

    def single_mask_loss(
        gt_mask: torch.Tensor, pred: torch.Tensor, proto: torch.Tensor, xyxy: torch.Tensor, area: torch.Tensor
    ) -> torch.Tensor:
        """
        Compute the instance segmentation loss for a single image.
        计算单张图像的掩码损失

        Args:
            gt_mask (torch.Tensor): Ground truth mask of shape (n, H, W), where n is the number of objects.
            pred (torch.Tensor): Predicted mask coefficients of shape (n, 32).
            proto (torch.Tensor): Prototype masks of shape (32, H, W).
            xyxy (torch.Tensor): Ground truth bounding boxes in xyxy format, normalized to [0, 1], of shape (n, 4).
            area (torch.Tensor): Area of each ground truth bounding box of shape (n,).

        Returns:
            (torch.Tensor): The calculated mask loss for a single image.

        Notes:
            The function uses the equation pred_mask = torch.einsum('in,nhw->ihw', pred, proto) to produce the
            predicted masks from the prototype masks and predicted mask coefficients.
        """
        pred_mask = torch.einsum("in,nhw->ihw", pred, proto)  # (n, 32) @ (32, 80, 80) -> (n, 80, 80)
        loss = F.binary_cross_entropy_with_logits(pred_mask, gt_mask, reduction="none")
        return (crop_mask(loss, xyxy).mean(dim=(1, 2)) / area).sum()

【YOLOv8 代码解读】所有任务Loss损失代码梳理_dfl loss-CSDN博客

best.pt选取

yolov8 选择并保存best.pt的依据 (附带修改记录,) + 设置早停-CSDN博客

在 ultralytics/engine/trainer.py 中,选取 best.pt 的逻辑主要在以下几个地方:

  1. validate():返回 metrics 和 fitness,(metrics返回一个字典,各种指标以及fitness)并更新 self.best_fitness(当当前 fitness 更好时)。
  2. 训练主流程(\_do_train):在每个 epoch 验证后调用 self.metrics, self.fitness = self.validate(),随后会调用保存相关逻辑。
  3. save_model():把当前 checkpoint 写入 last.pt,并在 self.best_fitness == self.fitness 时写入 best.pt。(只有权重出现更好的时才会存入)
  4. resume_training():从 checkpoint 恢复时会读入并设置 self.best_fitness,以保证继续训练时能正确比较。
  5. final_eval():在训练结束时会加载并验证 self.best(即 best.pt)。

总结:选 best.pt 是通过 validate() 计算并更新 self.best_fitness,再由 save_model() 在满足条件时写出 best.pt。


# 若需调整权重,编辑 Metric.fitness 中的 w 列表(例如提高 mask 权重或改变 mAP 权重)

self.metrics, self.fitness = self.validate()

调用 self.validator(self) 会触发其 __call__(BaseValidator 中的调用流程)

其中一些调用SegmentValidator中的方法

ultralytics/utils/metrics.py

指标解释

ultralytics/utils/metrics.py

不同IoU阈值的意义:较低的IoU阈值(如0.5)更关注检测是否成功("框到了"),而较高的阈值(如0.95)则严格要求定位必须极为精确

Attributes:
    p (list): Precision for each class. Shape: (nc,).
    r (list): Recall for each class. Shape: (nc,).
    f1 (list): F1 score for each class. Shape: (nc,).
    all_ap (list): AP scores for all classes and all IoU thresholds. 
Shape: (nc, 10).
存储所有类别在不同IoU阈值([0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95])下的平均精度得分
    ap_class_index (list): Index of class for each AP score. Shape: (nc,).
    nc (int): Number of classes.

Methods:
    ap50(): AP at IoU threshold of 0.5 for all classes. Returns: List of AP scores. Shape: (nc,) or [].存储所有类别在IoU阈值为0.5下的平均精度得分(就是all_ap的第一列)
    ap(): AP at IoU thresholds from 0.5 to 0.95 for all classes. Returns: List of AP scores. Shape: (nc,) or [].#对每个类别在所有IoU阈值上取平均,
(AP@0.5 + AP@0.55 + ... + AP@0.95) / 10
    mp(): Mean precision of all classes. Returns: Float.
    mr(): Mean recall of all classes. Returns: Float.
    map50(): Mean AP at IoU threshold of 0.5 for all classes. Returns: Float.
ap50().mean(就是all_ap的第一列的平均数)
    map75(): Mean AP at IoU threshold of 0.75 for all classes. Returns: Float.
(就是all_ap的第六列的平均数)
    map(): Mean AP at IoU thresholds from 0.5 to 0.95 for all classes. Returns: Float.(就是ap()/类别数)
    mean_results(): Mean of results, returns mp, mr, map50, map.
    class_result(i): Class-aware result, returns p[i], r[i], ap50[i], ap[i].
    maps(): mAP of each class. Returns: Array of mAP scores, shape: (nc,).
    fitness(): Model fitness as a weighted combination of metrics. Returns: Float.
    update(results): Update metric attributes with new evaluation results.

计算ap

先计算tp

像loss中计算preds一样,使用推理模式

模型输出:每个预测框有一个置信度分数(如0.92、0.85等)。
排序:将所有预测框按置信度从高到低排序。

postprocess 方法调用 ops.non_max_suppression,NMS 默认会按置信度降序排列输出的预测框


后续匹配:用排序后的预测依次与所有真实框做IoU匹配(设定IoU阈值(如0.5),IoU大于阈值,),能则tp=1,否则tp=0。输出是(检测框的数目,10)

每个真实框最多只能被一个预测框匹配(通常优先分配给置信度最高的预测)

先处理preds,

  1. predn边界框缩放:DetectionValidator的_prepare_pred方法将预测框从模型输出尺寸缩放到原始图像尺寸
  2. pred_masks掩码生成:使用掩码原型和掩码系数(pred中),通过矩阵乘法生成初始掩码,再根据边界框裁剪得到最终掩码


然后计算ap

ultralytics/engine/validator.py

在每一类中,tpc,fpc,p,r的维度都是(n,10),n为检测框数量

  1. 动态模拟:通过 cumsum,我们模拟了置信度阈值从高到低变化时,TP和FP的累积过程,得到了一系列 (Recall, Precision)数据点。

  2. 插值:由于这些原始数据点的置信度阈值分布不均匀,直接绘图会不平滑。通过 np.interp将它们映射到1000个均匀分布的召回率点上,生成平滑的曲线。

    置信度为横坐标(横坐标递增)

  3. 绘图与计算:利用插值后得到的 p_curver_curve(均为 (nc, 1000)维度的数组),可以绘制出平滑的PR曲线,积分得到AP(Average Precision)。
    每个iou都有一个pr图,所以每一类有iou个ap



    update得到all_ap

iou和conf

两个iou的解释

1. NMS中IoU阈值的使用

配置文件default.yaml中的iou: 0.7设置用于**非极大值抑制(NMS)**过程:

  • 调用位置:在ultralytics/models/yolo/segment/val.py第73行的postprocess方法中
  • 使用方式ops.non_max_suppression(preds[0], self.args.conf, self.args.iou, ...)
  • 作用当两个预测框的IoU大于0.7时,保留置信度更高的框,过滤重叠的低置信度框
  • 参数来源:配置文件中的iou值通过命令行参数或配置文件加载,存储在self.args.iou

2. AP计算中的多个IoU阈值

在AP计算中使用了10个IoU阈值,这与NMS的IoU阈值是不同的概念:

  • 阈值范围:0.5到0.95,步长0.05(共10个阈值)
  • 调用位置:在ultralytics/utils/metrics.pyap_per_class函数中
  • 作用:计算不同IoU严格程度下的平均精度(AP),用于评估模型在不同检测严格度下的性能
  • 结果应用
    • mAP50:IoU=0.5时的平均精度
    • mAP50-95:10个IoU阈值下AP的平均值

3. 两者的关系与区别

  • 阶段不同
    • NMS的IoU阈值用于推理阶段,过滤重叠预测框
    • AP计算的IoU阈值用于评估阶段,衡量模型性能
  • 作用不同
    • NMS阈值影响最终输出的预测框数量和质量
    • AP阈值影响模型性能指标的计算
  • 没有冲突:两者是独立的参数,用于不同的目的,不存在冲突

`conf`参数控制了预测边界框的置信度阈值。默认情况下,只有当预测边界框的置信度高于该阈值时,才会被认为是有效的目标边界框。如果设置较高的`conf`阈值,则会筛除一些低置信度的边界框,从而提高准确性。但是,这也可能导致漏检(即未能检测到真实目标)。相反,如果设置较低的`conf`阈值,则更多的边界框会被保留下来,但其中可能包含一些误检(即将背景等非目标识别为目标)。

请问深度学习yolov8测试验证的时候,参数conf和iou必须用默认值吗,可以自己调让指标升高点吗? - 知乎

mask抠图

使用yolo-seg模型实现自定义自动动态抠图_抠图模型-CSDN博客

其他

YOLOv8-seg的mask mAP50和mAP95在何处计算?_编程语言-CSDN问答

突破YOLO分割预测异常:从掩码缺失到边界模糊的全流程优化指南-CSDN博客

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐