Bỏ để qua phần nội dung

Tài liệu tham khảo cho ultralytics/models/yolo/detect/val.py

Ghi

Tệp này có sẵn tại https://github.com/ultralytics/ultralytics/blob/main/ultralytics/Mô hình/yolo/phát hiện/val.py. Nếu bạn phát hiện ra một vấn đề, vui lòng giúp khắc phục nó bằng cách đóng góp Yêu cầu 🛠️ kéo. Cảm ơn bạn 🙏 !



ultralytics.models.yolo.detect.val.DetectionValidator

Căn cứ: BaseValidator

Một lớp mở rộng lớp BaseValidator để xác thực dựa trên mô hình phát hiện.

Ví dụ
from ultralytics.models.yolo.detect import DetectionValidator

args = dict(model='yolov8n.pt', data='coco8.yaml')
validator = DetectionValidator(args=args)
validator()
Mã nguồn trong ultralytics/models/yolo/detect/val.py
class DetectionValidator(BaseValidator):
    """
    A class extending the BaseValidator class for validation based on a detection model.

    Example:
        ```python
        from ultralytics.models.yolo.detect import DetectionValidator

        args = dict(model='yolov8n.pt', data='coco8.yaml')
        validator = DetectionValidator(args=args)
        validator()
        ```
    """

    def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
        """Initialize detection model with necessary variables and settings."""
        super().__init__(dataloader, save_dir, pbar, args, _callbacks)
        self.nt_per_class = None
        self.nt_per_image = None
        self.is_coco = False
        self.is_lvis = False
        self.class_map = None
        self.args.task = "detect"
        self.metrics = DetMetrics(save_dir=self.save_dir, on_plot=self.on_plot)
        self.iouv = torch.linspace(0.5, 0.95, 10)  # IoU vector for mAP@0.5:0.95
        self.niou = self.iouv.numel()
        self.lb = []  # for autolabelling

    def preprocess(self, batch):
        """Preprocesses batch of images for YOLO training."""
        batch["img"] = batch["img"].to(self.device, non_blocking=True)
        batch["img"] = (batch["img"].half() if self.args.half else batch["img"].float()) / 255
        for k in ["batch_idx", "cls", "bboxes"]:
            batch[k] = batch[k].to(self.device)

        if self.args.save_hybrid:
            height, width = batch["img"].shape[2:]
            nb = len(batch["img"])
            bboxes = batch["bboxes"] * torch.tensor((width, height, width, height), device=self.device)
            self.lb = (
                [
                    torch.cat([batch["cls"][batch["batch_idx"] == i], bboxes[batch["batch_idx"] == i]], dim=-1)
                    for i in range(nb)
                ]
                if self.args.save_hybrid
                else []
            )  # for autolabelling

        return batch

    def init_metrics(self, model):
        """Initialize evaluation metrics for YOLO."""
        val = self.data.get(self.args.split, "")  # validation path
        self.is_coco = isinstance(val, str) and "coco" in val and val.endswith(f"{os.sep}val2017.txt")  # is COCO
        self.is_lvis = isinstance(val, str) and "lvis" in val and not self.is_coco  # is LVIS
        self.class_map = converter.coco80_to_coco91_class() if self.is_coco else list(range(len(model.names)))
        self.args.save_json |= (self.is_coco or self.is_lvis) and not self.training  # run on final val if training COCO
        self.names = model.names
        self.nc = len(model.names)
        self.metrics.names = self.names
        self.metrics.plot = self.args.plots
        self.confusion_matrix = ConfusionMatrix(nc=self.nc, conf=self.args.conf)
        self.seen = 0
        self.jdict = []
        self.stats = dict(tp=[], conf=[], pred_cls=[], target_cls=[], target_img=[])

    def get_desc(self):
        """Return a formatted string summarizing class metrics of YOLO model."""
        return ("%22s" + "%11s" * 6) % ("Class", "Images", "Instances", "Box(P", "R", "mAP50", "mAP50-95)")

    def postprocess(self, preds):
        """Apply Non-maximum suppression to prediction outputs."""
        return ops.non_max_suppression(
            preds,
            self.args.conf,
            self.args.iou,
            labels=self.lb,
            multi_label=True,
            agnostic=self.args.single_cls,
            max_det=self.args.max_det,
        )

    def _prepare_batch(self, si, batch):
        """Prepares a batch of images and annotations for validation."""
        idx = batch["batch_idx"] == si
        cls = batch["cls"][idx].squeeze(-1)
        bbox = batch["bboxes"][idx]
        ori_shape = batch["ori_shape"][si]
        imgsz = batch["img"].shape[2:]
        ratio_pad = batch["ratio_pad"][si]
        if len(cls):
            bbox = ops.xywh2xyxy(bbox) * torch.tensor(imgsz, device=self.device)[[1, 0, 1, 0]]  # target boxes
            ops.scale_boxes(imgsz, bbox, ori_shape, ratio_pad=ratio_pad)  # native-space labels
        return {"cls": cls, "bbox": bbox, "ori_shape": ori_shape, "imgsz": imgsz, "ratio_pad": ratio_pad}

    def _prepare_pred(self, pred, pbatch):
        """Prepares a batch of images and annotations for validation."""
        predn = pred.clone()
        ops.scale_boxes(
            pbatch["imgsz"], predn[:, :4], pbatch["ori_shape"], ratio_pad=pbatch["ratio_pad"]
        )  # native-space pred
        return predn

    def update_metrics(self, preds, batch):
        """Metrics."""
        for si, pred in enumerate(preds):
            self.seen += 1
            npr = len(pred)
            stat = dict(
                conf=torch.zeros(0, device=self.device),
                pred_cls=torch.zeros(0, device=self.device),
                tp=torch.zeros(npr, self.niou, dtype=torch.bool, device=self.device),
            )
            pbatch = self._prepare_batch(si, batch)
            cls, bbox = pbatch.pop("cls"), pbatch.pop("bbox")
            nl = len(cls)
            stat["target_cls"] = cls
            stat["target_img"] = cls.unique()
            if npr == 0:
                if nl:
                    for k in self.stats.keys():
                        self.stats[k].append(stat[k])
                    if self.args.plots:
                        self.confusion_matrix.process_batch(detections=None, gt_bboxes=bbox, gt_cls=cls)
                continue

            # Predictions
            if self.args.single_cls:
                pred[:, 5] = 0
            predn = self._prepare_pred(pred, pbatch)
            stat["conf"] = predn[:, 4]
            stat["pred_cls"] = predn[:, 5]

            # Evaluate
            if nl:
                stat["tp"] = self._process_batch(predn, bbox, cls)
                if self.args.plots:
                    self.confusion_matrix.process_batch(predn, bbox, cls)
            for k in self.stats.keys():
                self.stats[k].append(stat[k])

            # Save
            if self.args.save_json:
                self.pred_to_json(predn, batch["im_file"][si])
            if self.args.save_txt:
                file = self.save_dir / "labels" / f'{Path(batch["im_file"][si]).stem}.txt'
                self.save_one_txt(predn, self.args.save_conf, pbatch["ori_shape"], file)

    def finalize_metrics(self, *args, **kwargs):
        """Set final values for metrics speed and confusion matrix."""
        self.metrics.speed = self.speed
        self.metrics.confusion_matrix = self.confusion_matrix

    def get_stats(self):
        """Returns metrics statistics and results dictionary."""
        stats = {k: torch.cat(v, 0).cpu().numpy() for k, v in self.stats.items()}  # to numpy
        self.nt_per_class = np.bincount(stats["target_cls"].astype(int), minlength=self.nc)
        self.nt_per_image = np.bincount(stats["target_img"].astype(int), minlength=self.nc)
        stats.pop("target_img", None)
        if len(stats) and stats["tp"].any():
            self.metrics.process(**stats)
        return self.metrics.results_dict

    def print_results(self):
        """Prints training/validation set metrics per class."""
        pf = "%22s" + "%11i" * 2 + "%11.3g" * len(self.metrics.keys)  # print format
        LOGGER.info(pf % ("all", self.seen, self.nt_per_class.sum(), *self.metrics.mean_results()))
        if self.nt_per_class.sum() == 0:
            LOGGER.warning(f"WARNING ⚠️ no labels found in {self.args.task} set, can not compute metrics without labels")

        # Print results per class
        if self.args.verbose and not self.training and self.nc > 1 and len(self.stats):
            for i, c in enumerate(self.metrics.ap_class_index):
                LOGGER.info(
                    pf % (self.names[c], self.nt_per_image[c], self.nt_per_class[c], *self.metrics.class_result(i))
                )

        if self.args.plots:
            for normalize in True, False:
                self.confusion_matrix.plot(
                    save_dir=self.save_dir, names=self.names.values(), normalize=normalize, on_plot=self.on_plot
                )

    def _process_batch(self, detections, gt_bboxes, gt_cls):
        """
        Return correct prediction matrix.

        Args:
            detections (torch.Tensor): Tensor of shape [N, 6] representing detections.
                Each detection is of the format: x1, y1, x2, y2, conf, class.
            labels (torch.Tensor): Tensor of shape [M, 5] representing labels.
                Each label is of the format: class, x1, y1, x2, y2.

        Returns:
            (torch.Tensor): Correct prediction matrix of shape [N, 10] for 10 IoU levels.
        """
        iou = box_iou(gt_bboxes, detections[:, :4])
        return self.match_predictions(detections[:, 5], gt_cls, iou)

    def build_dataset(self, img_path, mode="val", batch=None):
        """
        Build YOLO Dataset.

        Args:
            img_path (str): Path to the folder containing images.
            mode (str): `train` mode or `val` mode, users are able to customize different augmentations for each mode.
            batch (int, optional): Size of batches, this is for `rect`. Defaults to None.
        """
        return build_yolo_dataset(self.args, img_path, batch, self.data, mode=mode, stride=self.stride)

    def get_dataloader(self, dataset_path, batch_size):
        """Construct and return dataloader."""
        dataset = self.build_dataset(dataset_path, batch=batch_size, mode="val")
        return build_dataloader(dataset, batch_size, self.args.workers, shuffle=False, rank=-1)  # return dataloader

    def plot_val_samples(self, batch, ni):
        """Plot validation image samples."""
        plot_images(
            batch["img"],
            batch["batch_idx"],
            batch["cls"].squeeze(-1),
            batch["bboxes"],
            paths=batch["im_file"],
            fname=self.save_dir / f"val_batch{ni}_labels.jpg",
            names=self.names,
            on_plot=self.on_plot,
        )

    def plot_predictions(self, batch, preds, ni):
        """Plots predicted bounding boxes on input images and saves the result."""
        plot_images(
            batch["img"],
            *output_to_target(preds, max_det=self.args.max_det),
            paths=batch["im_file"],
            fname=self.save_dir / f"val_batch{ni}_pred.jpg",
            names=self.names,
            on_plot=self.on_plot,
        )  # pred

    def save_one_txt(self, predn, save_conf, shape, file):
        """Save YOLO detections to a txt file in normalized coordinates in a specific format."""
        gn = torch.tensor(shape)[[1, 0, 1, 0]]  # normalization gain whwh
        for *xyxy, conf, cls in predn.tolist():
            xywh = (ops.xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh
            line = (cls, *xywh, conf) if save_conf else (cls, *xywh)  # label format
            with open(file, "a") as f:
                f.write(("%g " * len(line)).rstrip() % line + "\n")

    def pred_to_json(self, predn, filename):
        """Serialize YOLO predictions to COCO json format."""
        stem = Path(filename).stem
        image_id = int(stem) if stem.isnumeric() else stem
        box = ops.xyxy2xywh(predn[:, :4])  # xywh
        box[:, :2] -= box[:, 2:] / 2  # xy center to top-left corner
        for p, b in zip(predn.tolist(), box.tolist()):
            self.jdict.append(
                {
                    "image_id": image_id,
                    "category_id": self.class_map[int(p[5])]
                    + (1 if self.is_lvis else 0),  # index starts from 1 if it's lvis
                    "bbox": [round(x, 3) for x in b],
                    "score": round(p[4], 5),
                }
            )

    def eval_json(self, stats):
        """Evaluates YOLO output in JSON format and returns performance statistics."""
        if self.args.save_json and (self.is_coco or self.is_lvis) and len(self.jdict):
            pred_json = self.save_dir / "predictions.json"  # predictions
            anno_json = (
                self.data["path"]
                / "annotations"
                / ("instances_val2017.json" if self.is_coco else f"lvis_v1_{self.args.split}.json")
            )  # annotations
            pkg = "pycocotools" if self.is_coco else "lvis"
            LOGGER.info(f"\nEvaluating {pkg} mAP using {pred_json} and {anno_json}...")
            try:  # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
                for x in pred_json, anno_json:
                    assert x.is_file(), f"{x} file not found"
                check_requirements("pycocotools>=2.0.6" if self.is_coco else "lvis>=0.5.3")
                if self.is_coco:
                    from pycocotools.coco import COCO  # noqa
                    from pycocotools.cocoeval import COCOeval  # noqa

                    anno = COCO(str(anno_json))  # init annotations api
                    pred = anno.loadRes(str(pred_json))  # init predictions api (must pass string, not Path)
                    val = COCOeval(anno, pred, "bbox")
                else:
                    from lvis import LVIS, LVISEval

                    anno = LVIS(str(anno_json))  # init annotations api
                    pred = anno._load_json(str(pred_json))  # init predictions api (must pass string, not Path)
                    val = LVISEval(anno, pred, "bbox")
                val.params.imgIds = [int(Path(x).stem) for x in self.dataloader.dataset.im_files]  # images to eval
                val.evaluate()
                val.accumulate()
                val.summarize()
                if self.is_lvis:
                    val.print_results()  # explicitly call print_results
                # update mAP50-95 and mAP50
                stats[self.metrics.keys[-1]], stats[self.metrics.keys[-2]] = (
                    val.stats[:2] if self.is_coco else [val.results["AP50"], val.results["AP"]]
                )
            except Exception as e:
                LOGGER.warning(f"{pkg} unable to run: {e}")
        return stats

__init__(dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None)

Khởi tạo mô hình phát hiện với các biến và cài đặt cần thiết.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
    """Initialize detection model with necessary variables and settings."""
    super().__init__(dataloader, save_dir, pbar, args, _callbacks)
    self.nt_per_class = None
    self.nt_per_image = None
    self.is_coco = False
    self.is_lvis = False
    self.class_map = None
    self.args.task = "detect"
    self.metrics = DetMetrics(save_dir=self.save_dir, on_plot=self.on_plot)
    self.iouv = torch.linspace(0.5, 0.95, 10)  # IoU vector for mAP@0.5:0.95
    self.niou = self.iouv.numel()
    self.lb = []  # for autolabelling

build_dataset(img_path, mode='val', batch=None)

Xây dựng YOLO Tập dữ liệu.

Thông số:

Tên Kiểu Sự miêu tả Mặc định
img_path str

Đường dẫn đến thư mục chứa hình ảnh.

bắt buộc
mode str

train chế độ hoặc val Chế độ, người dùng có thể tùy chỉnh các phần tăng cường khác nhau cho từng chế độ.

'val'
batch int

Kích thước của lô, cái này dành cho rect. Mặc định là Không có.

None
Mã nguồn trong ultralytics/models/yolo/detect/val.py
def build_dataset(self, img_path, mode="val", batch=None):
    """
    Build YOLO Dataset.

    Args:
        img_path (str): Path to the folder containing images.
        mode (str): `train` mode or `val` mode, users are able to customize different augmentations for each mode.
        batch (int, optional): Size of batches, this is for `rect`. Defaults to None.
    """
    return build_yolo_dataset(self.args, img_path, batch, self.data, mode=mode, stride=self.stride)

eval_json(stats)

Đánh giá YOLO đầu ra ở định dạng JSON và trả về thống kê hiệu suất.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def eval_json(self, stats):
    """Evaluates YOLO output in JSON format and returns performance statistics."""
    if self.args.save_json and (self.is_coco or self.is_lvis) and len(self.jdict):
        pred_json = self.save_dir / "predictions.json"  # predictions
        anno_json = (
            self.data["path"]
            / "annotations"
            / ("instances_val2017.json" if self.is_coco else f"lvis_v1_{self.args.split}.json")
        )  # annotations
        pkg = "pycocotools" if self.is_coco else "lvis"
        LOGGER.info(f"\nEvaluating {pkg} mAP using {pred_json} and {anno_json}...")
        try:  # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
            for x in pred_json, anno_json:
                assert x.is_file(), f"{x} file not found"
            check_requirements("pycocotools>=2.0.6" if self.is_coco else "lvis>=0.5.3")
            if self.is_coco:
                from pycocotools.coco import COCO  # noqa
                from pycocotools.cocoeval import COCOeval  # noqa

                anno = COCO(str(anno_json))  # init annotations api
                pred = anno.loadRes(str(pred_json))  # init predictions api (must pass string, not Path)
                val = COCOeval(anno, pred, "bbox")
            else:
                from lvis import LVIS, LVISEval

                anno = LVIS(str(anno_json))  # init annotations api
                pred = anno._load_json(str(pred_json))  # init predictions api (must pass string, not Path)
                val = LVISEval(anno, pred, "bbox")
            val.params.imgIds = [int(Path(x).stem) for x in self.dataloader.dataset.im_files]  # images to eval
            val.evaluate()
            val.accumulate()
            val.summarize()
            if self.is_lvis:
                val.print_results()  # explicitly call print_results
            # update mAP50-95 and mAP50
            stats[self.metrics.keys[-1]], stats[self.metrics.keys[-2]] = (
                val.stats[:2] if self.is_coco else [val.results["AP50"], val.results["AP"]]
            )
        except Exception as e:
            LOGGER.warning(f"{pkg} unable to run: {e}")
    return stats

finalize_metrics(*args, **kwargs)

Đặt giá trị cuối cùng cho ma trận chỉ số, tốc độ và nhầm lẫn.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def finalize_metrics(self, *args, **kwargs):
    """Set final values for metrics speed and confusion matrix."""
    self.metrics.speed = self.speed
    self.metrics.confusion_matrix = self.confusion_matrix

get_dataloader(dataset_path, batch_size)

Xây dựng và trả về dataloader.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def get_dataloader(self, dataset_path, batch_size):
    """Construct and return dataloader."""
    dataset = self.build_dataset(dataset_path, batch=batch_size, mode="val")
    return build_dataloader(dataset, batch_size, self.args.workers, shuffle=False, rank=-1)  # return dataloader

get_desc()

Trả về một chuỗi được định dạng tóm tắt các chỉ số lớp của YOLO mẫu.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def get_desc(self):
    """Return a formatted string summarizing class metrics of YOLO model."""
    return ("%22s" + "%11s" * 6) % ("Class", "Images", "Instances", "Box(P", "R", "mAP50", "mAP50-95)")

get_stats()

Trả về số liệu thống kê và từ điển kết quả.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def get_stats(self):
    """Returns metrics statistics and results dictionary."""
    stats = {k: torch.cat(v, 0).cpu().numpy() for k, v in self.stats.items()}  # to numpy
    self.nt_per_class = np.bincount(stats["target_cls"].astype(int), minlength=self.nc)
    self.nt_per_image = np.bincount(stats["target_img"].astype(int), minlength=self.nc)
    stats.pop("target_img", None)
    if len(stats) and stats["tp"].any():
        self.metrics.process(**stats)
    return self.metrics.results_dict

init_metrics(model)

Khởi tạo số liệu đánh giá cho YOLO.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def init_metrics(self, model):
    """Initialize evaluation metrics for YOLO."""
    val = self.data.get(self.args.split, "")  # validation path
    self.is_coco = isinstance(val, str) and "coco" in val and val.endswith(f"{os.sep}val2017.txt")  # is COCO
    self.is_lvis = isinstance(val, str) and "lvis" in val and not self.is_coco  # is LVIS
    self.class_map = converter.coco80_to_coco91_class() if self.is_coco else list(range(len(model.names)))
    self.args.save_json |= (self.is_coco or self.is_lvis) and not self.training  # run on final val if training COCO
    self.names = model.names
    self.nc = len(model.names)
    self.metrics.names = self.names
    self.metrics.plot = self.args.plots
    self.confusion_matrix = ConfusionMatrix(nc=self.nc, conf=self.args.conf)
    self.seen = 0
    self.jdict = []
    self.stats = dict(tp=[], conf=[], pred_cls=[], target_cls=[], target_img=[])

plot_predictions(batch, preds, ni)

Biểu đồ dự đoán các hộp giới hạn trên hình ảnh đầu vào và lưu kết quả.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def plot_predictions(self, batch, preds, ni):
    """Plots predicted bounding boxes on input images and saves the result."""
    plot_images(
        batch["img"],
        *output_to_target(preds, max_det=self.args.max_det),
        paths=batch["im_file"],
        fname=self.save_dir / f"val_batch{ni}_pred.jpg",
        names=self.names,
        on_plot=self.on_plot,
    )  # pred

plot_val_samples(batch, ni)

Mẫu hình ảnh xác thực lô.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def plot_val_samples(self, batch, ni):
    """Plot validation image samples."""
    plot_images(
        batch["img"],
        batch["batch_idx"],
        batch["cls"].squeeze(-1),
        batch["bboxes"],
        paths=batch["im_file"],
        fname=self.save_dir / f"val_batch{ni}_labels.jpg",
        names=self.names,
        on_plot=self.on_plot,
    )

postprocess(preds)

Áp dụng Triệt tiêu không tối đa cho kết quả dự đoán.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def postprocess(self, preds):
    """Apply Non-maximum suppression to prediction outputs."""
    return ops.non_max_suppression(
        preds,
        self.args.conf,
        self.args.iou,
        labels=self.lb,
        multi_label=True,
        agnostic=self.args.single_cls,
        max_det=self.args.max_det,
    )

pred_to_json(predn, filename)

Lập số sê-ri hóa YOLO dự đoán sang định dạng COCO json.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def pred_to_json(self, predn, filename):
    """Serialize YOLO predictions to COCO json format."""
    stem = Path(filename).stem
    image_id = int(stem) if stem.isnumeric() else stem
    box = ops.xyxy2xywh(predn[:, :4])  # xywh
    box[:, :2] -= box[:, 2:] / 2  # xy center to top-left corner
    for p, b in zip(predn.tolist(), box.tolist()):
        self.jdict.append(
            {
                "image_id": image_id,
                "category_id": self.class_map[int(p[5])]
                + (1 if self.is_lvis else 0),  # index starts from 1 if it's lvis
                "bbox": [round(x, 3) for x in b],
                "score": round(p[4], 5),
            }
        )

preprocess(batch)

Xử lý sơ bộ hàng loạt hình ảnh cho YOLO Đào tạo.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def preprocess(self, batch):
    """Preprocesses batch of images for YOLO training."""
    batch["img"] = batch["img"].to(self.device, non_blocking=True)
    batch["img"] = (batch["img"].half() if self.args.half else batch["img"].float()) / 255
    for k in ["batch_idx", "cls", "bboxes"]:
        batch[k] = batch[k].to(self.device)

    if self.args.save_hybrid:
        height, width = batch["img"].shape[2:]
        nb = len(batch["img"])
        bboxes = batch["bboxes"] * torch.tensor((width, height, width, height), device=self.device)
        self.lb = (
            [
                torch.cat([batch["cls"][batch["batch_idx"] == i], bboxes[batch["batch_idx"] == i]], dim=-1)
                for i in range(nb)
            ]
            if self.args.save_hybrid
            else []
        )  # for autolabelling

    return batch

print_results()

In số liệu đào tạo/xác thực cho mỗi lớp.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def print_results(self):
    """Prints training/validation set metrics per class."""
    pf = "%22s" + "%11i" * 2 + "%11.3g" * len(self.metrics.keys)  # print format
    LOGGER.info(pf % ("all", self.seen, self.nt_per_class.sum(), *self.metrics.mean_results()))
    if self.nt_per_class.sum() == 0:
        LOGGER.warning(f"WARNING ⚠️ no labels found in {self.args.task} set, can not compute metrics without labels")

    # Print results per class
    if self.args.verbose and not self.training and self.nc > 1 and len(self.stats):
        for i, c in enumerate(self.metrics.ap_class_index):
            LOGGER.info(
                pf % (self.names[c], self.nt_per_image[c], self.nt_per_class[c], *self.metrics.class_result(i))
            )

    if self.args.plots:
        for normalize in True, False:
            self.confusion_matrix.plot(
                save_dir=self.save_dir, names=self.names.values(), normalize=normalize, on_plot=self.on_plot
            )

save_one_txt(predn, save_conf, shape, file)

Cứu YOLO Phát hiện tệp TXT ở tọa độ chuẩn hóa ở một định dạng cụ thể.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def save_one_txt(self, predn, save_conf, shape, file):
    """Save YOLO detections to a txt file in normalized coordinates in a specific format."""
    gn = torch.tensor(shape)[[1, 0, 1, 0]]  # normalization gain whwh
    for *xyxy, conf, cls in predn.tolist():
        xywh = (ops.xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh
        line = (cls, *xywh, conf) if save_conf else (cls, *xywh)  # label format
        with open(file, "a") as f:
            f.write(("%g " * len(line)).rstrip() % line + "\n")

update_metrics(preds, batch)

Metrics.

Mã nguồn trong ultralytics/models/yolo/detect/val.py
def update_metrics(self, preds, batch):
    """Metrics."""
    for si, pred in enumerate(preds):
        self.seen += 1
        npr = len(pred)
        stat = dict(
            conf=torch.zeros(0, device=self.device),
            pred_cls=torch.zeros(0, device=self.device),
            tp=torch.zeros(npr, self.niou, dtype=torch.bool, device=self.device),
        )
        pbatch = self._prepare_batch(si, batch)
        cls, bbox = pbatch.pop("cls"), pbatch.pop("bbox")
        nl = len(cls)
        stat["target_cls"] = cls
        stat["target_img"] = cls.unique()
        if npr == 0:
            if nl:
                for k in self.stats.keys():
                    self.stats[k].append(stat[k])
                if self.args.plots:
                    self.confusion_matrix.process_batch(detections=None, gt_bboxes=bbox, gt_cls=cls)
            continue

        # Predictions
        if self.args.single_cls:
            pred[:, 5] = 0
        predn = self._prepare_pred(pred, pbatch)
        stat["conf"] = predn[:, 4]
        stat["pred_cls"] = predn[:, 5]

        # Evaluate
        if nl:
            stat["tp"] = self._process_batch(predn, bbox, cls)
            if self.args.plots:
                self.confusion_matrix.process_batch(predn, bbox, cls)
        for k in self.stats.keys():
            self.stats[k].append(stat[k])

        # Save
        if self.args.save_json:
            self.pred_to_json(predn, batch["im_file"][si])
        if self.args.save_txt:
            file = self.save_dir / "labels" / f'{Path(batch["im_file"][si]).stem}.txt'
            self.save_one_txt(predn, self.args.save_conf, pbatch["ori_shape"], file)





Created 2023-11-12, Updated 2024-06-02
Authors: glenn-jocher (5), Burhan-Q (1)