ultralytics 8.3.125__py3-none-any.whl → 8.3.126__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.
tests/test_cuda.py CHANGED
@@ -10,8 +10,18 @@ from tests import CUDA_DEVICE_COUNT, CUDA_IS_AVAILABLE, MODEL, SOURCE
10
10
  from ultralytics import YOLO
11
11
  from ultralytics.cfg import TASK2DATA, TASK2MODEL, TASKS
12
12
  from ultralytics.utils import ASSETS, WEIGHTS_DIR
13
+ from ultralytics.utils.autodevice import GPUInfo
13
14
  from ultralytics.utils.checks import check_amp
14
15
 
16
+ # Try to find idle devices if CUDA is available
17
+ DEVICES = []
18
+ if CUDA_IS_AVAILABLE:
19
+ gpu_info = GPUInfo()
20
+ gpu_info.print_status()
21
+ idle_gpus = gpu_info.select_idle_gpu(count=2, min_memory_mb=2048)
22
+ if idle_gpus:
23
+ DEVICES = idle_gpus
24
+
15
25
 
16
26
  def test_checks():
17
27
  """Validate CUDA settings against torch CUDA functions."""
@@ -19,16 +29,16 @@ def test_checks():
19
29
  assert torch.cuda.device_count() == CUDA_DEVICE_COUNT
20
30
 
21
31
 
22
- @pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason="CUDA is not available")
32
+ @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
23
33
  def test_amp():
24
34
  """Test AMP training checks."""
25
- model = YOLO("yolo11n.pt").model.cuda()
35
+ model = YOLO("yolo11n.pt").model.to(f"cuda:{DEVICES[0]}")
26
36
  assert check_amp(model)
27
37
 
28
38
 
29
39
  @pytest.mark.slow
30
40
  @pytest.mark.skipif(True, reason="CUDA export tests disabled pending additional Ultralytics GPU server availability")
31
- @pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason="CUDA is not available")
41
+ @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
32
42
  @pytest.mark.parametrize(
33
43
  "task, dynamic, int8, half, batch",
34
44
  [ # generate all combinations but exclude those where both int8 and half are True
@@ -40,16 +50,7 @@ def test_amp():
40
50
  ],
41
51
  )
42
52
  def test_export_engine_matrix(task, dynamic, int8, half, batch):
43
- """
44
- Test YOLO model export to TensorRT format for various configurations and run inference.
45
-
46
- Args:
47
- task (str): Task type like 'detect', 'segment', etc.
48
- dynamic (bool): Whether to use dynamic input size.
49
- int8 (bool): Whether to use INT8 precision.
50
- half (bool): Whether to use FP16 precision.
51
- batch (int): Batch size for export.
52
- """
53
+ """Test YOLO model export to TensorRT format for various configurations and run inference."""
53
54
  file = YOLO(TASK2MODEL[task]).export(
54
55
  format="engine",
55
56
  imgsz=32,
@@ -60,105 +61,109 @@ def test_export_engine_matrix(task, dynamic, int8, half, batch):
60
61
  data=TASK2DATA[task],
61
62
  workspace=1, # reduce workspace GB for less resource utilization during testing
62
63
  simplify=True, # use 'onnxslim'
64
+ device=DEVICES[0],
63
65
  )
64
- YOLO(file)([SOURCE] * batch, imgsz=64 if dynamic else 32) # exported model inference
66
+ YOLO(file)([SOURCE] * batch, imgsz=64 if dynamic else 32, device=DEVICES[0]) # exported model inference
65
67
  Path(file).unlink() # cleanup
66
68
  Path(file).with_suffix(".cache").unlink() if int8 else None # cleanup INT8 cache
67
69
 
68
70
 
69
- @pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason="CUDA is not available")
71
+ @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
70
72
  def test_train():
71
73
  """Test model training on a minimal dataset using available CUDA devices."""
72
- device = 0 if CUDA_DEVICE_COUNT == 1 else [0, 1]
74
+ device = DEVICES if len(DEVICES) > 1 else DEVICES[0]
73
75
  YOLO(MODEL).train(data="coco8.yaml", imgsz=64, epochs=1, device=device) # requires imgsz>=64
74
76
 
75
77
 
76
78
  @pytest.mark.slow
77
- @pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason="CUDA is not available")
79
+ @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
78
80
  def test_predict_multiple_devices():
79
81
  """Validate model prediction consistency across CPU and CUDA devices."""
80
82
  model = YOLO("yolo11n.pt")
83
+
84
+ # Test CPU
81
85
  model = model.cpu()
82
86
  assert str(model.device) == "cpu"
83
- _ = model(SOURCE) # CPU inference
87
+ _ = model(SOURCE)
84
88
  assert str(model.device) == "cpu"
85
89
 
86
- model = model.to("cuda:0")
87
- assert str(model.device) == "cuda:0"
88
- _ = model(SOURCE) # CUDA inference
89
- assert str(model.device) == "cuda:0"
90
+ # Test CUDA
91
+ cuda_device = f"cuda:{DEVICES[0]}"
92
+ model = model.to(cuda_device)
93
+ assert str(model.device) == cuda_device
94
+ _ = model(SOURCE)
95
+ assert str(model.device) == cuda_device
90
96
 
97
+ # Test CPU again
91
98
  model = model.cpu()
92
99
  assert str(model.device) == "cpu"
93
- _ = model(SOURCE) # CPU inference
100
+ _ = model(SOURCE)
94
101
  assert str(model.device) == "cpu"
95
102
 
96
- model = model.cuda()
97
- assert str(model.device) == "cuda:0"
98
- _ = model(SOURCE) # CUDA inference
99
- assert str(model.device) == "cuda:0"
103
+ # Test CUDA again
104
+ model = model.to(cuda_device)
105
+ assert str(model.device) == cuda_device
106
+ _ = model(SOURCE)
107
+ assert str(model.device) == cuda_device
100
108
 
101
109
 
102
- @pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason="CUDA is not available")
110
+ @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
103
111
  def test_autobatch():
104
112
  """Check optimal batch size for YOLO model training using autobatch utility."""
105
113
  from ultralytics.utils.autobatch import check_train_batch_size
106
114
 
107
- check_train_batch_size(YOLO(MODEL).model.cuda(), imgsz=128, amp=True)
115
+ check_train_batch_size(YOLO(MODEL).model.to(f"cuda:{DEVICES[0]}"), imgsz=128, amp=True)
108
116
 
109
117
 
110
118
  @pytest.mark.slow
111
- @pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason="CUDA is not available")
119
+ @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
112
120
  def test_utils_benchmarks():
113
121
  """Profile YOLO models for performance benchmarks."""
114
122
  from ultralytics.utils.benchmarks import ProfileModels
115
123
 
116
124
  # Pre-export a dynamic engine model to use dynamic inference
117
- YOLO(MODEL).export(format="engine", imgsz=32, dynamic=True, batch=1)
118
- ProfileModels([MODEL], imgsz=32, half=False, min_time=1, num_timed_runs=3, num_warmup_runs=1).run()
125
+ YOLO(MODEL).export(format="engine", imgsz=32, dynamic=True, batch=1, device=DEVICES[0])
126
+ ProfileModels(
127
+ [MODEL],
128
+ imgsz=32,
129
+ half=False,
130
+ min_time=1,
131
+ num_timed_runs=3,
132
+ num_warmup_runs=1,
133
+ device=DEVICES[0],
134
+ ).run()
119
135
 
120
136
 
121
- @pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason="CUDA is not available")
137
+ @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
122
138
  def test_predict_sam():
123
- """Test SAM model predictions using different prompts, including bounding boxes and point annotations."""
139
+ """Test SAM model predictions using different prompts."""
124
140
  from ultralytics import SAM
125
141
  from ultralytics.models.sam import Predictor as SAMPredictor
126
142
 
127
- # Load a model
128
143
  model = SAM(WEIGHTS_DIR / "sam2.1_b.pt")
129
-
130
- # Display model information (optional)
131
144
  model.info()
132
145
 
133
- # Run inference
134
- model(SOURCE, device=0)
135
-
136
- # Run inference with bboxes prompt
137
- model(SOURCE, bboxes=[439, 437, 524, 709], device=0)
138
-
139
- # Run inference with no labels
140
- model(ASSETS / "zidane.jpg", points=[900, 370], device=0)
141
-
142
- # Run inference with 1D points and 1D labels
143
- model(ASSETS / "zidane.jpg", points=[900, 370], labels=[1], device=0)
144
-
145
- # Run inference with 2D points and 1D labels
146
- model(ASSETS / "zidane.jpg", points=[[900, 370]], labels=[1], device=0)
147
-
148
- # Run inference with multiple 2D points and 1D labels
149
- model(ASSETS / "zidane.jpg", points=[[400, 370], [900, 370]], labels=[1, 1], device=0)
150
-
151
- # Run inference with 3D points and 2D labels (multiple points per object)
152
- model(ASSETS / "zidane.jpg", points=[[[900, 370], [1000, 100]]], labels=[[1, 1]], device=0)
153
-
154
- # Create SAMPredictor
155
- overrides = dict(conf=0.25, task="segment", mode="predict", imgsz=1024, model=WEIGHTS_DIR / "mobile_sam.pt")
156
- predictor = SAMPredictor(overrides=overrides)
157
-
158
- # Set image
159
- predictor.set_image(ASSETS / "zidane.jpg") # set with image file
146
+ # Run inference with various prompts
147
+ model(SOURCE, device=DEVICES[0])
148
+ model(SOURCE, bboxes=[439, 437, 524, 709], device=DEVICES[0])
149
+ model(ASSETS / "zidane.jpg", points=[900, 370], device=DEVICES[0])
150
+ model(ASSETS / "zidane.jpg", points=[900, 370], labels=[1], device=DEVICES[0])
151
+ model(ASSETS / "zidane.jpg", points=[[900, 370]], labels=[1], device=DEVICES[0])
152
+ model(ASSETS / "zidane.jpg", points=[[400, 370], [900, 370]], labels=[1, 1], device=DEVICES[0])
153
+ model(ASSETS / "zidane.jpg", points=[[[900, 370], [1000, 100]]], labels=[[1, 1]], device=DEVICES[0])
154
+
155
+ # Test predictor
156
+ predictor = SAMPredictor(
157
+ overrides=dict(
158
+ conf=0.25,
159
+ task="segment",
160
+ mode="predict",
161
+ imgsz=1024,
162
+ model=WEIGHTS_DIR / "mobile_sam.pt",
163
+ device=DEVICES[0],
164
+ )
165
+ )
166
+ predictor.set_image(ASSETS / "zidane.jpg")
160
167
  # predictor(bboxes=[439, 437, 524, 709])
161
168
  # predictor(points=[900, 370], labels=[1])
162
-
163
- # Reset image
164
169
  predictor.reset_image()
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.125"
3
+ __version__ = "8.3.126"
4
4
 
5
5
  import os
6
6
 
@@ -17,7 +17,7 @@ imgsz: 640 # (int | list) input images size as int for train and val modes, or l
17
17
  save: True # (bool) save train checkpoints and predict results
18
18
  save_period: -1 # (int) Save checkpoint every x epochs (disabled if < 1)
19
19
  cache: False # (bool) True/ram, disk or False. Use cache for data loading
20
- device: # (int | str | list, optional) device to run on, i.e. cuda device=0 or device=0,1,2,3 or device=cpu
20
+ device: # (int | str | list) device: CUDA device=0 or [0,1,2,3] or "cpu/mps" or -1 or [-1,-1] to auto-select idle GPUs
21
21
  workers: 8 # (int) number of worker threads for data loading (per RANK if DDP)
22
22
  project: # (str, optional) project name
23
23
  name: # (str, optional) experiment name, results saved to 'project/name' directory
@@ -105,6 +105,7 @@ class BaseTrainer:
105
105
  self.args = get_cfg(cfg, overrides)
106
106
  self.check_resume(overrides)
107
107
  self.device = select_device(self.args.device, self.args.batch)
108
+ self.args.device = str(self.device) # ensure -1 is updated to selected CUDA device
108
109
  self.validator = None
109
110
  self.metrics = None
110
111
  self.plots = {}
@@ -182,6 +182,10 @@ class TQDM(rich.tqdm if TQDM_RICH else tqdm.tqdm):
182
182
  kwargs.setdefault("bar_format", TQDM_BAR_FORMAT) # override default value if passed
183
183
  super().__init__(*args, **kwargs)
184
184
 
185
+ def __iter__(self):
186
+ """Return self as iterator to satisfy Iterable interface."""
187
+ return super().__iter__()
188
+
185
189
 
186
190
  class SimpleClass:
187
191
  """
@@ -0,0 +1,175 @@
1
+ # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
+
3
+
4
+ from ultralytics.utils import LOGGER
5
+ from ultralytics.utils.checks import check_requirements
6
+
7
+
8
+ class GPUInfo:
9
+ """
10
+ Manages NVIDIA GPU information via pynvml with robust error handling.
11
+
12
+ Provides methods to query detailed GPU statistics (utilization, memory, temp, power) and select the most idle
13
+ GPUs based on configurable criteria. It safely handles the absence or initialization failure of the pynvml
14
+ library by logging warnings and disabling related features, preventing application crashes.
15
+
16
+ Includes fallback logic using `torch.cuda` for basic device counting if NVML is unavailable during GPU
17
+ selection. Manages NVML initialization and shutdown internally.
18
+
19
+ Attributes:
20
+ pynvml (module | None): The `pynvml` module if successfully imported and initialized, otherwise `None`.
21
+ nvml_available (bool): Indicates if `pynvml` is ready for use. True if import and `nvmlInit()` succeeded,
22
+ False otherwise.
23
+ gpu_stats (list[dict]): A list of dictionaries, each holding stats for one GPU. Populated on initialization
24
+ and by `refresh_stats()`. Keys include: 'index', 'name', 'utilization' (%), 'memory_used' (MiB),
25
+ 'memory_total' (MiB), 'memory_free' (MiB), 'temperature' (C), 'power_draw' (W),
26
+ 'power_limit' (W or 'N/A'). Empty if NVML is unavailable or queries fail.
27
+ """
28
+
29
+ def __init__(self):
30
+ """Initializes GPUInfo, attempting to import and initialize pynvml."""
31
+ self.pynvml = None
32
+ self.nvml_available = False
33
+ self.gpu_stats = []
34
+
35
+ try:
36
+ check_requirements("pynvml>=12.0.0")
37
+ self.pynvml = __import__("pynvml")
38
+ self.pynvml.nvmlInit()
39
+ self.nvml_available = True
40
+ self.refresh_stats()
41
+ except Exception as e:
42
+ LOGGER.warning(f"Failed to initialize pynvml, GPU stats disabled: {e}")
43
+
44
+ def __del__(self):
45
+ """Ensures NVML is shut down when the object is garbage collected."""
46
+ self.shutdown()
47
+
48
+ def shutdown(self):
49
+ """Shuts down NVML if it was initialized."""
50
+ if self.nvml_available and self.pynvml:
51
+ try:
52
+ self.pynvml.nvmlShutdown()
53
+ except Exception:
54
+ pass
55
+ self.nvml_available = False
56
+
57
+ def refresh_stats(self):
58
+ """Refreshes the internal gpu_stats list by querying NVML."""
59
+ self.gpu_stats = []
60
+ if not self.nvml_available or not self.pynvml:
61
+ return
62
+
63
+ try:
64
+ device_count = self.pynvml.nvmlDeviceGetCount()
65
+ for i in range(device_count):
66
+ self.gpu_stats.append(self._get_device_stats(i))
67
+ except Exception as e:
68
+ LOGGER.warning(f"Error during device query: {e}")
69
+ self.gpu_stats = []
70
+
71
+ def _get_device_stats(self, index):
72
+ """Gets stats for a single GPU device."""
73
+ handle = self.pynvml.nvmlDeviceGetHandleByIndex(index)
74
+ memory = self.pynvml.nvmlDeviceGetMemoryInfo(handle)
75
+ util = self.pynvml.nvmlDeviceGetUtilizationRates(handle)
76
+
77
+ def safe_get(func, *args, default=-1, divisor=1):
78
+ try:
79
+ val = func(*args)
80
+ return val // divisor if divisor != 1 and isinstance(val, (int, float)) else val
81
+ except Exception:
82
+ return default
83
+
84
+ temp_type = getattr(self.pynvml, "NVML_TEMPERATURE_GPU", -1)
85
+
86
+ return {
87
+ "index": index,
88
+ "name": self.pynvml.nvmlDeviceGetName(handle),
89
+ "utilization": util.gpu if util else -1,
90
+ "memory_used": memory.used >> 20 if memory else -1,
91
+ "memory_total": memory.total >> 20 if memory else -1,
92
+ "memory_free": memory.free >> 20 if memory else -1,
93
+ "temperature": safe_get(self.pynvml.nvmlDeviceGetTemperature, handle, temp_type),
94
+ "power_draw": safe_get(self.pynvml.nvmlDeviceGetPowerUsage, handle, divisor=1000),
95
+ "power_limit": safe_get(self.pynvml.nvmlDeviceGetEnforcedPowerLimit, handle, divisor=1000),
96
+ }
97
+
98
+ def print_status(self):
99
+ """Prints GPU status in a compact table format using current stats."""
100
+ self.refresh_stats()
101
+ if not self.gpu_stats:
102
+ LOGGER.warning("No GPU stats available.")
103
+ return
104
+
105
+ stats = self.gpu_stats
106
+ name_len = max(len(gpu.get("name", "N/A")) for gpu in stats)
107
+ hdr = f"{'Idx':<3} {'Name':<{name_len}} {'Util':>6} {'Mem (MiB)':>15} {'Temp':>5} {'Pwr (W)':>10}"
108
+ LOGGER.info(f"\n--- GPU Status ---\n{hdr}\n{'-' * len(hdr)}")
109
+
110
+ for gpu in stats:
111
+ u = f"{gpu['utilization']:>5}%" if gpu["utilization"] >= 0 else " N/A "
112
+ m = f"{gpu['memory_used']:>6}/{gpu['memory_total']:<6}" if gpu["memory_used"] >= 0 else " N/A / N/A "
113
+ t = f"{gpu['temperature']}C" if gpu["temperature"] >= 0 else " N/A "
114
+ p = f"{gpu['power_draw']:>3}/{gpu['power_limit']:<3}" if gpu["power_draw"] >= 0 else " N/A "
115
+
116
+ LOGGER.info(f"{gpu.get('index'):<3d} {gpu.get('name', 'N/A'):<{name_len}} {u:>6} {m:>15} {t:>5} {p:>10}")
117
+
118
+ LOGGER.info(f"{'-' * len(hdr)}\n")
119
+
120
+ def select_idle_gpu(self, count=1, min_memory_mb=0):
121
+ """
122
+ Selects the 'count' most idle GPUs based on utilization and free memory.
123
+
124
+ Args:
125
+ count (int): The number of idle GPUs to select. Defaults to 1.
126
+ min_memory_mb (int): Minimum free memory required (MiB). Defaults to 0.
127
+
128
+ Returns:
129
+ (list[int]): Indices of the selected GPUs, sorted by idleness.
130
+
131
+ Notes:
132
+ Returns fewer than 'count' if not enough qualify or exist.
133
+ Returns basic CUDA indices if NVML fails. Empty list if no GPUs found.
134
+ """
135
+ LOGGER.info(f"Searching for {count} idle GPUs with >= {min_memory_mb} MiB free memory...")
136
+
137
+ if count <= 0:
138
+ return []
139
+
140
+ self.refresh_stats()
141
+ if not self.gpu_stats:
142
+ LOGGER.warning("NVML stats unavailable.")
143
+ return []
144
+
145
+ # Filter and sort eligible GPUs
146
+ eligible_gpus = [
147
+ gpu
148
+ for gpu in self.gpu_stats
149
+ if gpu.get("memory_free", -1) >= min_memory_mb and gpu.get("utilization", -1) != -1
150
+ ]
151
+ eligible_gpus.sort(key=lambda x: (x.get("utilization", 101), -x.get("memory_free", 0)))
152
+
153
+ # Select top 'count' indices
154
+ selected = [gpu["index"] for gpu in eligible_gpus[:count]]
155
+
156
+ if selected:
157
+ LOGGER.info(f"Selected idle CUDA devices {selected}")
158
+ else:
159
+ LOGGER.warning(f"No GPUs met criteria (Util != -1, Free Mem >= {min_memory_mb} MiB).")
160
+
161
+ return selected
162
+
163
+
164
+ if __name__ == "__main__":
165
+ required_free_mem = 2048 # Require 2GB free VRAM
166
+ num_gpus_to_select = 1
167
+
168
+ gpu_info = GPUInfo()
169
+ gpu_info.print_status()
170
+
171
+ selected = gpu_info.select_idle_gpu(count=num_gpus_to_select, min_memory_mb=required_free_mem)
172
+ if selected:
173
+ print(f"\n==> Using selected GPU indices: {selected}")
174
+ devices = [f"cuda:{idx}" for idx in selected]
175
+ print(f" Target devices: {devices}")
@@ -399,7 +399,7 @@ class ProfileModels:
399
399
  imgsz (int): Size of the image used during profiling.
400
400
  half (bool): Flag to indicate whether to use FP16 half-precision for TensorRT profiling.
401
401
  trt (bool): Flag to indicate whether to profile using TensorRT.
402
- device (torch.device | None): Device used for profiling. If None, it is determined automatically.
402
+ device (torch.device | str | None): Device used for profiling. If None, it is determined automatically.
403
403
 
404
404
  Notes:
405
405
  FP16 'half' argument option removed for ONNX as slower on CPU than FP32.
@@ -417,7 +417,7 @@ class ProfileModels:
417
417
  self.imgsz = imgsz
418
418
  self.half = half
419
419
  self.trt = trt # run TensorRT profiling
420
- self.device = device or torch.device(0 if torch.cuda.is_available() else "cpu")
420
+ self.device = device if isinstance(device, torch.device) else select_device(device)
421
421
 
422
422
  def run(self):
423
423
  """
@@ -608,7 +608,7 @@ def check_yolo(verbose=True, device=""):
608
608
 
609
609
  Args:
610
610
  verbose (bool): Whether to print verbose information.
611
- device (str): Device to use for YOLO.
611
+ device (str | torch.device): Device to use for YOLO.
612
612
  """
613
613
  import psutil
614
614
 
@@ -810,7 +810,7 @@ def print_args(args: Optional[dict] = None, show_file=True, show_func=False):
810
810
  except ValueError:
811
811
  file = Path(file).stem
812
812
  s = (f"{file}: " if show_file else "") + (f"{func}: " if show_func else "")
813
- LOGGER.info(colorstr(s) + ", ".join(f"{k}={strip_auth(v)}" for k, v in args.items()))
813
+ LOGGER.info(colorstr(s) + ", ".join(f"{k}={strip_auth(v)}" for k, v in sorted(args.items())))
814
814
 
815
815
 
816
816
  def cuda_device_count() -> int:
@@ -136,9 +136,9 @@ def select_device(device="", batch=0, newline=False, verbose=True):
136
136
  device (str | torch.device, optional): Device string or torch.device object.
137
137
  Options are 'None', 'cpu', or 'cuda', or '0' or '0,1,2,3'. Defaults to an empty string, which auto-selects
138
138
  the first available GPU, or CPU if no GPU is available.
139
- batch (int, optional): Batch size being used in your model. Defaults to 0.
140
- newline (bool, optional): If True, adds a newline at the end of the log string. Defaults to False.
141
- verbose (bool, optional): If True, logs the device information. Defaults to True.
139
+ batch (int, optional): Batch size being used in your model.
140
+ newline (bool, optional): If True, adds a newline at the end of the log string.
141
+ verbose (bool, optional): If True, logs the device information.
142
142
 
143
143
  Returns:
144
144
  (torch.device): Selected device.
@@ -157,13 +157,26 @@ def select_device(device="", batch=0, newline=False, verbose=True):
157
157
  Note:
158
158
  Sets the 'CUDA_VISIBLE_DEVICES' environment variable for specifying which GPUs to use.
159
159
  """
160
- if isinstance(device, torch.device) or str(device).startswith("tpu") or str(device).startswith("intel"):
160
+ if isinstance(device, torch.device) or str(device).startswith(("tpu", "intel")):
161
161
  return device
162
162
 
163
163
  s = f"Ultralytics {__version__} 🚀 Python-{PYTHON_VERSION} torch-{torch.__version__} "
164
164
  device = str(device).lower()
165
165
  for remove in "cuda:", "none", "(", ")", "[", "]", "'", " ":
166
166
  device = device.replace(remove, "") # to string, 'cuda:0' -> '0' and '(0, 1)' -> '0,1'
167
+
168
+ # Auto-select GPUs
169
+ if "-1" in device:
170
+ from ultralytics.utils.autodevice import GPUInfo
171
+
172
+ # Replace each -1 with a selected GPU or remove it
173
+ parts = device.split(",")
174
+ selected = GPUInfo().select_idle_gpu(count=parts.count("-1"), min_memory_mb=2048)
175
+ for i in range(len(parts)):
176
+ if parts[i] == "-1":
177
+ parts[i] = str(selected.pop(0)) if selected else ""
178
+ device = ",".join(p for p in parts if p)
179
+
167
180
  cpu = device == "cpu"
168
181
  mps = device in {"mps", "mps:0"} # Apple Metal Performance Shaders (MPS)
169
182
  if cpu or mps:
@@ -200,7 +213,7 @@ def select_device(device="", batch=0, newline=False, verbose=True):
200
213
  if batch < 1:
201
214
  raise ValueError(
202
215
  "AutoBatch with batch<1 not supported for Multi-GPU training, "
203
- "please specify a valid batch size, i.e. batch=16."
216
+ f"please specify a valid batch size multiple of GPU count {n}, i.e. batch={n * 8}."
204
217
  )
205
218
  if batch >= 0 and batch % n != 0: # check batch_size is divisible by device_count
206
219
  raise ValueError(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ultralytics
3
- Version: 8.3.125
3
+ Version: 8.3.126
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>
@@ -1,17 +1,17 @@
1
1
  tests/__init__.py,sha256=xnMhv3O_DF1YrW4zk__ZywQzAaoTDjPKPoiI1Ktss1w,670
2
2
  tests/conftest.py,sha256=rsIAipRKfrVNoTaJ1LdpYue8AbcJ_fr3d3WIlM_6uXY,2982
3
3
  tests/test_cli.py,sha256=PtMFl5Lp_6ygBbYDJ1ndofz2k7ZYupMPEAiZw6aZVm8,5450
4
- tests/test_cuda.py,sha256=vCpPMAkEUQrQMVe4oMwGZQVOiuujEAkZ2zturNXFF-4,6256
4
+ tests/test_cuda.py,sha256=vMjegc23QlEzMdpzav2JEjXR1n8W-lYZ-KLGiLiwLok,6167
5
5
  tests/test_engine.py,sha256=aGqZ8P7QO5C_nOa1b4FOyk92Ysdk5WiP-ST310Vyxys,4962
6
6
  tests/test_exports.py,sha256=dhZn86LdbapW15RthQF870LGxDjC1MUZhlGdBgPmgIQ,9716
7
7
  tests/test_integrations.py,sha256=dQteeRsRVuT_p5-T88-7jqT65Zm9iAXkyKg-KQ1_TQ8,6341
8
8
  tests/test_python.py,sha256=hkOJc0Ejin3Bywyw0BT4pPex5hwwfbmw0K5ChRtvdvw,25398
9
9
  tests/test_solutions.py,sha256=BIvg9zW0a_ggEmrPKgB_Y0MncveH-eYuN5KlqdJ6nHs,5726
10
- ultralytics/__init__.py,sha256=4QZuLQ9zDL-596BL2iVx4dIiVPri_412418f6UrtZDQ,730
10
+ ultralytics/__init__.py,sha256=HfUbgU67v26Obhd8l6aWhYnTG8W_ycy0mBgszTdxe6E,730
11
11
  ultralytics/assets/bus.jpg,sha256=wCAZxJecGR63Od3ZRERe9Aja1Weayrb9Ug751DS_vGM,137419
12
12
  ultralytics/assets/zidane.jpg,sha256=Ftc4aeMmen1O0A3o6GCDO9FlfBslLpTAw0gnetx7bts,50427
13
13
  ultralytics/cfg/__init__.py,sha256=We3ti0mvUQrGRmUPcufDGboW0YAO3nSRYuoWxGagk3M,39462
14
- ultralytics/cfg/default.yaml,sha256=ceGQ1n6gAhImYs5xwn4uWrX4jzQffVbNnKcWOScy-k0,8296
14
+ ultralytics/cfg/default.yaml,sha256=oFG6llJO-Py5H-cR9qs-7FieJamroDLwpbrkhmfROOM,8307
15
15
  ultralytics/cfg/datasets/Argoverse.yaml,sha256=_xlEDIJ9XkUo0v_iNL7FW079BoSeZtKSuLteKTtGbA8,3275
16
16
  ultralytics/cfg/datasets/DOTAv1.5.yaml,sha256=SHND_CFkojxw5iQD5Mcgju2kCZIl0gW2ajuzv1cqoL0,1224
17
17
  ultralytics/cfg/datasets/DOTAv1.yaml,sha256=j_DvXVQzZ4dQmf8I7oPX4v9xO3WZXztxV4Xo9VhUTsM,1194
@@ -121,7 +121,7 @@ ultralytics/engine/exporter.py,sha256=aaZ_-np1q0klWtDXp6CxVjyiZ0DDXx-8Pqg4jZSByu
121
121
  ultralytics/engine/model.py,sha256=37qGh6aqqPTUyMfpsvBQMaZ1Av7eJDe6mfRl9GvlfKg,52860
122
122
  ultralytics/engine/predictor.py,sha256=YJ5l-0qIpr6JAJxowswtZ0IqmXBqVTvAA9vR40v0sCM,21752
123
123
  ultralytics/engine/results.py,sha256=-JPBn_YMyZv6HhdlyhjRIZCcMf41LTyWID7JrEP64rc,79632
124
- ultralytics/engine/trainer.py,sha256=1J_by51vXccnF-uCB_40eUsuPLDexCEWv4775v7RrAQ,38859
124
+ ultralytics/engine/trainer.py,sha256=sQCtjCI7_qOvXp4z-OPIQB1Nnqgeoi8YAIJAiCs_OOY,38951
125
125
  ultralytics/engine/tuner.py,sha256=zEW1UpLlZ6N4xbvS7MxICkshRlaFgLNfuADA0VfRpao,12629
126
126
  ultralytics/engine/validator.py,sha256=jfV81wuFDgrVVXEcPzgOpxAPrAZn-1LgpKwu9l_1-ts,17050
127
127
  ultralytics/hub/__init__.py,sha256=wDtAUKdfqob95tfFHgDJFXcsNSDSdoIQkJTm-CfIUTI,6616
@@ -230,10 +230,11 @@ ultralytics/trackers/utils/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6D
230
230
  ultralytics/trackers/utils/gmc.py,sha256=dz3I5LbIv7h1__Xg7rGHecQFE32VFTe54tUnxb8F0Z8,14466
231
231
  ultralytics/trackers/utils/kalman_filter.py,sha256=A0CqOnnaKH6kr0XwuHzyHmIU6aJAjJYxF9jVlNBKZHo,21326
232
232
  ultralytics/trackers/utils/matching.py,sha256=7eIufSdeN7cXuFMjvcfvz0Ldq84m4YKZl5IGxBR8IIo,7169
233
- ultralytics/utils/__init__.py,sha256=mmLPuinPhaSyvSfDdsyADIjlJQ2ow5z3OhYFo2NxwE0,52679
233
+ ultralytics/utils/__init__.py,sha256=YSBOQcgak2v6l03EHPjkpzH-ZtjVXrg2_4o0BF1cqDQ,52807
234
234
  ultralytics/utils/autobatch.py,sha256=kg05q2qKg74y_Uq2vvr01i3KhLfpVR7sT0IXBt3_kyI,4921
235
- ultralytics/utils/benchmarks.py,sha256=Tcmq8e04Gw5nNtV4vXIWnr7zfV21PQ6Tqg_0srbt-fc,30162
236
- ultralytics/utils/checks.py,sha256=NtDOmKMmsiOiOecjBoaLFQLp2K_kr7LFFO-gxSBDgYU,32688
235
+ ultralytics/utils/autodevice.py,sha256=OrLSk34UpW0I5ndxnkQEIWBxL--CvAON_W9Qw51zOGA,7233
236
+ ultralytics/utils/benchmarks.py,sha256=1Y6R1DxdSOzeHRsKKgMOab_bdtEWF9z32HOU2hqgzss,30172
237
+ ultralytics/utils/checks.py,sha256=Z87AuJ3C5JcTVYdhAn31BFErmF48bRyMc4_WZ9ku5-E,32711
237
238
  ultralytics/utils/dist.py,sha256=aytW0JEkcA5ZTZucV92ot7Bn-apiej8aLk3QNWicjAc,4103
238
239
  ultralytics/utils/downloads.py,sha256=Rn8xDwn2bzgBqiYz3Xn0rm3MWjk4T-QUd2Ajlu1EpQ4,22312
239
240
  ultralytics/utils/errors.py,sha256=vY9h2evFSrHnZdHJVVrmm8Zzw4qVDLyo9DeYW5g0dFk,1573
@@ -246,7 +247,7 @@ ultralytics/utils/ops.py,sha256=YFwPrKlPcgEmgAWqnJVR0Ccx5NQgp5e3P-YYHwVSP0k,3477
246
247
  ultralytics/utils/patches.py,sha256=6rVT-l8WDp_Py3O-gZdv9t3PnrYRRkrX_lF3mZ1XS8c,4928
247
248
  ultralytics/utils/plotting.py,sha256=8n9G1RvFAv4fk09iqZt7D-VXUqfAHoOTBcGXE7BHEE0,46807
248
249
  ultralytics/utils/tal.py,sha256=P5nPoR9qNnFuDIda0fsn8WP6m1V8r7EbvXUuhNRFFTA,20805
249
- ultralytics/utils/torch_utils.py,sha256=SOdT9asxyQ-MEJGZQIH_Va9jcbonjISeHOwiFg1gRYE,39180
250
+ ultralytics/utils/torch_utils.py,sha256=2SJxxg8Qr0YqOoQ-8qAYn6VrzZdQMObqiw3CJZ-rAY0,39611
250
251
  ultralytics/utils/triton.py,sha256=xK9Db_ZUVDnIK1u76S2G-6ulIBsLfj9HN_YOaSrnMuU,5304
251
252
  ultralytics/utils/tuner.py,sha256=0Bp7l5dWZe1RzdvAIa11wQoX6eoAaoNRcA-EAnpofbk,6755
252
253
  ultralytics/utils/callbacks/__init__.py,sha256=hzL63Rce6VkZhP4Lcim9LKjadixaQG86nKqPhk7IkS0,242
@@ -260,9 +261,9 @@ ultralytics/utils/callbacks/neptune.py,sha256=JaI95Cj2kIjUhlEEOiDN0-Drc-fDelLhNI
260
261
  ultralytics/utils/callbacks/raytune.py,sha256=A8amUGpux7dYES-L1iSeMoMXBySGWCD1aUqT7vcG-pU,1284
261
262
  ultralytics/utils/callbacks/tensorboard.py,sha256=jgYnym3cUQFAgN1GzTyO7l3jINtfAh8zhrllDvnLuVQ,5339
262
263
  ultralytics/utils/callbacks/wb.py,sha256=iDRFXI4IIDm8R5OI89DMTmjs8aHLo1HRCLkOFKdaMG4,7507
263
- ultralytics-8.3.125.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
264
- ultralytics-8.3.125.dist-info/METADATA,sha256=Z_UvZKfIDM36rzOfTtP77x_sD2rdAApQONjqSwDs6KI,37180
265
- ultralytics-8.3.125.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
266
- ultralytics-8.3.125.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
267
- ultralytics-8.3.125.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
268
- ultralytics-8.3.125.dist-info/RECORD,,
264
+ ultralytics-8.3.126.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
265
+ ultralytics-8.3.126.dist-info/METADATA,sha256=zkgsqHwnXIUCbSDzK_gK_HunHRJAQvvvIgix7NOzXgw,37180
266
+ ultralytics-8.3.126.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
267
+ ultralytics-8.3.126.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
268
+ ultralytics-8.3.126.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
269
+ ultralytics-8.3.126.dist-info/RECORD,,