рд╕рд╛рдордЧреНрд░реА рдкрд░ рдЬрд╛рдПрдВ

рдХреЗ рд▓рд┐рдП рд╕рдВрджрд░реНрдн ultralytics/engine/validator.py

рдиреЛрдЯ

рдпрд╣ рдлрд╝рд╛рдЗрд▓ рдпрд╣рд╛рдБ рдЙрдкрд▓рдмреНрдз рд╣реИ https://github.com/ultralytics/ultralytics/рдмреВрдБрдж/рдореБрдЦреНрдп/ultralytics/рдЗрдВрдЬрди/validator.pyред рдпрджрд┐ рдЖрдк рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рджреЗрдЦрддреЗ рд╣реИрдВ рддреЛ рдХреГрдкрдпрд╛ рдкреБрд▓ рдЕрдиреБрд░реЛрдз рдХрд╛ рдпреЛрдЧрджрд╛рди рдХрд░рдХреЗ рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдВ ЁЯЫая╕Пред ЁЯЩП рдзрдиреНрдпрд╡рд╛рдж !



ultralytics.engine.validator.BaseValidator

BaseValidator.

рд╕рддреНрдпрд╛рдкрдирдХрд░реНрддрд╛ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЖрдзрд╛рд░ рд╡рд░реНрдЧред

рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдБ:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
args SimpleNamespace

рд╕рддреНрдпрд╛рдкрдирдХрд░реНрддрд╛ рдХреЗ рд▓рд┐рдП рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рдиред

dataloader DataLoader

рд╕рддреНрдпрд╛рдкрди рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛рд▓реЛрдбрд░ред

pbar tqdm

рд╕рддреНрдпрд╛рдкрди рдХреЗ рджреМрд░рд╛рди рдЕрджреНрдпрддрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рдЧрддрд┐ рдкрдЯреНрдЯреАред

model Module

рдорд╛рдиреНрдп рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдореЙрдбрд▓ред

data dict

рдбреЗрдЯрд╛ рд╢рдмреНрджрдХреЛрд╢ред

device device

рд╕рддреНрдпрд╛рдкрди рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбрд┐рд╡рд╛рдЗрд╕ред

batch_i int

рд╡рд░реНрддрдорд╛рди рдмреИрдЪ рд╕реВрдЪрдХрд╛рдВрдХред

training bool

рдХреНрдпрд╛ рдореЙрдбрд▓ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдореЛрдб рдореЗрдВ рд╣реИред

names dict

рдХрдХреНрд╖рд╛ рдХреЗ рдирд╛рдоред

seen

рд╕рддреНрдпрд╛рдкрди рдХреЗ рджреМрд░рд╛рди рдЕрдм рддрдХ рджреЗрдЦреА рдЧрдИ рдЫрд╡рд┐рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рд░рд┐рдХреЙрд░реНрдб рдХрд░рддрд╛ рд╣реИред

stats

рд╕рддреНрдпрд╛рдкрди рдХреЗ рджреМрд░рд╛рди рдЖрдВрдХрдбрд╝реЛрдВ рдХреЗ рд▓рд┐рдП рдкреНрд▓реЗрд╕рд╣реЛрд▓реНрдбрд░ред

confusion_matrix

рдПрдХ рднреНрд░рдо рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХреЗ рд▓рд┐рдП рдкреНрд▓реЗрд╕рд╣реЛрд▓реНрдбрд░ред

nc

рдХрдХреНрд╖рд╛рдУрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ред

iouv

(torch.Tensor): 0.05 рдХреЗ рд░рд┐рдХреНрдд рд╕реНрдерд╛рди рдореЗрдВ 0.50 рд╕реЗ 0.95 рддрдХ IoU рдереНрд░реЗрд╕рд╣реЛрд▓реНрдбред

jdict dict

JSON рд╕рддреНрдпрд╛рдкрди рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╢рдмреНрджрдХреЛрд╢ред

speed dict

рдХреБрдВрдЬреА 'рдкреНрд░реАрдкреНрд░реЛрд╕реЗрд╕', 'рдЕрдиреБрдорд╛рди', 'рд╣рд╛рдирд┐', 'рдкреЛрд╕реНрдЯрдкреНрд░реЛрд╕реЗрд╕' рдФрд░ рдЙрдирдХреЗ рд╕рдВрдмрдВрдзрд┐рдд рдХреЗ рд╕рд╛рде рд╢рдмреНрджрдХреЛрд╢ рдорд┐рд▓реАрд╕реЗрдХрдВрдб рдореЗрдВ рдмреИрдЪ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рд╕рдордпред

