ultralytics 8.3.217__py3-none-any.whl → 8.3.218__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ultralytics/__init__.py +1 -1
 - ultralytics/data/build.py +106 -1
 - ultralytics/engine/trainer.py +37 -31
 - ultralytics/engine/validator.py +27 -9
 - ultralytics/models/yolo/classify/train.py +2 -15
 - ultralytics/models/yolo/classify/val.py +15 -1
 - ultralytics/models/yolo/detect/val.py +17 -1
 - {ultralytics-8.3.217.dist-info → ultralytics-8.3.218.dist-info}/METADATA +1 -1
 - {ultralytics-8.3.217.dist-info → ultralytics-8.3.218.dist-info}/RECORD +13 -13
 - {ultralytics-8.3.217.dist-info → ultralytics-8.3.218.dist-info}/WHEEL +0 -0
 - {ultralytics-8.3.217.dist-info → ultralytics-8.3.218.dist-info}/entry_points.txt +0 -0
 - {ultralytics-8.3.217.dist-info → ultralytics-8.3.218.dist-info}/licenses/LICENSE +0 -0
 - {ultralytics-8.3.217.dist-info → ultralytics-8.3.218.dist-info}/top_level.txt +0 -0
 
    
        ultralytics/__init__.py
    CHANGED
    
    
    
        ultralytics/data/build.py
    CHANGED
    
    | 
         @@ -2,6 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            from __future__ import annotations
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            import math
         
     | 
| 
       5 
6 
     | 
    
         
             
            import os
         
     | 
| 
       6 
7 
     | 
    
         
             
            import random
         
     | 
| 
       7 
8 
     | 
    
         
             
            from collections.abc import Iterator
         
     | 
| 
         @@ -11,6 +12,7 @@ from urllib.parse import urlsplit 
     | 
|
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
            import numpy as np
         
     | 
| 
       13 
14 
     | 
    
         
             
            import torch
         
     | 
| 
      
 15 
     | 
    
         
            +
            import torch.distributed as dist
         
     | 
| 
       14 
16 
     | 
    
         
             
            from PIL import Image
         
     | 
| 
       15 
17 
     | 
    
         
             
            from torch.utils.data import dataloader, distributed
         
     | 
| 
       16 
18 
     | 
    
         | 
| 
         @@ -112,6 +114,103 @@ class _RepeatSampler: 
     | 
|
| 
       112 
114 
     | 
    
         
             
                        yield from iter(self.sampler)
         
     | 
| 
       113 
115 
     | 
    
         | 
| 
       114 
116 
     | 
    
         | 
| 
      
 117 
     | 
    
         
            +
            class ContiguousDistributedSampler(torch.utils.data.Sampler):
         
     | 
| 
      
 118 
     | 
    
         
            +
                """
         
     | 
| 
      
 119 
     | 
    
         
            +
                Distributed sampler that assigns contiguous batch-aligned chunks of the dataset to each GPU.
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                Unlike PyTorch's DistributedSampler which distributes samples in a round-robin fashion (GPU 0 gets indices
         
     | 
| 
      
 122 
     | 
    
         
            +
                [0,2,4,...], GPU 1 gets [1,3,5,...]), this sampler gives each GPU contiguous batches of the dataset
         
     | 
| 
      
 123 
     | 
    
         
            +
                (GPU 0 gets batches [0,1,2,...], GPU 1 gets batches [k,k+1,...], etc.). This preserves any ordering or
         
     | 
| 
      
 124 
     | 
    
         
            +
                grouping in the original dataset, which is critical when samples are organized by similarity (e.g., images
         
     | 
| 
      
 125 
     | 
    
         
            +
                sorted by size to enable efficient batching without padding when using rect=True).
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                The sampler handles uneven batch counts by distributing remainder batches to the first few ranks, ensuring
         
     | 
| 
      
 128 
     | 
    
         
            +
                all samples are covered exactly once across all GPUs.
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 131 
     | 
    
         
            +
                    dataset (torch.utils.data.Dataset): Dataset to sample from. Must implement __len__.
         
     | 
| 
      
 132 
     | 
    
         
            +
                    num_replicas (int, optional): Number of distributed processes. Defaults to world size.
         
     | 
| 
      
 133 
     | 
    
         
            +
                    batch_size (int, optional): Batch size used by dataloader. Defaults to dataset batch size.
         
     | 
| 
      
 134 
     | 
    
         
            +
                    rank (int, optional): Rank of current process. Defaults to current rank.
         
     | 
| 
      
 135 
     | 
    
         
            +
                    shuffle (bool, optional): Whether to shuffle indices within each rank's chunk. Defaults to False.
         
     | 
| 
      
 136 
     | 
    
         
            +
                        When True, shuffling is deterministic and controlled by set_epoch() for reproducibility.
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                Example:
         
     | 
| 
      
 139 
     | 
    
         
            +
                    >>> # For validation with size-grouped images
         
     | 
| 
      
 140 
     | 
    
         
            +
                    >>> sampler = ContiguousDistributedSampler(val_dataset, batch_size=32, shuffle=False)
         
     | 
| 
      
 141 
     | 
    
         
            +
                    >>> loader = DataLoader(val_dataset, batch_size=32, sampler=sampler)
         
     | 
| 
      
 142 
     | 
    
         
            +
                    >>> # For training with shuffling
         
     | 
| 
      
 143 
     | 
    
         
            +
                    >>> sampler = ContiguousDistributedSampler(train_dataset, batch_size=32, shuffle=True)
         
     | 
| 
      
 144 
     | 
    
         
            +
                    >>> for epoch in range(num_epochs):
         
     | 
| 
      
 145 
     | 
    
         
            +
                    ...     sampler.set_epoch(epoch)
         
     | 
| 
      
 146 
     | 
    
         
            +
                    ...     for batch in loader:
         
     | 
| 
      
 147 
     | 
    
         
            +
                    ...         ...
         
     | 
| 
      
 148 
     | 
    
         
            +
                """
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                def __init__(self, dataset, num_replicas=None, batch_size=None, rank=None, shuffle=False):
         
     | 
| 
      
 151 
     | 
    
         
            +
                    """Initialize the sampler with dataset and distributed training parameters."""
         
     | 
| 
      
 152 
     | 
    
         
            +
                    if num_replicas is None:
         
     | 
| 
      
 153 
     | 
    
         
            +
                        num_replicas = dist.get_world_size() if dist.is_initialized() else 1
         
     | 
| 
      
 154 
     | 
    
         
            +
                    if rank is None:
         
     | 
