ultralytics 8.3.216__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.

Potentially problematic release.


This version of ultralytics might be problematic. Click here for more details.

ultralytics/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
- __version__ = "8.3.216"
3
+ __version__ = "8.3.218"
4
4
 
5
5
  import importlib
6
6
  import os
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
@@ -181,7 +280,15 @@ def build_grounding(
181
280
  )
182
281
 
183
282
 
184
- def build_dataloader(dataset, batch: int, workers: int, shuffle: bool = True, rank: int = -1, drop_last: bool = False):
283
+ def build_dataloader(
284
+ dataset,
285
+ batch: int,
286
+ workers: int,
287
+ shuffle: bool = True,
288
+ rank: int = -1,
289
+ drop_last: bool = False,
290
+ pin_memory: bool = True,
291
+ ):
185
292
  """
186
293
  Create and return an InfiniteDataLoader or DataLoader for training or validation.
187
294
 
@@ -192,6 +299,7 @@ def build_dataloader(dataset, batch: int, workers: int, shuffle: bool = True, ra
192
299
  shuffle (bool, optional): Whether to shuffle the dataset.
193
300
  rank (int, optional): Process rank in distributed training. -1 for single-GPU training.
194
301
  drop_last (bool, optional): Whether to drop the last incomplete batch.
302
+ pin_memory (bool, optional): Whether to use pinned memory for dataloader.
195
303
 
196
304
  Returns:
197
305
  (InfiniteDataLoader): A dataloader that can be used for training or validation.
@@ -204,7 +312,13 @@ def build_dataloader(dataset, batch: int, workers: int, shuffle: bool = True, ra
204
312
  batch = min(batch, len(dataset))
205
313
  nd = torch.cuda.device_count() # number of CUDA devices
206
314
  nw = min(os.cpu_count() // max(nd, 1), workers) # number of workers
207
- sampler = None if rank == -1 else distributed.DistributedSampler(dataset, shuffle=shuffle)
315
+ sampler = (
316
+ None
317
+ if rank == -1
318
+ else distributed.DistributedSampler(dataset, shuffle=shuffle)
319
+ if shuffle
320
+ else ContiguousDistributedSampler(dataset)
321
+ )
208
322
  generator = torch.Generator()
209
323
  generator.manual_seed(6148914691236517205 + RANK)
210
324
  return InfiniteDataLoader(
@@ -214,7 +328,7 @@ def build_dataloader(dataset, batch: int, workers: int, shuffle: bool = True, ra
214
328
  num_workers=nw,
215
329
  sampler=sampler,
216
330
  prefetch_factor=4 if nw > 0 else None, # increase over default 2
217
- pin_memory=nd > 0,
331
+ pin_memory=nd > 0 and pin_memory,
218
332
  collate_fn=getattr(dataset, "collate_fn", None),
219
333
  worker_init_fn=seed_worker,
220
334
  generator=generator,
@@ -527,7 +527,7 @@ class Results(SimpleClass, DataExportMixin):
527
527
  """
528
528
  assert color_mode in {"instance", "class"}, f"Expected color_mode='instance' or 'class', not {color_mode}."
529
529
  if img is None and isinstance(self.orig_img, torch.Tensor):
530
- img = (self.orig_img[0].detach().permute(1, 2, 0).contiguous() * 255).to(torch.uint8).cpu().numpy()
530
+ img = (self.orig_img[0].detach().permute(1, 2, 0).contiguous() * 255).byte().cpu().numpy()
531
531
 
532
532
  names = self.names
533
533
  is_obb = self.obb is not None
@@ -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
- # 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()
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
- ckpt = {}
772
- for f in self.last, self.best:
773
- if f.exists():
774
- if f is self.last:
775
- ckpt = strip_optimizer(f)
776
- elif f is self.best:
777
- k = "train_results" # update best.pt train_metrics from last.pt
778
- strip_optimizer(f, updates={k: ckpt[k]} if k in ckpt else None)
779
- LOGGER.info(f"\nValidating {f}...")
780
- self.validator.args.plots = self.args.plots
781
- self.validator.args.compile = False # disable final val compile as too slow
782
- self.metrics = self.validator(model=f)
783
- self.metrics.pop("fitness", None)
784
- self.run_callbacks("on_fit_epoch_end")
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."""
@@ -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
- stats = self.get_stats()
232
- self.speed = dict(zip(self.speed.keys(), (x.t / len(self.dataloader.dataset) * 1e3 for x in dt)))
233
- self.finalize_metrics()
234
- self.print_results()
235
- self.run_callbacks("on_val_end")
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
- results = {**stats, **trainer.label_loss_items(self.loss.cpu() / len(self.dataloader), prefix="val")}
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, LOGGER, RANK
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, strip_optimizer, torch_distributed_zero_first
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.
@@ -300,7 +316,13 @@ class DetectionValidator(BaseValidator):
300
316
  """
301
317
  dataset = self.build_dataset(dataset_path, batch=batch_size, mode="val")
302
318
  return build_dataloader(
303
- dataset, batch_size, self.args.workers, shuffle=False, rank=-1, drop_last=self.args.compile
319
+ dataset,
320
+ batch_size,
321
+ self.args.workers,
322
+ shuffle=False,
323
+ rank=-1,
324
+ drop_last=self.args.compile,
325
+ pin_memory=self.training,
304
326
  )
305
327
 
306
328
  def plot_val_samples(self, batch: dict[str, Any], ni: int) -> None:
@@ -173,7 +173,7 @@ class SegmentationValidator(DetectionValidator):
173
173
  if gt_cls.shape[0] == 0 or preds["cls"].shape[0] == 0:
174
174
  tp_m = np.zeros((preds["cls"].shape[0], self.niou), dtype=bool)
175
175
  else:
176
- iou = mask_iou(batch["masks"].flatten(1), preds["masks"].flatten(1))
176
+ iou = mask_iou(batch["masks"].flatten(1), preds["masks"].flatten(1).float()) # float, uint8
177
177
  tp_m = self.match_predictions(preds["cls"], gt_cls, iou).cpu().numpy()
178
178
  tp.update({"tp_m": tp_m}) # update tp with mask IoU
179
179
  return tp
@@ -19,6 +19,7 @@ from PIL import Image
19
19
  from ultralytics.utils import ARM64, IS_JETSON, LINUX, LOGGER, PYTHON_VERSION, ROOT, YAML, is_jetson
20
20
  from ultralytics.utils.checks import check_requirements, check_suffix, check_version, check_yaml, is_rockchip
21
21
  from ultralytics.utils.downloads import attempt_download_asset, is_url
22
+ from ultralytics.utils.nms import non_max_suppression
22
23
 
23
24
 
24
25
  def check_class_names(names: list | dict) -> dict[int, str]:
@@ -854,7 +855,10 @@ class AutoBackend(nn.Module):
854
855
  if any(warmup_types) and (self.device.type != "cpu" or self.triton):
855
856
  im = torch.empty(*imgsz, dtype=torch.half if self.fp16 else torch.float, device=self.device) # input
856
857
  for _ in range(2 if self.jit else 1):
857
- self.forward(im) # warmup
858
+ self.forward(im) # warmup model
859
+ warmup_boxes = torch.rand(1, 84, 16, device=self.device) # 16 boxes works best empirically
860
+ warmup_boxes[:, :4] *= imgsz[-1]
861
+ non_max_suppression(warmup_boxes) # warmup NMS
858
862
 
859
863
  @staticmethod
860
864
  def _model_type(p: str = "path/to/model.pt") -> list[bool]:
ultralytics/utils/ops.py CHANGED
@@ -557,7 +557,7 @@ def process_mask(protos, masks_in, bboxes, shape, upsample: bool = False):
557
557
  masks = crop_mask(masks, boxes=bboxes * ratios) # CHW
558
558
  if upsample:
559
559
  masks = F.interpolate(masks[None], shape, mode="bilinear")[0] # CHW
560
- return masks.gt_(0.0)
560
+ return masks.gt_(0.0).byte()
561
561
 
562
562
 
563
563
  def process_mask_native(protos, masks_in, bboxes, shape):
@@ -577,7 +577,7 @@ def process_mask_native(protos, masks_in, bboxes, shape):
577
577
  masks = (masks_in @ protos.float().view(c, -1)).view(-1, mh, mw)
578
578
  masks = scale_masks(masks[None], shape)[0] # CHW