save_dir Path

рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЛ рдмрдЪрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ред

plots dict

рд╡рд┐рдЬрд╝реБрдЕрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдХреЗ рд▓рд┐рдП рднреВрдЦрдВрдбреЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╢рдмреНрджрдХреЛрд╢ред

callbacks dict

рд╡рд┐рднрд┐рдиреНрди рдХреЙрд▓рдмреИрдХ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╢рдмреНрджрдХреЛрд╢ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
class BaseValidator:
    """
    BaseValidator.

    A base class for creating validators.

    Attributes:
        args (SimpleNamespace): Configuration for the validator.
        dataloader (DataLoader): Dataloader to use for validation.
        pbar (tqdm): Progress bar to update during validation.
        model (nn.Module): Model to validate.
        data (dict): Data dictionary.
        device (torch.device): Device to use for validation.
        batch_i (int): Current batch index.
        training (bool): Whether the model is in training mode.
        names (dict): Class names.
        seen: Records the number of images seen so far during validation.
        stats: Placeholder for statistics during validation.
        confusion_matrix: Placeholder for a confusion matrix.
        nc: Number of classes.
        iouv: (torch.Tensor): IoU thresholds from 0.50 to 0.95 in spaces of 0.05.
        jdict (dict): Dictionary to store JSON validation results.
        speed (dict): Dictionary with keys 'preprocess', 'inference', 'loss', 'postprocess' and their respective
                      batch processing times in milliseconds.
        save_dir (Path): Directory to save results.
        plots (dict): Dictionary to store plots for visualization.
        callbacks (dict): Dictionary to store various callback functions.
    """

    def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
        """
        Initializes a BaseValidator instance.

        Args:
            dataloader (torch.utils.data.DataLoader): Dataloader to be used for validation.
            save_dir (Path, optional): Directory to save results.
            pbar (tqdm.tqdm): Progress bar for displaying progress.
            args (SimpleNamespace): Configuration for the validator.
            _callbacks (dict): Dictionary to store various callback functions.
        """
        self.args = get_cfg(overrides=args)
        self.dataloader = dataloader
        self.pbar = pbar
        self.stride = None
        self.data = None
        self.device = None
        self.batch_i = None
        self.training = True
        self.names = None
        self.seen = None
        self.stats = None
        self.confusion_matrix = None
        self.nc = None
        self.iouv = None
        self.jdict = None
        self.speed = {"preprocess": 0.0, "inference": 0.0, "loss": 0.0, "postprocess": 0.0}

        self.save_dir = save_dir or get_save_dir(self.args)
        (self.save_dir / "labels" if self.args.save_txt else self.save_dir).mkdir(parents=True, exist_ok=True)
        if self.args.conf is None:
            self.args.conf = 0.001  # default conf=0.001
        self.args.imgsz = check_imgsz(self.args.imgsz, max_dim=1)

        self.plots = {}
        self.callbacks = _callbacks or callbacks.get_default_callbacks()

    @smart_inference_mode()
    def __call__(self, trainer=None, model=None):
        """Supports validation of a pre-trained model if passed or a model being trained if trainer is passed (trainer
        gets priority).
        """
        self.training = trainer is not None
        augment = self.args.augment and (not self.training)
        if self.training:
            self.device = trainer.device
            self.data = trainer.data
            self.args.half = self.device.type != "cpu"  # force FP16 val during training
            model = trainer.ema.ema or trainer.model
            model = model.half() if self.args.half else model.float()
            # self.model = model
            self.loss = torch.zeros_like(trainer.loss_items, device=trainer.device)
            self.args.plots &= trainer.stopper.possible_stop or (trainer.epoch == trainer.epochs - 1)
            model.eval()
        else:
            callbacks.add_integration_callbacks(self)
            model = AutoBackend(
                weights=model or self.args.model,
                device=select_device(self.args.device, self.args.batch),
                dnn=self.args.dnn,
                data=self.args.data,
                fp16=self.args.half,
            )
            # self.model = model
            self.device = model.device  # update device
            self.args.half = model.fp16  # update half
            stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine
            imgsz = check_imgsz(self.args.imgsz, stride=stride)
            if engine:
                self.args.batch = model.batch_size
            elif not pt and not jit:
                self.args.batch = 1  # export.py models default to batch-size 1
                LOGGER.info(f"Forcing batch=1 square inference (1,3,{imgsz},{imgsz}) for non-PyTorch models")

            if str(self.args.data).split(".")[-1] in {"yaml", "yml"}:
                self.data = check_det_dataset(self.args.data)
            elif self.args.task == "classify":
                self.data = check_cls_dataset(self.args.data, split=self.args.split)
            else:
                raise FileNotFoundError(emojis(f"Dataset '{self.args.data}' for task={self.args.task} not found тЭМ"))

            if self.device.type in {"cpu", "mps"}:
                self.args.workers = 0  # faster CPU val as time dominated by inference, not dataloading
            if not pt:
                self.args.rect = False
            self.stride = model.stride  # used in get_dataloader() for padding
            self.dataloader = self.dataloader or self.get_dataloader(self.data.get(self.args.split), self.args.batch)

            model.eval()
            model.warmup(imgsz=(1 if pt else self.args.batch, 3, imgsz, imgsz))  # warmup

        self.run_callbacks("on_val_start")
        dt = (
            Profile(device=self.device),
            Profile(device=self.device),
            Profile(device=self.device),
            Profile(device=self.device),
        )
        bar = TQDM(self.dataloader, desc=self.get_desc(), total=len(self.dataloader))
        self.init_metrics(de_parallel(model))
        self.jdict = []  # empty before each val
        for batch_i, batch in enumerate(bar):
            self.run_callbacks("on_val_batch_start")
            self.batch_i = batch_i
            # Preprocess
            with dt[0]:
                batch = self.preprocess(batch)

            # Inference
            with dt[1]:
                preds = model(batch["img"], augment=augment)

            # Loss
            with dt[2]:
                if self.training:
                    self.loss += model.loss(batch, preds)[1]

            # Postprocess
            with dt[3]:
                preds = self.postprocess(preds)

            self.update_metrics(preds, batch)
            if self.args.plots and batch_i < 3:
                self.plot_val_samples(batch, batch_i)
                self.plot_predictions(batch, preds, batch_i)

            self.run_callbacks("on_val_batch_end")
        stats = self.get_stats()
        self.check_stats(stats)
        self.speed = dict(zip(self.speed.keys(), (x.t / len(self.dataloader.dataset) * 1e3 for x in dt)))
        self.finalize_metrics()
        self.print_results()
        self.run_callbacks("on_val_end")
        if self.training:
            model.float()
            results = {**stats, **trainer.label_loss_items(self.loss.cpu() / len(self.dataloader), prefix="val")}
            return {k: round(float(v), 5) for k, v in results.items()}  # return results as 5 decimal place floats
        else:
            LOGGER.info(
                "Speed: %.1fms preprocess, %.1fms inference, %.1fms loss, %.1fms postprocess per image"
                % tuple(self.speed.values())
            )
            if self.args.save_json and self.jdict:
                with open(str(self.save_dir / "predictions.json"), "w") as f:
                    LOGGER.info(f"Saving {f.name}...")
                    json.dump(self.jdict, f)  # flatten and save
                stats = self.eval_json(stats)  # update stats
            if self.args.plots or self.args.save_json:
                LOGGER.info(f"Results saved to {colorstr('bold', self.save_dir)}")
            return stats

    def match_predictions(self, pred_classes, true_classes, iou, use_scipy=False):
        """
        Matches predictions to ground truth objects (pred_classes, true_classes) using IoU.

        Args:
            pred_classes (torch.Tensor): Predicted class indices of shape(N,).
            true_classes (torch.Tensor): Target class indices of shape(M,).
            iou (torch.Tensor): An NxM tensor containing the pairwise IoU values for predictions and ground of truth
            use_scipy (bool): Whether to use scipy for matching (more precise).

        Returns:
            (torch.Tensor): Correct tensor of shape(N,10) for 10 IoU thresholds.
        """
        # Dx10 matrix, where D - detections, 10 - IoU thresholds
        correct = np.zeros((pred_classes.shape[0], self.iouv.shape[0])).astype(bool)
        # LxD matrix where L - labels (rows), D - detections (columns)
        correct_class = true_classes[:, None] == pred_classes
        iou = iou * correct_class  # zero out the wrong classes
        iou = iou.cpu().numpy()
        for i, threshold in enumerate(self.iouv.cpu().tolist()):
            if use_scipy:
                # WARNING: known issue that reduces mAP in https://github.com/ultralytics/ultralytics/pull/4708
                import scipy  # scope import to avoid importing for all commands

                cost_matrix = iou * (iou >= threshold)
                if cost_matrix.any():
                    labels_idx, detections_idx = scipy.optimize.linear_sum_assignment(cost_matrix, maximize=True)
                    valid = cost_matrix[labels_idx, detections_idx] > 0
                    if valid.any():
                        correct[detections_idx[valid], i] = True
            else:
                matches = np.nonzero(iou >= threshold)  # IoU > threshold and classes match
                matches = np.array(matches).T
                if matches.shape[0]:
                    if matches.shape[0] > 1:
                        matches = matches[iou[matches[:, 0], matches[:, 1]].argsort()[::-1]]
                        matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
                        # matches = matches[matches[:, 2].argsort()[::-1]]
                        matches = matches[np.unique(matches[:, 0], return_index=True)[1]]
                    correct[matches[:, 1].astype(int), i] = True
        return torch.tensor(correct, dtype=torch.bool, device=pred_classes.device)

    def add_callback(self, event: str, callback):
        """Appends the given callback."""
        self.callbacks[event].append(callback)

    def run_callbacks(self, event: str):
        """Runs all callbacks associated with a specified event."""
        for callback in self.callbacks.get(event, []):
            callback(self)

    def get_dataloader(self, dataset_path, batch_size):
        """Get data loader from dataset path and batch size."""
        raise NotImplementedError("get_dataloader function not implemented for this validator")

    def build_dataset(self, img_path):
        """Build dataset."""
        raise NotImplementedError("build_dataset function not implemented in validator")

    def preprocess(self, batch):
        """Preprocesses an input batch."""
        return batch

    def postprocess(self, preds):
        """Describes and summarizes the purpose of 'postprocess()' but no details mentioned."""
        return preds

    def init_metrics(self, model):
        """Initialize performance metrics for the YOLO model."""
        pass

    def update_metrics(self, preds, batch):
        """Updates metrics based on predictions and batch."""
        pass

    def finalize_metrics(self, *args, **kwargs):
        """Finalizes and returns all metrics."""
        pass

    def get_stats(self):
        """Returns statistics about the model's performance."""
        return {}

    def check_stats(self, stats):
        """Checks statistics."""
        pass

    def print_results(self):
        """Prints the results of the model's predictions."""
        pass

    def get_desc(self):
        """Get description of the YOLO model."""
        pass

    @property
    def metric_keys(self):
        """Returns the metric keys used in YOLO training/validation."""
        return []

    def on_plot(self, name, data=None):
        """Registers plots (e.g. to be consumed in callbacks)"""
        self.plots[Path(name)] = {"data": data, "timestamp": time.time()}

    # TODO: may need to put these following functions into callback
    def plot_val_samples(self, batch, ni):
        """Plots validation samples during training."""
        pass

    def plot_predictions(self, batch, preds, ni):
        """Plots YOLO model predictions on batch images."""
        pass

    def pred_to_json(self, preds, batch):
        """Convert predictions to JSON format."""
        pass

    def eval_json(self, stats):
        """Evaluate and return JSON format of prediction statistics."""
        pass

metric_keys property

рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХреА рдЧрдИ рдореАрдЯреНрд░рд┐рдХ рдХреБрдВрдЬрд┐рдпреЛрдВ рдХреЛ рд▓реМрдЯрд╛рддрд╛ рд╣реИ YOLO рдкреНрд░рд╢рд┐рдХреНрд╖рдг/рд╕рддреНрдпрд╛рдкрдиред

__call__(trainer=None, model=None)

рдПрдХ рдкреВрд░реНрд╡-рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдореЙрдбрд▓ рдХреЗ рд╕рддреНрдпрд╛рдкрди рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ рдпрджрд┐ рдЙрддреНрддреАрд░реНрдг рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ рдпрд╛ рдПрдХ рдореЙрдбрд▓ рдХреЛ рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИ рдпрджрд┐ рдЯреНрд░реЗрдирд░ рдкрд╛рд╕ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ (рдЯреНрд░реЗрдирд░ рдкреНрд░рд╛рдердорд┐рдХрддрд╛ рдорд┐рд▓рддреА рд╣реИ)ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
@smart_inference_mode()
def __call__(self, trainer=None, model=None):
    """Supports validation of a pre-trained model if passed or a model being trained if trainer is passed (trainer
    gets priority).
    """
    self.training = trainer is not None
    augment = self.args.augment and (not self.training)
    if self.training:
        self.device = trainer.device
        self.data = trainer.data
        self.args.half = self.device.type != "cpu"  # force FP16 val during training
        model = trainer.ema.ema or trainer.model
        model = model.half() if self.args.half else model.float()
        # self.model = model
        self.loss = torch.zeros_like(trainer.loss_items, device=trainer.device)
        self.args.plots &= trainer.stopper.possible_stop or (trainer.epoch == trainer.epochs - 1)
        model.eval()
    else:
        callbacks.add_integration_callbacks(self)
        model = AutoBackend(
            weights=model or self.args.model,
            device=select_device(self.args.device, self.args.batch),
            dnn=self.args.dnn,
            data=self.args.data,
            fp16=self.args.half,
        )
        # self.model = model
        self.device = model.device  # update device
        self.args.half = model.fp16  # update half
        stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine
        imgsz = check_imgsz(self.args.imgsz, stride=stride)
        if engine:
            self.args.batch = model.batch_size
        elif not pt and not jit:
            self.args.batch = 1  # export.py models default to batch-size 1
            LOGGER.info(f"Forcing batch=1 square inference (1,3,{imgsz},{imgsz}) for non-PyTorch models")

        if str(self.args.data).split(".")[-1] in {"yaml", "yml"}:
            self.data = check_det_dataset(self.args.data)
        elif self.args.task == "classify":
            self.data = check_cls_dataset(self.args.data, split=self.args.split)
        else:
            raise FileNotFoundError(emojis(f"Dataset '{self.args.data}' for task={self.args.task} not found тЭМ"))

        if self.device.type in {"cpu", "mps"}:
            self.args.workers = 0  # faster CPU val as time dominated by inference, not dataloading
        if not pt:
            self.args.rect = False
        self.stride = model.stride  # used in get_dataloader() for padding
        self.dataloader = self.dataloader or self.get_dataloader(self.data.get(self.args.split), self.args.batch)

        model.eval()
        model.warmup(imgsz=(1 if pt else self.args.batch, 3, imgsz, imgsz))  # warmup

    self.run_callbacks("on_val_start")
    dt = (
        Profile(device=self.device),
        Profile(device=self.device),
        Profile(device=self.device),
        Profile(device=self.device),
    )
    bar = TQDM(self.dataloader, desc=self.get_desc(), total=len(self.dataloader))
    self.init_metrics(de_parallel(model))
    self.jdict = []  # empty before each val
    for batch_i, batch in enumerate(bar):
        self.run_callbacks("on_val_batch_start")
        self.batch_i = batch_i
        # Preprocess
        with dt[0]:
            batch = self.preprocess(batch)

        # Inference
        with dt[1]:
            preds = model(batch["img"], augment=augment)

        # Loss
        with dt[2]:
            if self.training:
                self.loss += model.loss(batch, preds)[1]

        # Postprocess
        with dt[3]:
            preds = self.postprocess(preds)

        self.update_metrics(preds, batch)
        if self.args.plots and batch_i < 3:
            self.plot_val_samples(batch, batch_i)
            self.plot_predictions(batch, preds, batch_i)

        self.run_callbacks("on_val_batch_end")
    stats = self.get_stats()
    self.check_stats(stats)
    self.speed = dict(zip(self.speed.keys(), (x.t / len(self.dataloader.dataset) * 1e3 for x in dt)))
    self.finalize_metrics()
    self.print_results()
    self.run_callbacks("on_val_end")
    if self.training:
        model.float()
        results = {**stats, **trainer.label_loss_items(self.loss.cpu() / len(self.dataloader), prefix="val")}
        return {k: round(float(v), 5) for k, v in results.items()}  # return results as 5 decimal place floats
    else:
        LOGGER.info(
            "Speed: %.1fms preprocess, %.1fms inference, %.1fms loss, %.1fms postprocess per image"
            % tuple(self.speed.values())
        )
        if self.args.save_json and self.jdict:
            with open(str(self.save_dir / "predictions.json"), "w") as f:
                LOGGER.info(f"Saving {f.name}...")
                json.dump(self.jdict, f)  # flatten and save
            stats = self.eval_json(stats)  # update stats
        if self.args.plots or self.args.save_json:
            LOGGER.info(f"Results saved to {colorstr('bold', self.save_dir)}")
        return stats

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

BaseValidator рдЖрд╡реГрддреНрддрд┐ рдкреНрд░рд╛рд░рдВрдн рдХрд░рддрд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
dataloader DataLoader

рд╕рддреНрдпрд╛рдкрди рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдбреЗрдЯрд╛рд▓реЛрдбрд░ред

None
save_dir Path

рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЛ рдмрдЪрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ред

None
pbar tqdm

рдкреНрд░рдЧрддрд┐ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рдЧрддрд┐ рдкрдЯреНрдЯреАред

None
args SimpleNamespace

рд╕рддреНрдпрд╛рдкрдирдХрд░реНрддрд╛ рдХреЗ рд▓рд┐рдП рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рдиред

None
_callbacks dict

рд╡рд┐рднрд┐рдиреНрди рдХреЙрд▓рдмреИрдХ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╢рдмреНрджрдХреЛрд╢ред

None
рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
    """
    Initializes a BaseValidator instance.

    Args:
        dataloader (torch.utils.data.DataLoader): Dataloader to be used for validation.
        save_dir (Path, optional): Directory to save results.
        pbar (tqdm.tqdm): Progress bar for displaying progress.
        args (SimpleNamespace): Configuration for the validator.
        _callbacks (dict): Dictionary to store various callback functions.
    """
    self.args = get_cfg(overrides=args)
    self.dataloader = dataloader
    self.pbar = pbar
    self.stride = None
    self.data = None
    self.device = None
    self.batch_i = None
    self.training = True
    self.names = None
    self.seen = None
    self.stats = None
    self.confusion_matrix = None
    self.nc = None
    self.iouv = None
    self.jdict = None
    self.speed = {"preprocess": 0.0, "inference": 0.0, "loss": 0.0, "postprocess": 0.0}

    self.save_dir = save_dir or get_save_dir(self.args)
    (self.save_dir / "labels" if self.args.save_txt else self.save_dir).mkdir(parents=True, exist_ok=True)
    if self.args.conf is None:
        self.args.conf = 0.001  # default conf=0.001
    self.args.imgsz = check_imgsz(self.args.imgsz, max_dim=1)

    self.plots = {}
    self.callbacks = _callbacks or callbacks.get_default_callbacks()

add_callback(event, callback)

рджрд┐рдП рдЧрдП рдХреЙрд▓рдмреИрдХ рдХреЛ рдЬреЛрдбрд╝рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def add_callback(self, event: str, callback):
    """Appends the given callback."""
    self.callbacks[event].append(callback)

build_dataset(img_path)

рдбреЗрдЯрд╛рд╕реЗрдЯ рдмрдирд╛рдПрдБред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def build_dataset(self, img_path):
    """Build dataset."""
    raise NotImplementedError("build_dataset function not implemented in validator")

check_stats(stats)

рдЖрдБрдХрдбрд╝реЛрдВ рдХреА рдЬрд╛рдБрдЪ рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def check_stats(self, stats):
    """Checks statistics."""
    pass

eval_json(stats)

рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА рдЖрдВрдХрдбрд╝реЛрдВ рдХреЗ JSON рдкреНрд░рд╛рд░реВрдк рдХрд╛ рдореВрд▓реНрдпрд╛рдВрдХрди рдХрд░реЗрдВ рдФрд░ рд╡рд╛рдкрд╕ рдХрд░реЗрдВред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def eval_json(self, stats):
    """Evaluate and return JSON format of prediction statistics."""
    pass

finalize_metrics(*args, **kwargs)

рд╕рднреА рдореАрдЯреНрд░рд┐рдХ рдХреЛ рдЕрдВрддрд┐рдо рд░реВрдк рджреЗрддрд╛ рд╣реИ рдФрд░ рд▓реМрдЯрд╛рддрд╛ рд╣реИ.

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def finalize_metrics(self, *args, **kwargs):
    """Finalizes and returns all metrics."""
    pass

get_dataloader(dataset_path, batch_size)

рдбреЗрдЯрд╛рд╕реЗрдЯ рдкрде рдФрд░ рдмреИрдЪ рдЖрдХрд╛рд░ рд╕реЗ рдбреЗрдЯрд╛ рд▓реЛрдбрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def get_dataloader(self, dataset_path, batch_size):
    """Get data loader from dataset path and batch size."""
    raise NotImplementedError("get_dataloader function not implemented for this validator")

get_desc()

рдХрд╛ рд╡рд┐рд╡рд░рдг рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ YOLO рдХреЛ рдЧрдврд╝рдирд╛ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def get_desc(self):
    """Get description of the YOLO model."""
    pass

get_stats()

рдореЙрдбрд▓ рдХреЗ рдкреНрд░рджрд░реНрд╢рди рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЖрдБрдХрдбрд╝реЗ рджреЗрддрд╛ рд╣реИ.

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def get_stats(self):
    """Returns statistics about the model's performance."""
    return {}

init_metrics(model)

рдХреЗ рд▓рд┐рдП рдкреНрд░рджрд░реНрд╢рди рдореАрдЯреНрд░рд┐рдХ рдкреНрд░рд╛рд░рдВрдн рдХрд░реЗрдВ YOLO рдХреЛ рдЧрдврд╝рдирд╛ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def init_metrics(self, model):
    """Initialize performance metrics for the YOLO model."""
    pass

match_predictions(pred_classes, true_classes, iou, use_scipy=False)

IoU рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЬрдореАрдиреА рд╕рдЪреНрдЪрд╛рдИ рдХреА рд╡рд╕реНрддреБрдУрдВ (pred_classes, true_classes) рд╕реЗ рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгрд┐рдпреЛрдВ рдХрд╛ рдорд┐рд▓рд╛рди рдХрд░рддрд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
pred_classes Tensor

рдЖрдХрд╛рд░ рдХреЗ рдЕрдиреБрдорд╛рдирд┐рдд рд╡рд░реНрдЧ рд╕реВрдЪрдХрд╛рдВрдХ (рдПрди,)ред

рдЖрд╡рд╢реНрдпрдХ
true_classes Tensor

рдЖрдХрд╛рд░ рдХреЗ рд▓рдХреНрд╖реНрдп рд╡рд░реНрдЧ рд╕реВрдЪрдХрд╛рдВрдХ (рдПрдо,)ред

рдЖрд╡рд╢реНрдпрдХ
iou Tensor

рдПрдХ рдПрдирдПрдХреНрд╕рдПрдо tensor рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгрд┐рдпреЛрдВ рдФрд░ рд╕рддреНрдп рдХреЗ рдЖрдзрд╛рд░ рдХреЗ рд▓рд┐рдП рдЬреЛрдбрд╝реАрджрд╛рд░ IoU рдореВрд▓реНрдпреЛрдВ рд╕реЗ рдпреБрдХреНрдд

рдЖрд╡рд╢реНрдпрдХ
use_scipy bool

рдорд┐рд▓рд╛рди рдХреЗ рд▓рд┐рдП рд╕реНрдХрд┐рдкреА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВ (рдЕрдзрд┐рдХ рд╕рдЯреАрдХ)ред

False

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
Tensor

рдЬреА рд╣рд╛рдБ tensor 10 IoU рдереНрд░реЗрд╕рд╣реЛрд▓реНрдб рдХреЗ рд▓рд┐рдП рдЖрдХрд╛рд░ (N, 10)ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def match_predictions(self, pred_classes, true_classes, iou, use_scipy=False):
    """
    Matches predictions to ground truth objects (pred_classes, true_classes) using IoU.

    Args:
        pred_classes (torch.Tensor): Predicted class indices of shape(N,).
        true_classes (torch.Tensor): Target class indices of shape(M,).
        iou (torch.Tensor): An NxM tensor containing the pairwise IoU values for predictions and ground of truth
        use_scipy (bool): Whether to use scipy for matching (more precise).

    Returns:
        (torch.Tensor): Correct tensor of shape(N,10) for 10 IoU thresholds.
    """
    # Dx10 matrix, where D - detections, 10 - IoU thresholds
    correct = np.zeros((pred_classes.shape[0], self.iouv.shape[0])).astype(bool)
    # LxD matrix where L - labels (rows), D - detections (columns)
    correct_class = true_classes[:, None] == pred_classes
    iou = iou * correct_class  # zero out the wrong classes
    iou = iou.cpu().numpy()
    for i, threshold in enumerate(self.iouv.cpu().tolist()):
        if use_scipy:
            # WARNING: known issue that reduces mAP in https://github.com/ultralytics/ultralytics/pull/4708
            import scipy  # scope import to avoid importing for all commands

            cost_matrix = iou * (iou >= threshold)
            if cost_matrix.any():
                labels_idx, detections_idx = scipy.optimize.linear_sum_assignment(cost_matrix, maximize=True)
                valid = cost_matrix[labels_idx, detections_idx] > 0
                if valid.any():
                    correct[detections_idx[valid], i] = True
        else:
            matches = np.nonzero(iou >= threshold)  # IoU > threshold and classes match
            matches = np.array(matches).T
            if matches.shape[0]:
                if matches.shape[0] > 1:
                    matches = matches[iou[matches[:, 0], matches[:, 1]].argsort()[::-1]]
                    matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
                    # matches = matches[matches[:, 2].argsort()[::-1]]
                    matches = matches[np.unique(matches[:, 0], return_index=True)[1]]
                correct[matches[:, 1].astype(int), i] = True
    return torch.tensor(correct, dtype=torch.bool, device=pred_classes.device)

on_plot(name, data=None)

рд░рдЬрд┐рд╕реНрдЯрд░, рднреВрдЦрдВрдб (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдХреЙрд▓рдмреИрдХ рдореЗрдВ рдЙрдкрднреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛)

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def on_plot(self, name, data=None):
    """Registers plots (e.g. to be consumed in callbacks)"""
    self.plots[Path(name)] = {"data": data, "timestamp": time.time()}

plot_predictions(batch, preds, ni)

рднреВрдЦрдВрдбреЛрдВ YOLO рдмреИрдЪ рдЫрд╡рд┐рдпреЛрдВ рдкрд░ рдореЙрдбрд▓ рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгрд┐рдпрд╛рдВред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def plot_predictions(self, batch, preds, ni):
    """Plots YOLO model predictions on batch images."""
    pass

plot_val_samples(batch, ni)

рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдХреЗ рджреМрд░рд╛рди рдкреНрд▓реЙрдЯ рд╕рддреНрдпрд╛рдкрди рдирдореВрдиреЗред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def plot_val_samples(self, batch, ni):
    """Plots validation samples during training."""
    pass

postprocess(preds)

'рдкреЛрд╕реНрдЯрдкреНрд░реЛрд╕реЗрд╕ ()' рдХреЗ рдЙрджреНрджреЗрд╢реНрдп рдХрд╛ рд╡рд░реНрдгрди рдФрд░ рд╕рд╛рд░рд╛рдВрд╢ рдХрд░рддрд╛ рд╣реИ рд▓реЗрдХрд┐рди рдХреЛрдИ рд╡рд┐рд╡рд░рдг рдирд╣реАрдВ рдмрддрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def postprocess(self, preds):
    """Describes and summarizes the purpose of 'postprocess()' but no details mentioned."""
    return preds

pred_to_json(preds, batch)

рдкреВрд░реНрд╡рд╛рдиреБрдорд╛рдиреЛрдВ рдХреЛ JSON рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдмрджрд▓реЗрдВред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def pred_to_json(self, preds, batch):
    """Convert predictions to JSON format."""
    pass

preprocess(batch)

рдПрдХ рдЗрдирдкреБрдЯ рдмреИрдЪ рдХреЛ рдкреНрд░реАрдкреНрд░реЛрд╕реЗрд╕ рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def preprocess(self, batch):
    """Preprocesses an input batch."""
    return batch

print_results()

рдореЙрдбрд▓ рдХреА рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгрд┐рдпреЛрдВ рдХреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЛ рдкреНрд░рд┐рдВрдЯ рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def print_results(self):
    """Prints the results of the model's predictions."""
    pass

run_callbacks(event)

рдХрд┐рд╕реА рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдИрд╡реЗрдВрдЯ рд╕реЗ рдЬреБрдбрд╝реЗ рд╕рднреА рдХреЙрд▓рдмреИрдХ рдЪрд▓рд╛рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def run_callbacks(self, event: str):
    """Runs all callbacks associated with a specified event."""
    for callback in self.callbacks.get(event, []):
        callback(self)

update_metrics(preds, batch)

рдкреВрд░реНрд╡рд╛рдиреБрдорд╛рдиреЛрдВ рдФрд░ рдмреИрдЪ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдореАрдЯреНрд░рд┐рдХ рдЕрдкрдбреЗрдЯ рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/engine/validator.py
def update_metrics(self, preds, batch):
    """Updates metrics based on predictions and batch."""
    pass





2023-11-12 рдмрдирд╛рдпрд╛ рдЧрдпрд╛, рдЕрдкрдбреЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ 2024-05-08
рд▓реЗрдЦрдХ: рдмреБрд░рд╣рд╛рди-рдХреНрдпреВ (1), рдЧреНрд▓реЗрди-рдЬреЛрдЪрд░ (3)