| 
      
 155 
     | 
    
         
            +
                        rank = dist.get_rank() if dist.is_initialized() else 0
         
     | 
| 
      
 156 
     | 
    
         
            +
                    if batch_size is None:
         
     | 
| 
      
 157 
     | 
    
         
            +
                        batch_size = getattr(dataset, "batch_size", 1)
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                    self.dataset = dataset
         
     | 
| 
      
 160 
     | 
    
         
            +
                    self.num_replicas = num_replicas
         
     | 
| 
      
 161 
     | 
    
         
            +
                    self.batch_size = batch_size
         
     | 
| 
      
 162 
     | 
    
         
            +
                    self.rank = rank
         
     | 
| 
      
 163 
     | 
    
         
            +
                    self.epoch = 0
         
     | 
| 
      
 164 
     | 
    
         
            +
                    self.shuffle = shuffle
         
     | 
| 
      
 165 
     | 
    
         
            +
                    self.total_size = len(dataset)
         
     | 
| 
      
 166 
     | 
    
         
            +
                    self.num_batches = math.ceil(self.total_size / self.batch_size)
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                def _get_rank_indices(self):
         
     | 
| 
      
 169 
     | 
    
         
            +
                    """Calculate the start and end sample indices for this rank."""
         
     | 
| 
      
 170 
     | 
    
         
            +
                    # Calculate which batches this rank handles
         
     | 
| 
      
 171 
     | 
    
         
            +
                    batches_per_rank_base = self.num_batches // self.num_replicas
         
     | 
| 
      
 172 
     | 
    
         
            +
                    remainder = self.num_batches % self.num_replicas
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                    # This rank gets an extra batch if rank < remainder
         
     | 
| 
      
 175 
     | 
    
         
            +
                    batches_for_this_rank = batches_per_rank_base + (1 if self.rank < remainder else 0)
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                    # Calculate starting batch: base position + number of extra batches given to earlier ranks
         
     | 
| 
      
 178 
     | 
    
         
            +
                    start_batch = self.rank * batches_per_rank_base + min(self.rank, remainder)
         
     | 
| 
      
 179 
     | 
    
         
            +
                    end_batch = start_batch + batches_for_this_rank
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    # Convert batch indices to sample indices
         
     | 
| 
      
 182 
     | 
    
         
            +
                    start_idx = start_batch * self.batch_size
         
     | 
| 
      
 183 
     | 
    
         
            +
                    end_idx = min(end_batch * self.batch_size, self.total_size)
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                    return start_idx, end_idx
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                def __iter__(self):
         
     | 
| 
      
 188 
     | 
    
         
            +
                    """Generate indices for this rank's contiguous chunk of the dataset."""
         
     | 
| 
      
 189 
     | 
    
         
            +
                    start_idx, end_idx = self._get_rank_indices()
         
     | 
| 
      
 190 
     | 
    
         
            +
                    indices = list(range(start_idx, end_idx))
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                    if self.shuffle:
         
     | 
| 
      
 193 
     | 
    
         
            +
                        g = torch.Generator()
         
     | 
| 
      
 194 
     | 
    
         
            +
                        g.manual_seed(self.epoch)
         
     | 
| 
      
 195 
     | 
    
         
            +
                        indices = [indices[i] for i in torch.randperm(len(indices), generator=g).tolist()]
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                    return iter(indices)
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                def __len__(self):
         
     | 
| 
      
 200 
     | 
    
         
            +
                    """Return the number of samples in this rank's chunk."""
         
     | 
| 
      
 201 
     | 
    
         
            +
                    start_idx, end_idx = self._get_rank_indices()
         
     | 
| 
      
 202 
     | 
    
         
            +
                    return end_idx - start_idx
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
                def set_epoch(self, epoch):
         
     | 
| 
      
 205 
     | 
    
         
            +
                    """
         
     | 
| 
      
 206 
     | 
    
         
            +
                    Set the epoch for this sampler to ensure different shuffling patterns across epochs.
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 209 
     | 
    
         
            +
                        epoch (int): Epoch number to use as the random seed for shuffling.
         
     | 
| 
      
 210 
     | 
    
         
            +
                    """
         
     | 
| 
      
 211 
     | 
    
         
            +
                    self.epoch = epoch
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
       115 
214 
     | 
    
         
             
            def seed_worker(worker_id: int):  # noqa
         
     | 
| 
       116 
215 
     | 
    
         
             
                """Set dataloader worker seed for reproducibility across worker processes."""
         
     | 
| 
       117 
216 
     | 
    
         
             
                worker_seed = torch.initial_seed() % 2**32
         
     | 