579
579
  masks = crop_mask(masks, bboxes) # CHW
580
- return masks.gt_(0.0)
580
+ return masks.gt_(0.0).byte()
581
581
 
582
582
 
583
583
  def scale_masks(masks, shape, padding: bool = True):
@@ -674,7 +674,7 @@ def masks2segments(masks, strategy: str = "all"):
674
674
  from ultralytics.data.converter import merge_multi_segment
675
675
 
676
676
  segments = []
677
- for x in masks.int().cpu().numpy().astype("uint8"):
677
+ for x in masks.byte().cpu().numpy():
678
678
  c = cv2.findContours(x, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
679
679
  if c:
680
680
  if strategy == "all": # merge and concatenate all segments
@@ -701,7 +701,7 @@ def convert_torch2numpy_batch(batch: torch.Tensor) -> np.ndarray:
701
701
  Returns:
702
702
  (np.ndarray): Output NumPy array batch with shape (Batch, Height, Width, Channels) and dtype uint8.
703
703
  """
704
- return (batch.permute(0, 2, 3, 1).contiguous() * 255).clamp(0, 255).to(torch.uint8).cpu().numpy()
704
+ return (batch.permute(0, 2, 3, 1).contiguous() * 255).clamp(0, 255).byte().cpu().numpy()
705
705
 
706
706
 
707
707
  def clean_str(s):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ultralytics
3
- Version: 8.3.216
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=4O9rGwsIt8uAjy51S1RIM1b1J9H4mog1w1OsM7XU058,1302
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=cdhD1Z4Gv9KLi5n9OchDRBH8rfMQ1NyDja_D7DmAS00,11879
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
@@ -124,10 +124,10 @@ ultralytics/engine/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6DXppv1-QU
124
124
  ultralytics/engine/exporter.py,sha256=LnxviDE4kHklCYpef8IEmDOteeSibGLLjX35g9vICyw,71584
125
125
  ultralytics/engine/model.py,sha256=uX6cTFdlLllGRbz8Lr90IZGb4OrtMDIHQEg7DxUqwe8,53449
126
126
  ultralytics/engine/predictor.py,sha256=4lfw2RbBDE7939011FcSCuznscrcnMuabZtc8GXaKO4,22735
127
- ultralytics/engine/results.py,sha256=uQ_tgvdxKAg28pRgb5WCHiqx9Ktu7wYiVbwZy_IJ5bo,71499
128
- ultralytics/engine/trainer.py,sha256=URv3-BKeipw0Szl1xrnTH5cCIU3_SA10mx89GSA7Vs4,43832
127
+ ultralytics/engine/results.py,sha256=oHQdV_eIMvAU2qLCV7wG7iLifdfaLEgP80lDXB5ghkg,71490
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=s7cKMqj2HgVm-GL9bUc76QBeue2jb4cKPk-uQQG5nck,16949
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=juAdpi0wIsnleACkq9Rct9io-Gr1A4gG511VqIUvu8E,9656
173
- ultralytics/models/yolo/classify/val.py,sha256=FUTTrvIMlFxdJm8dlrsguKsDvfRdDtGNlIMdJ_-PMtE,10134
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=yWzaimDaR6pvGX4hIy5ytaqKy8Qo-B7w7hJPavMmVNg,21351
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
@@ -186,7 +186,7 @@ ultralytics/models/yolo/pose/val.py,sha256=MK-GueXmXrl7eZ5WHYjJMghE4AYJTEut7AuS-
186
186
  ultralytics/models/yolo/segment/__init__.py,sha256=3IThhZ1wlkY9FvmWm9cE-5-ZyE6F1FgzAtQ6jOOFzzw,275
187
187
  ultralytics/models/yolo/segment/predict.py,sha256=Qf6B4v2O8usK5wHfbre4gkJjEWKidxZRhetWv4nyr6M,5470
188
188
  ultralytics/models/yolo/segment/train.py,sha256=5aPK5FDHLzbXb3R5TCpsAr1O6-8rtupOIoDokY8bSDs,3032
189
- ultralytics/models/yolo/segment/val.py,sha256=fJLDJpK1RZgeMvmtf47BjHhZ9lzX_4QfUuBzGXZqIhA,11289
189
+ ultralytics/models/yolo/segment/val.py,sha256=wly-R-1hE-6vOdhp2TTOQKJxOcYbNHKE24sUb27RhQ4,11313
190
190
  ultralytics/models/yolo/world/__init__.py,sha256=nlh8I6t8hMGz_vZg8QSlsUW1R-2eKvn9CGUoPPQEGhA,131
191
191
  ultralytics/models/yolo/world/train.py,sha256=IBuzLgsNJEFuMaWgrhE3sqIl0vltdzxlPj9Wm0S2diI,7956
192
192
  ultralytics/models/yolo/world/train_world.py,sha256=9p9YIckrATaJjGOrpmuC8MbZX9qdoCPCEV9EGZ0sExg,9553
@@ -196,7 +196,7 @@ ultralytics/models/yolo/yoloe/train.py,sha256=qefvNNXDTOK1tO3va0kNHr8lE5QJkOlV8G
196
196
  ultralytics/models/yolo/yoloe/train_seg.py,sha256=aCV7M8oQOvODFnU4piZdJh3tIrBJYAzZfRVRx1vRgxo,4956
197
197
  ultralytics/models/yolo/yoloe/val.py,sha256=5Gd9EoFH0FmKKvWXBl4J7gBe9DVxIczN-s3ceHwdUDo,9458
198
198
  ultralytics/nn/__init__.py,sha256=PJgOn2phQTTBR2P3s_JWvGeGXQpvw1znsumKow4tCuE,545
199
- ultralytics/nn/autobackend.py,sha256=gDMNtTnlB_t06BvaegcPuXyo6oMP1Pi4zJIjzNWyF9g,41333
199
+ ultralytics/nn/autobackend.py,sha256=Wc3oIpaguT9GJ4BwNVhG51TUhe5f32rwqRxVhF28YK0,41614
200
200
  ultralytics/nn/tasks.py,sha256=r01JGRa9bgGdOHXycN6TSK30I_Ip4GHO9dZ8LtpkmYk,70846
201
201
  ultralytics/nn/text_model.py,sha256=pHqnKe8UueR1MuwJcIE_IvrnYIlt68QL796xjcRJs2A,15275
202
202
  ultralytics/nn/modules/__init__.py,sha256=BPMbEm1daI7Tuds3zph2_afAX7Gq1uAqK8BfiCfKTZs,3198
@@ -253,7 +253,7 @@ ultralytics/utils/logger.py,sha256=o_vH4CCgQat6_Sbmwm1sUAJ4muAgVcsUed-WqpGNQZw,1
253
253
  ultralytics/utils/loss.py,sha256=wJ0F2DpRTI9-e9adxIm2io0zcXRa0RTWFTOc7WmS1-A,39827
254
254
  ultralytics/utils/metrics.py,sha256=DC-JuakuhHfeCeLvUHb7wj1HPhuFakx00rqXicTka5Y,68834
255
255
  ultralytics/utils/nms.py,sha256=AVOmPuUTEJqmq2J6rvjq-nHNxYIyabgzHdc41siyA0w,14161
256
- ultralytics/utils/ops.py,sha256=OYntCTGzMDiABISxbu5WrIfH76PXfsfHe2s79-ZWdpU,27068
256
+ ultralytics/utils/ops.py,sha256=oJjEd1Ly9pYbQn0fO1V4OFRLr1BPJi3A7IXlXszEiVA,27058
257
257
  ultralytics/utils/patches.py,sha256=0-2G4jXCIPnMonlft-cPcjfFcOXQS6ODwUDNUwanfg4,6541
258
258
  ultralytics/utils/plotting.py,sha256=lWvjC_ojjWYca8atorCdJGlDCIph83NA7h7hlnfZx54,48342
259
259
  ultralytics/utils/tal.py,sha256=7KQYNyetfx18CNc_bvNG7BDb44CIU3DEu4qziVVvNAE,20869
@@ -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.216.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
279
- ultralytics-8.3.216.dist-info/METADATA,sha256=2_oJXSwfFWG-SDVdAdwHbkywgsQ-Rsvtd1LYx3gsVSk,37667
280
- ultralytics-8.3.216.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
281
- ultralytics-8.3.216.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
282
- ultralytics-8.3.216.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
283
- ultralytics-8.3.216.dist-info/RECORD,,
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,,