| 
         @@ -213,7 +312,13 @@ def build_dataloader( 
     | 
|
| 
       213 
312 
     | 
    
         
             
                batch = min(batch, len(dataset))
         
     | 
| 
       214 
313 
     | 
    
         
             
                nd = torch.cuda.device_count()  # number of CUDA devices
         
     | 
| 
       215 
314 
     | 
    
         
             
                nw = min(os.cpu_count() // max(nd, 1), workers)  # number of workers
         
     | 
| 
       216 
     | 
    
         
            -
                sampler =  
     | 
| 
      
 315 
     | 
    
         
            +
                sampler = (
         
     | 
| 
      
 316 
     | 
    
         
            +
                    None
         
     | 
| 
      
 317 
     | 
    
         
            +
                    if rank == -1
         
     | 
| 
      
 318 
     | 
    
         
            +
                    else distributed.DistributedSampler(dataset, shuffle=shuffle)
         
     | 
| 
      
 319 
     | 
    
         
            +
                    if shuffle
         
     | 
| 
      
 320 
     | 
    
         
            +
                    else ContiguousDistributedSampler(dataset)
         
     | 
| 
      
 321 
     | 
    
         
            +
                )
         
     | 
| 
       217 
322 
     | 
    
         
             
                generator = torch.Generator()
         
     | 
| 
       218 
323 
     | 
    
         
             
                generator.manual_seed(6148914691236517205 + RANK)
         
     | 
| 
       219 
324 
     | 
    
         
             
                return InfiniteDataLoader(
         
     | 
    
        ultralytics/engine/trainer.py
    CHANGED
    
    | 
         @@ -318,18 +318,18 @@ class BaseTrainer: 
     | 
|
| 
       318 
318 
     | 
    
         
             
                    self.train_loader = self.get_dataloader(
         
     | 
| 
       319 
319 
     | 
    
         
             
                        self.data["train"], batch_size=batch_size, rank=LOCAL_RANK, mode="train"
         
     | 
| 
       320 
320 
     | 
    
         
             
                    )
         
     | 
| 
      
 321 
     | 
    
         
            +
                    # Note: When training DOTA dataset, double batch size could get OOM on images with >2000 objects.
         
     | 
| 
      
 322 
     | 
    
         
            +
                    self.test_loader = self.get_dataloader(
         
     | 
| 
      
 323 
     | 
    
         
            +
                        self.data.get("val") or self.data.get("test"),
         
     | 
| 
      
 324 
     | 
    
         
            +
                        batch_size=batch_size if self.args.task == "obb" else batch_size * 2,
         
     | 
| 
      
 325 
     | 
    
         
            +
                        rank=LOCAL_RANK,
         
     | 
| 
      
 326 
     | 
    
         
            +
                        mode="val",
         
     | 
| 
      
 327 
     | 
    
         
            +
                    )
         
     | 
| 
      
 328 
     | 
    
         
            +
                    self.validator = self.get_validator()
         
     | 
| 
      
 329 
     | 
    
         
            +
                    self.ema = ModelEMA(self.model)
         
     | 
| 
       321 
330 
     | 
    
         
             
                    if RANK in {-1, 0}:
         
     | 
| 
       322 
     | 
    
         
            -
                        # Note: When training DOTA dataset, double batch size could get OOM on images with >2000 objects.
         
     | 
| 
       323 
     | 
    
         
            -
                        self.test_loader = self.get_dataloader(
         
     | 
| 
       324 
     | 
    
         
            -
                            self.data.get("val") or self.data.get("test"),
         
     | 
| 
       325 
     | 
    
         
            -
                            batch_size=batch_size if self.args.task == "obb" else batch_size * 2,
         
     | 
| 
       326 
     | 
    
         
            -
                            rank=-1,
         
     | 
| 
       327 
     | 
    
         
            -
                            mode="val",
         
     | 
| 
       328 
     | 
    
         
            -
                        )
         
     | 
| 
       329 
     | 
    
         
            -
                        self.validator = self.get_validator()
         
     | 
| 
       330 
331 
     | 
    
         
             
                        metric_keys = self.validator.metrics.keys + self.label_loss_items(prefix="val")
         
     | 
| 
       331 
332 
     | 
    
         
             
                        self.metrics = dict(zip(metric_keys, [0] * len(metric_keys)))
         
     | 
| 
       332 
     | 
    
         
            -
                        self.ema = ModelEMA(self.model)
         
     | 
| 
       333 
333 
     | 
    
         
             
                        if self.args.plots:
         
     | 
| 
       334 
334 
     | 
    
         
             
                            self.plot_training_labels()
         
     | 
| 
       335 
335 
     | 
    
         | 
| 
         @@ -467,10 +467,10 @@ class BaseTrainer: 
     | 
|
| 
       467 
467 
     | 
    
         
             
                            final_epoch = epoch + 1 >= self.epochs
         
     | 
| 
       468 
468 
     | 
    
         
             
                            self.ema.update_attr(self.model, include=["yaml", "nc", "args", "names", "stride", "class_weights"])
         
     | 
| 
       469 
469 
     | 
    
         | 
| 
       470 
     | 
    
         
            -
             
     | 
| 
       471 
     | 
    
         
            -
             
     | 
| 
       472 
     | 
    
         
            -
             
     | 
| 
       473 
     | 
    
         
            -
             
     | 
| 
      
 470 
     | 
    
         
            +
                        # Validation
         
     | 
| 
      
 471 
     | 
    
         
            +
                        if self.args.val or final_epoch or self.stopper.possible_stop or self.stop:
         
     | 
| 
      
 472 
     | 
    
         
            +
                            self._clear_memory(threshold=0.5)  # prevent VRAM spike
         
     | 
| 
      
 473 
     | 
    
         
            +
                            self.metrics, self.fitness = self.validate()
         
     | 
| 
       474 
474 
     | 
    
         | 
| 
       475 
475 
     | 
    
         
             
                        # NaN recovery
         
     | 
| 
       476 
476 
     | 
    
         
             
                        if self._handle_nan_recovery(epoch):
         
     | 
| 
         @@ -510,11 +510,11 @@ class BaseTrainer: 
     | 
|
| 
       510 
510 
     | 
    
         
             
                            break  # must break all DDP ranks
         
     | 
| 
       511 
511 
     | 
    
         
             
                        epoch += 1
         
     | 
| 
       512 
512 
     | 
    
         | 
| 
      
 513 
     | 
    
         
            +
                    seconds = time.time() - self.train_time_start
         
     | 
| 
      
 514 
     | 
    
         
            +
                    LOGGER.info(f"\n{epoch - self.start_epoch + 1} epochs completed in {seconds / 3600:.3f} hours.")
         
     | 
| 
      
 515 
     | 
    
         
            +
                    # Do final val with best.pt
         
     | 
| 
      
 516 
     | 
    
         
            +
                    self.final_eval()
         
     | 
| 
       513 
517 
     | 
    
         
             
                    if RANK in {-1, 0}:
         
     | 
| 
       514 
     | 
    
         
            -
                        # Do final val with best.pt
         
     | 
| 
       515 
     | 
    
         
            -
                        seconds = time.time() - self.train_time_start
         
     | 
| 
       516 
     | 
    
         
            -
                        LOGGER.info(f"\n{epoch - self.start_epoch + 1} epochs completed in {seconds / 3600:.3f} hours.")
         
     | 
| 
       517 
     | 
    
         
            -
                        self.final_eval()
         
     | 
| 
       518 
518 
     | 
    
         
             
                        if self.args.plots:
         
     | 
| 
       519 
519 
     | 
    
         
             
                            self.plot_metrics()
         
     | 
| 
       520 
520 
     | 
    
         
             
                        self.run_callbacks("on_train_end")
         
     | 
| 
         @@ -695,7 +695,13 @@ class BaseTrainer: 
     | 
|
| 
       695 
695 
     | 
    
         
             
                        metrics (dict): Dictionary of validation metrics.
         
     | 
| 
       696 
696 
     | 
    
         
             
                        fitness (float): Fitness score for the validation.
         
     | 
| 
       697 
697 
     | 
    
         
             
                    """
         
     | 
| 
      
 698 
     | 
    
         
            +
                    if self.ema and self.world_size > 1:
         
     | 
| 
      
 699 
     | 
    
         
            +
                        # Sync EMA buffers from rank 0 to all ranks
         
     | 
| 
      
 700 
     | 
    
         
            +
                        for buffer in self.ema.ema.buffers():
         
     | 
| 
      
 701 
     | 
    
         
            +
                            dist.broadcast(buffer, src=0)
         
     | 
| 
       698 
702 
     | 
    
         
             
                    metrics = self.validator(self)
         
     | 
| 
      
 703 
     | 
    
         
            +
                    if metrics is None:
         
     | 
| 
      
 704 
     | 
    
         
            +
                        return None, None
         
     | 
| 
       699 
705 
     | 
    
         
             
                    fitness = metrics.pop("fitness", -self.loss.detach().cpu().numpy())  # use loss as fitness measure if not found
         
     | 
| 
       700 
706 
     | 
    
         
             
                    if not self.best_fitness or self.best_fitness < fitness:
         
     | 
| 
       701 
707 
     | 
    
         
             
                        self.best_fitness = fitness
         
     | 
| 
         @@ -768,20 +774,20 @@ class BaseTrainer: 
     | 
|
| 
       768 
774 
     | 
    
         | 
| 
       769 
775 
     | 
    
         
             
                def final_eval(self):
         
     | 
| 
       770 
776 
     | 
    
         
             
                    """Perform final evaluation and validation for object detection YOLO model."""
         
     | 
| 
       771 
     | 
    
         
            -
                     
     | 
| 
       772 
     | 
    
         
            -
                     
     | 
| 
       773 
     | 
    
         
            -
                        if  
     | 
| 
       774 
     | 
    
         
            -
                             
     | 
| 
       775 
     | 
    
         
            -
             
     | 
| 
       776 
     | 
    
         
            -
             
     | 
| 
       777 
     | 
    
         
            -
                                 
     | 
| 
       778 
     | 
    
         
            -
             
     | 
| 
       779 
     | 
    
         
            -
             
     | 
| 
       780 
     | 
    
         
            -
             
     | 
| 
       781 
     | 
    
         
            -
             
     | 
| 
       782 
     | 
    
         
            -
             
     | 
| 
       783 
     | 
    
         
            -
             
     | 
| 
       784 
     | 
    
         
            -
             
     | 
| 
      
 777 
     | 
    
         
            +
                    model = self.best if self.best.exists() else None
         
     | 
| 
      
 778 
     | 
    
         
            +
                    with torch_distributed_zero_first(LOCAL_RANK):  # strip only on GPU 0; other GPUs should wait
         
     | 
| 
      
 779 
     | 
    
         
            +
                        if RANK in {-1, 0}:
         
     | 
| 
      
 780 
     | 
    
         
            +
                            ckpt = strip_optimizer(self.last) if self.last.exists() else {}
         
     | 
| 
      
 781 
     | 
    
         
            +
                            if model:
         
     | 
| 
      
 782 
     | 
    
         
            +
                                # update best.pt train_metrics from last.pt
         
     | 
| 
      
 783 
     | 
    
         
            +
                                strip_optimizer(self.best, updates={"train_results": ckpt.get("train_results")})
         
     | 
| 
      
 784 
     | 
    
         
            +
                    if model:
         
     | 
| 
      
 785 
     | 
    
         
            +
                        LOGGER.info(f"\nValidating {model}...")
         
     | 
| 
      
 786 
     | 
    
         
            +
                        self.validator.args.plots = self.args.plots
         
     | 
| 
      
 787 
     | 
    
         
            +
                        self.validator.args.compile = False  # disable final val compile as too slow
         
     | 
| 
      
 788 
     | 
    
         
            +
                        self.metrics = self.validator(model=model)
         
     | 
| 
      
 789 
     | 
    
         
            +
                        self.metrics.pop("fitness", None)
         
     | 
| 
      
 790 
     | 
    
         
            +
                        self.run_callbacks("on_fit_epoch_end")
         
     | 
| 
       785 
791 
     | 
    
         | 
| 
       786 
792 
     | 
    
         
             
                def check_resume(self, overrides):
         
     | 
| 
       787 
793 
     | 
    
         
             
                    """Check if resume checkpoint exists and update arguments accordingly."""
         
     | 
    
        ultralytics/engine/validator.py
    CHANGED
    
    | 
         @@ -29,11 +29,12 @@ from pathlib import Path 
     | 
|
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
            import numpy as np
         
     | 
| 
       31 
31 
     | 
    
         
             
            import torch
         
     | 
| 
      
 32 
     | 
    
         
            +
            import torch.distributed as dist
         
     | 
| 
       32 
33 
     | 
    
         | 
| 
       33 
34 
     | 
    
         
             
            from ultralytics.cfg import get_cfg, get_save_dir
         
     | 
| 
       34 
35 
     | 
    
         
             
            from ultralytics.data.utils import check_cls_dataset, check_det_dataset
         
     | 
| 
       35 
36 
     | 
    
         
             
            from ultralytics.nn.autobackend import AutoBackend
         
     | 
| 
       36 
     | 
    
         
            -
            from ultralytics.utils import LOGGER, TQDM, callbacks, colorstr, emojis
         
     | 
| 
      
 37 
     | 
    
         
            +
            from ultralytics.utils import LOGGER, RANK, TQDM, callbacks, colorstr, emojis
         
     | 
| 
       37 
38 
     | 
    
         
             
            from ultralytics.utils.checks import check_imgsz
         
     | 
| 
       38 
39 
     | 
    
         
             
            from ultralytics.utils.ops import Profile
         
     | 
| 
       39 
40 
     | 
    
         
             
            from ultralytics.utils.torch_utils import attempt_compile, select_device, smart_inference_mode, unwrap_model
         
     | 
| 
         @@ -160,7 +161,7 @@ class BaseValidator: 
     | 
|
| 
       160 
161 
     | 
    
         
             
                        callbacks.add_integration_callbacks(self)
         
     | 
| 
       161 
162 
     | 
    
         
             
                        model = AutoBackend(
         
     | 
| 
       162 
163 
     | 
    
         
             
                            model=model or self.args.model,
         
     | 
| 
       163 
     | 
    
         
            -
                            device=select_device(self.args.device),
         
     | 
| 
      
 164 
     | 
    
         
            +
                            device=select_device(self.args.device) if RANK == -1 else torch.device("cuda", RANK),
         
     | 
| 
       164 
165 
     | 
    
         
             
                            dnn=self.args.dnn,
         
     | 
| 
       165 
166 
     | 
    
         
             
                            data=self.args.data,
         
     | 
| 
       166 
167 
     | 
    
         
             
                            fp16=self.args.half,
         
     | 
| 
         @@ -223,21 +224,34 @@ class BaseValidator: 
     | 
|
| 
       223 
224 
     | 
    
         
             
                            preds = self.postprocess(preds)
         
     | 
| 
       224 
225 
     | 
    
         | 
| 
       225 
226 
     | 
    
         
             
                        self.update_metrics(preds, batch)
         
     | 
| 
       226 
     | 
    
         
            -
                        if self.args.plots and batch_i < 3:
         
     | 
| 
      
 227 
     | 
    
         
            +
                        if self.args.plots and batch_i < 3 and RANK in {-1, 0}:
         
     | 
| 
       227 
228 
     | 
    
         
             
                            self.plot_val_samples(batch, batch_i)
         
     | 
| 
       228 
229 
     | 
    
         
             
                            self.plot_predictions(batch, preds, batch_i)
         
     | 
| 
       229 
230 
     | 
    
         | 
| 
       230 
231 
     | 
    
         
             
                        self.run_callbacks("on_val_batch_end")
         
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
       232 
     | 
    
         
            -
                     
     | 
| 
       233 
     | 
    
         
            -
                    self. 
     | 
| 
       234 
     | 
    
         
            -
                     
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
                    stats = {}
         
     | 
| 
      
 234 
     | 
    
         
            +
                    self.gather_stats()
         
     | 
| 
      
 235 
     | 
    
         
            +
                    if RANK in {-1, 0}:
         
     | 
| 
      
 236 
     | 
    
         
            +
                        stats = self.get_stats()
         
     | 
| 
      
 237 
     | 
    
         
            +
                        self.speed = dict(zip(self.speed.keys(), (x.t / len(self.dataloader.dataset) * 1e3 for x in dt)))
         
     | 
| 
      
 238 
     | 
    
         
            +
                        self.finalize_metrics()
         
     | 
| 
      
 239 
     | 
    
         
            +
                        self.print_results()
         
     | 
| 
      
 240 
     | 
    
         
            +
                        self.run_callbacks("on_val_end")
         
     | 
| 
      
 241 
     | 
    
         
            +
             
     | 
| 
       236 
242 
     | 
    
         
             
                    if self.training:
         
     | 
| 
       237 
243 
     | 
    
         
             
                        model.float()
         
     | 
| 
       238 
     | 
    
         
            -
                         
     | 
| 
      
 244 
     | 
    
         
            +
                        # Reduce loss across all GPUs
         
     | 
| 
      
 245 
     | 
    
         
            +
                        loss = self.loss.clone().detach()
         
     | 
| 
      
 246 
     | 
    
         
            +
                        if trainer.world_size > 1:
         
     | 
| 
      
 247 
     | 
    
         
            +
                            dist.reduce(loss, dst=0, op=dist.ReduceOp.AVG)
         
     | 
| 
      
 248 
     | 
    
         
            +
                        if RANK > 0:
         
     | 
| 
      
 249 
     | 
    
         
            +
                            return
         
     | 
| 
      
 250 
     | 
    
         
            +
                        results = {**stats, **trainer.label_loss_items(loss.cpu() / len(self.dataloader), prefix="val")}
         
     | 
| 
       239 
251 
     | 
    
         
             
                        return {k: round(float(v), 5) for k, v in results.items()}  # return results as 5 decimal place floats
         
     | 
| 
       240 
252 
     | 
    
         
             
                    else:
         
     | 
| 
      
 253 
     | 
    
         
            +
                        if RANK > 0:
         
     | 
| 
      
 254 
     | 
    
         
            +
                            return stats
         
     | 
| 
       241 
255 
     | 
    
         
             
                        LOGGER.info(
         
     | 
| 
       242 
256 
     | 
    
         
             
                            "Speed: {:.1f}ms preprocess, {:.1f}ms inference, {:.1f}ms loss, {:.1f}ms postprocess per image".format(
         
     | 
| 
       243 
257 
     | 
    
         
             
                                *tuple(self.speed.values())
         
     | 
| 
         @@ -336,6 +350,10 @@ class BaseValidator: 
     | 
|
| 
       336 
350 
     | 
    
         
             
                    """Return statistics about the model's performance."""
         
     | 
| 
       337 
351 
     | 
    
         
             
                    return {}
         
     | 
| 
       338 
352 
     | 
    
         | 
| 
      
 353 
     | 
    
         
            +
                def gather_stats(self):
         
     | 
| 
      
 354 
     | 
    
         
            +
                    """Gather statistics from all the GPUs during DDP training to GPU 0."""
         
     | 
| 
      
 355 
     | 
    
         
            +
                    pass
         
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
       339 
357 
     | 
    
         
             
                def print_results(self):
         
     | 
| 
       340 
358 
     | 
    
         
             
                    """Print the results of the model's predictions."""
         
     | 
| 
       341 
359 
     | 
    
         
             
                    pass
         
     | 
| 
         @@ -11,9 +11,9 @@ from ultralytics.data import ClassificationDataset, build_dataloader 
     | 
|
| 
       11 
11 
     | 
    
         
             
            from ultralytics.engine.trainer import BaseTrainer
         
     | 
| 
       12 
12 
     | 
    
         
             
            from ultralytics.models import yolo
         
     | 
| 
       13 
13 
     | 
    
         
             
            from ultralytics.nn.tasks import ClassificationModel
         
     | 
| 
       14 
     | 
    
         
            -
            from ultralytics.utils import DEFAULT_CFG,  
     | 
| 
      
 14 
     | 
    
         
            +
            from ultralytics.utils import DEFAULT_CFG, RANK
         
     | 
| 
       15 
15 
     | 
    
         
             
            from ultralytics.utils.plotting import plot_images
         
     | 
| 
       16 
     | 
    
         
            -
            from ultralytics.utils.torch_utils import is_parallel,  
     | 
| 
      
 16 
     | 
    
         
            +
            from ultralytics.utils.torch_utils import is_parallel, torch_distributed_zero_first
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
            class ClassificationTrainer(BaseTrainer):
         
     | 
| 
         @@ -194,19 +194,6 @@ class ClassificationTrainer(BaseTrainer): 
     | 
|
| 
       194 
194 
     | 
    
         
             
                    loss_items = [round(float(loss_items), 5)]
         
     | 
| 
       195 
195 
     | 
    
         
             
                    return dict(zip(keys, loss_items))
         
     | 
| 
       196 
196 
     | 
    
         | 
| 
       197 
     | 
    
         
            -
                def final_eval(self):
         
     | 
| 
       198 
     | 
    
         
            -
                    """Evaluate trained model and save validation results."""
         
     | 
| 
       199 
     | 
    
         
            -
                    for f in self.last, self.best:
         
     | 
| 
       200 
     | 
    
         
            -
                        if f.exists():
         
     | 
| 
       201 
     | 
    
         
            -
                            strip_optimizer(f)  # strip optimizers
         
     | 
| 
       202 
     | 
    
         
            -
                            if f is self.best:
         
     | 
| 
       203 
     | 
    
         
            -
                                LOGGER.info(f"\nValidating {f}...")
         
     | 
| 
       204 
     | 
    
         
            -
                                self.validator.args.data = self.args.data
         
     | 
| 
       205 
     | 
    
         
            -
                                self.validator.args.plots = self.args.plots
         
     | 
| 
       206 
     | 
    
         
            -
                                self.metrics = self.validator(model=f)
         
     | 
| 
       207 
     | 
    
         
            -
                                self.metrics.pop("fitness", None)
         
     | 
| 
       208 
     | 
    
         
            -
                                self.run_callbacks("on_fit_epoch_end")
         
     | 
| 
       209 
     | 
    
         
            -
             
     | 
| 
       210 
197 
     | 
    
         
             
                def plot_training_samples(self, batch: dict[str, torch.Tensor], ni: int):
         
     | 
| 
       211 
198 
     | 
    
         
             
                    """
         
     | 
| 
       212 
199 
     | 
    
         
             
                    Plot training samples with their annotations.
         
     | 
| 
         @@ -6,10 +6,11 @@ from pathlib import Path 
     | 
|
| 
       6 
6 
     | 
    
         
             
            from typing import Any
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            import torch
         
     | 
| 
      
 9 
     | 
    
         
            +
            import torch.distributed as dist
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
11 
     | 
    
         
             
            from ultralytics.data import ClassificationDataset, build_dataloader
         
     | 
| 
       11 
12 
     | 
    
         
             
            from ultralytics.engine.validator import BaseValidator
         
     | 
| 
       12 
     | 
    
         
            -
            from ultralytics.utils import LOGGER
         
     | 
| 
      
 13 
     | 
    
         
            +
            from ultralytics.utils import LOGGER, RANK
         
     | 
| 
       13 
14 
     | 
    
         
             
            from ultralytics.utils.metrics import ClassifyMetrics, ConfusionMatrix
         
     | 
| 
       14 
15 
     | 
    
         
             
            from ultralytics.utils.plotting import plot_images
         
     | 
| 
       15 
16 
     | 
    
         | 
| 
         @@ -142,6 +143,19 @@ class ClassificationValidator(BaseValidator): 
     | 
|
| 
       142 
143 
     | 
    
         
             
                    self.metrics.process(self.targets, self.pred)
         
     | 
| 
       143 
144 
     | 
    
         
             
                    return self.metrics.results_dict
         
     | 
| 
       144 
145 
     | 
    
         | 
| 
      
 146 
     | 
    
         
            +
                def gather_stats(self) -> None:
         
     | 
| 
      
 147 
     | 
    
         
            +
                    """Gather stats from all GPUs."""
         
     | 
| 
      
 148 
     | 
    
         
            +
                    if RANK == 0:
         
     | 
| 
      
 149 
     | 
    
         
            +
                        gathered_preds = [None] * dist.get_world_size()
         
     | 
| 
      
 150 
     | 
    
         
            +
                        gathered_targets = [None] * dist.get_world_size()
         
     | 
| 
      
 151 
     | 
    
         
            +
                        dist.gather_object(self.pred, gathered_preds, dst=0)
         
     | 
| 
      
 152 
     | 
    
         
            +
                        dist.gather_object(self.targets, gathered_targets, dst=0)
         
     | 
| 
      
 153 
     | 
    
         
            +
                        self.pred = [pred for rank in gathered_preds for pred in rank]
         
     | 
| 
      
 154 
     | 
    
         
            +
                        self.targets = [targets for rank in gathered_targets for targets in rank]
         
     | 
| 
      
 155 
     | 
    
         
            +
                    elif RANK > 0:
         
     | 
| 
      
 156 
     | 
    
         
            +
                        dist.gather_object(self.pred, None, dst=0)
         
     | 
| 
      
 157 
     | 
    
         
            +
                        dist.gather_object(self.targets, None, dst=0)
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
       145 
159 
     | 
    
         
             
                def build_dataset(self, img_path: str) -> ClassificationDataset:
         
     | 
| 
       146 
160 
     | 
    
         
             
                    """Create a ClassificationDataset instance for validation."""
         
     | 
| 
       147 
161 
     | 
    
         
             
                    return ClassificationDataset(root=img_path, args=self.args, augment=False, prefix=self.args.split)
         
     | 
| 
         @@ -8,10 +8,11 @@ from typing import Any 
     | 
|
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            import numpy as np
         
     | 
| 
       10 
10 
     | 
    
         
             
            import torch
         
     | 
| 
      
 11 
     | 
    
         
            +
            import torch.distributed as dist
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
            from ultralytics.data import build_dataloader, build_yolo_dataset, converter
         
     | 
| 
       13 
14 
     | 
    
         
             
            from ultralytics.engine.validator import BaseValidator
         
     | 
| 
       14 
     | 
    
         
            -
            from ultralytics.utils import LOGGER, nms, ops
         
     | 
| 
      
 15 
     | 
    
         
            +
            from ultralytics.utils import LOGGER, RANK, nms, ops
         
     | 
| 
       15 
16 
     | 
    
         
             
            from ultralytics.utils.checks import check_requirements
         
     | 
| 
       16 
17 
     | 
    
         
             
            from ultralytics.utils.metrics import ConfusionMatrix, DetMetrics, box_iou
         
     | 
| 
       17 
18 
     | 
    
         
             
            from ultralytics.utils.plotting import plot_images
         
     | 
| 
         @@ -226,6 +227,21 @@ class DetectionValidator(BaseValidator): 
     | 
|
| 
       226 
227 
     | 
    
         
             
                    self.metrics.confusion_matrix = self.confusion_matrix
         
     | 
| 
       227 
228 
     | 
    
         
             
                    self.metrics.save_dir = self.save_dir
         
     | 
| 
       228 
229 
     | 
    
         | 
| 
      
 230 
     | 
    
         
            +
                def gather_stats(self) -> None:
         
     | 
| 
      
 231 
     | 
    
         
            +
                    """Gather stats from all GPUs."""
         
     | 
| 
      
 232 
     | 
    
         
            +
                    if RANK == 0:
         
     | 
| 
      
 233 
     | 
    
         
            +
                        gathered_stats = [None] * dist.get_world_size()
         
     | 
| 
      
 234 
     | 
    
         
            +
                        dist.gather_object(self.metrics.stats, gathered_stats, dst=0)
         
     | 
| 
      
 235 
     | 
    
         
            +
                        merged_stats = {key: [] for key in self.metrics.stats.keys()}
         
     | 
| 
      
 236 
     | 
    
         
            +
                        for stats_dict in gathered_stats:
         
     | 
| 
      
 237 
     | 
    
         
            +
                            for key in merged_stats.keys():
         
     | 
| 
      
 238 
     | 
    
         
            +
                                merged_stats[key].extend(stats_dict[key])
         
     | 
| 
      
 239 
     | 
    
         
            +
                        self.metrics.stats = merged_stats
         
     | 
| 
      
 240 
     | 
    
         
            +
                        self.seen = len(self.dataloader.dataset)  # total image count from dataset
         
     | 
| 
      
 241 
     | 
    
         
            +
                    elif RANK > 0:
         
     | 
| 
      
 242 
     | 
    
         
            +
                        dist.gather_object(self.metrics.stats, None, dst=0)
         
     | 
| 
      
 243 
     | 
    
         
            +
                        self.metrics.clear_stats()
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
       229 
245 
     | 
    
         
             
                def get_stats(self) -> dict[str, Any]:
         
     | 
| 
       230 
246 
     | 
    
         
             
                    """
         
     | 
| 
       231 
247 
     | 
    
         
             
                    Calculate and return metrics statistics.
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.4
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: ultralytics
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 8.3. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 8.3.218
         
     | 
| 
       4 
4 
     | 
    
         
             
            Summary: Ultralytics YOLO 🚀 for SOTA object detection, multi-object tracking, instance segmentation, pose estimation and image classification.
         
     | 
| 
       5 
5 
     | 
    
         
             
            Author-email: Glenn Jocher <glenn.jocher@ultralytics.com>, Jing Qiu <jing.qiu@ultralytics.com>
         
     | 
| 
       6 
6 
     | 
    
         
             
            Maintainer-email: Ultralytics <hello@ultralytics.com>
         
     | 
| 
         @@ -7,7 +7,7 @@ tests/test_exports.py,sha256=3o-qqPrPqjD1a_U6KBvwAusZ_Wy6S1WzmuvgRRUXmcA,11099 
     | 
|
| 
       7 
7 
     | 
    
         
             
            tests/test_integrations.py,sha256=ehRcYMpGvUI3KvgsaT1pkN1rXkr7tDSlYYMqIcXyGbg,6220
         
     | 
| 
       8 
8 
     | 
    
         
             
            tests/test_python.py,sha256=x2q5Wx3eOl32ymmr_4p6srz7ebO-O8zFttuerys_OWg,28083
         
     | 
| 
       9 
9 
     | 
    
         
             
            tests/test_solutions.py,sha256=oaTz5BttPDIeHkQh9oEaw-O73L4iYDP3Lfe82V7DeKM,13416
         
     | 
| 
       10 
     | 
    
         
            -
            ultralytics/__init__.py,sha256 
     | 
| 
      
 10 
     | 
    
         
            +
            ultralytics/__init__.py,sha256=-cmy8wbhEXQSozdsSFrklvknpsp0kac_c0ubp8T_wSc,1302
         
     | 
| 
       11 
11 
     | 
    
         
             
            ultralytics/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
         
     | 
| 
       12 
12 
     | 
    
         
             
            ultralytics/assets/bus.jpg,sha256=wCAZxJecGR63Od3ZRERe9Aja1Weayrb9Ug751DS_vGM,137419
         
     | 
| 
       13 
13 
     | 
    
         
             
            ultralytics/assets/zidane.jpg,sha256=Ftc4aeMmen1O0A3o6GCDO9FlfBslLpTAw0gnetx7bts,50427
         
     | 
| 
         @@ -109,7 +109,7 @@ ultralytics/data/__init__.py,sha256=nAXaL1puCc7z_NjzQNlJnhbVhT9Fla2u7Dsqo7q1dAc, 
     | 
|
| 
       109 
109 
     | 
    
         
             
            ultralytics/data/annotator.py,sha256=f15TCDEM8SuuzHiFB8oyhTy9vfywKmPTLSPAgsZQP9I,2990
         
     | 
| 
       110 
110 
     | 
    
         
             
            ultralytics/data/augment.py,sha256=7NsRCYu_uM6KkpU0F03NC9Ra_GQVGp2dRO1RksrrU38,132897
         
     | 
| 
       111 
111 
     | 
    
         
             
            ultralytics/data/base.py,sha256=gWoGFifyNe1TCwtGdGp5jzKOQ9sh4b-XrfyN0PPvRaY,19661
         
     | 
| 
       112 
     | 
    
         
            -
            ultralytics/data/build.py,sha256= 
     | 
| 
      
 112 
     | 
    
         
            +
            ultralytics/data/build.py,sha256=m9sM-W9DKalKr2JLC4BjAvb3FvPfkMfesihITE9GONs,16696
         
     | 
| 
       113 
113 
     | 
    
         
             
            ultralytics/data/converter.py,sha256=HMJ5H7nvHkeeSYNEwcWrSDkPJykVVg3kLmTC_V8adqg,31967
         
     | 
| 
       114 
114 
     | 
    
         
             
            ultralytics/data/dataset.py,sha256=GL6J_fvluaF2Ck1in3W5q3Xm7lRcUd6Amgd_uu6r_FM,36772
         
     | 
| 
       115 
115 
     | 
    
         
             
            ultralytics/data/loaders.py,sha256=sfQ0C86uBg9QQbN3aU0W8FIjGQmMdJTQAMK4DA1bjk8,31748
         
     | 
| 
         @@ -125,9 +125,9 @@ ultralytics/engine/exporter.py,sha256=LnxviDE4kHklCYpef8IEmDOteeSibGLLjX35g9vICy 
     | 
|
| 
       125 
125 
     | 
    
         
             
            ultralytics/engine/model.py,sha256=uX6cTFdlLllGRbz8Lr90IZGb4OrtMDIHQEg7DxUqwe8,53449
         
     | 
| 
       126 
126 
     | 
    
         
             
            ultralytics/engine/predictor.py,sha256=4lfw2RbBDE7939011FcSCuznscrcnMuabZtc8GXaKO4,22735
         
     | 
| 
       127 
127 
     | 
    
         
             
            ultralytics/engine/results.py,sha256=oHQdV_eIMvAU2qLCV7wG7iLifdfaLEgP80lDXB5ghkg,71490
         
     | 
| 
       128 
     | 
    
         
            -
            ultralytics/engine/trainer.py,sha256= 
     | 
| 
      
 128 
     | 
    
         
            +
            ultralytics/engine/trainer.py,sha256=TCn_xzQG3r6XFZp3lCGhl9msLLASS2vK-T_O6DqdV8Y,44097
         
     | 
| 
       129 
129 
     | 
    
         
             
            ultralytics/engine/tuner.py,sha256=8uiZ9DSYdjHmbhfiuzbMPw--1DLS3cpfZPeSzJ9dGEA,21664
         
     | 
| 
       130 
     | 
    
         
            -
            ultralytics/engine/validator.py,sha256= 
     | 
| 
      
 130 
     | 
    
         
            +
            ultralytics/engine/validator.py,sha256=HoBE5Lc7Ro7IQ5TlfDC94L5BmdOb8J8yfGlELozZ6EM,17560
         
     | 
| 
       131 
131 
     | 
    
         
             
            ultralytics/hub/__init__.py,sha256=xCF02lzlPKbdmGfO3NxLuXl5Kb0MaBZp_-fAWDHZ8zw,6698
         
     | 
| 
       132 
132 
     | 
    
         
             
            ultralytics/hub/auth.py,sha256=RIwZDWfW6vS2yGpZKR0xVl0-38itJYEFtmqY_M70bl8,6304
         
     | 
| 
       133 
133 
     | 
    
         
             
            ultralytics/hub/session.py,sha256=1o9vdd_fvPUHQ5oZgljtPePuPMUalIoXqOvE7Sdmd2o,18450
         
     | 
| 
         @@ -169,12 +169,12 @@ ultralytics/models/yolo/__init__.py,sha256=or0j5xvcM0usMlsFTYhNAOcQUri7reD0cD9JR 
     | 
|
| 
       169 
169 
     | 
    
         
             
            ultralytics/models/yolo/model.py,sha256=PH8nXl0ZulgjWMr9M-XAK2TcdaBNXX5AzofIhcKbTQ0,18840
         
     | 
| 
       170 
170 
     | 
    
         
             
            ultralytics/models/yolo/classify/__init__.py,sha256=9--HVaNOfI1K7rn_rRqclL8FUAnpfeBrRqEQIaQw2xM,383
         
     | 
| 
       171 
171 
     | 
    
         
             
            ultralytics/models/yolo/classify/predict.py,sha256=o7pDE8xwjkHUUIIOph7ZVQZyGZyob24dYDQ460v_7R0,4149
         
     | 
| 
       172 
     | 
    
         
            -
            ultralytics/models/yolo/classify/train.py,sha256= 
     | 
| 
       173 
     | 
    
         
            -
            ultralytics/models/yolo/classify/val.py,sha256= 
     | 
| 
      
 172 
     | 
    
         
            +
            ultralytics/models/yolo/classify/train.py,sha256=9o-UDqiLZwL2fzYAenAgInngZhiIMj0mVWAfa490jbw,9028
         
     | 
| 
      
 173 
     | 
    
         
            +
            ultralytics/models/yolo/classify/val.py,sha256=VbjlFQf219gFGxu0Gx0PYH2v31c4HxvM4BnH5AqWzOE,10828
         
     | 
| 
       174 
174 
     | 
    
         
             
            ultralytics/models/yolo/detect/__init__.py,sha256=GIRsLYR-kT4JJx7lh4ZZAFGBZj0aebokuU0A7JbjDVA,257
         
     | 
| 
       175 
175 
     | 
    
         
             
            ultralytics/models/yolo/detect/predict.py,sha256=Vtpqb2gHI7hv9TaBBXsnoScQ8HrSnj0PPOkEu07MwLc,5394
         
     | 
| 
       176 
176 
     | 
    
         
             
            ultralytics/models/yolo/detect/train.py,sha256=rnmCt0TG5bdySE2TVUsUqwyyF_LTy4dZdlACoM1MhcU,10554
         
     | 
| 
       177 
     | 
    
         
            -
            ultralytics/models/yolo/detect/val.py,sha256= 
     | 
| 
      
 177 
     | 
    
         
            +
            ultralytics/models/yolo/detect/val.py,sha256=WcinU6-Kj7K9gqziqEoGLNtzTXtm3OIPOHIRVE2Lf9U,22213
         
     | 
| 
       178 
178 
     | 
    
         
             
            ultralytics/models/yolo/obb/__init__.py,sha256=tQmpG8wVHsajWkZdmD6cjGohJ4ki64iSXQT8JY_dydo,221
         
     | 
| 
       179 
179 
     | 
    
         
             
            ultralytics/models/yolo/obb/predict.py,sha256=4r1eSld6TNJlk9JG56e-DX6oPL8uBBqiuztyBpxWlHE,2888
         
     | 
| 
       180 
180 
     | 
    
         
             
            ultralytics/models/yolo/obb/train.py,sha256=BbehrsKP0lHRV3v7rrw8wAeiDdc-szbhHAmDy0OdhoM,3461
         
     | 
| 
         @@ -275,9 +275,9 @@ ultralytics/utils/callbacks/tensorboard.py,sha256=_4nfGK1dDLn6ijpvphBDhc-AS8qhS3 
     | 
|
| 
       275 
275 
     | 
    
         
             
            ultralytics/utils/callbacks/wb.py,sha256=ngQO8EJ1kxJDF1YajScVtzBbm26jGuejA0uWeOyvf5A,7685
         
     | 
| 
       276 
276 
     | 
    
         
             
            ultralytics/utils/export/__init__.py,sha256=eZg5z2I61k8H0ykQLc22HhKwFRsLxwuSlDVMuUlYXfU,10023
         
     | 
| 
       277 
277 
     | 
    
         
             
            ultralytics/utils/export/imx.py,sha256=Jl5nuNxqaP_bY5yrV2NypmoJSrexHE71TxR72SDdjcg,11394
         
     | 
| 
       278 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       279 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       280 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       281 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       282 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       283 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
      
 278 
     | 
    
         
            +
            ultralytics-8.3.218.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
         
     | 
| 
      
 279 
     | 
    
         
            +
            ultralytics-8.3.218.dist-info/METADATA,sha256=sbgNoHw9u1XoAtvRMhsiQSLKs0gwmH0dfX6jTwC9JFs,37667
         
     | 
| 
      
 280 
     | 
    
         
            +
            ultralytics-8.3.218.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
         
     | 
| 
      
 281 
     | 
    
         
            +
            ultralytics-8.3.218.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
         
     | 
| 
      
 282 
     | 
    
         
            +
            ultralytics-8.3.218.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
         
     | 
| 
      
 283 
     | 
    
         
            +
            ultralytics-8.3.218.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |