dgenerate-ultralytics-headless 8.3.190__py3-none-any.whl → 8.3.192__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.
Files changed (103) hide show
  1. {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/METADATA +1 -1
  2. {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/RECORD +103 -102
  3. tests/test_cuda.py +6 -5
  4. tests/test_exports.py +1 -6
  5. tests/test_python.py +1 -4
  6. tests/test_solutions.py +1 -1
  7. ultralytics/__init__.py +1 -1
  8. ultralytics/cfg/__init__.py +16 -14
  9. ultralytics/cfg/datasets/SKU-110K.yaml +1 -1
  10. ultralytics/cfg/datasets/VisDrone.yaml +4 -4
  11. ultralytics/data/annotator.py +6 -6
  12. ultralytics/data/augment.py +53 -51
  13. ultralytics/data/base.py +15 -13
  14. ultralytics/data/build.py +7 -4
  15. ultralytics/data/converter.py +9 -10
  16. ultralytics/data/dataset.py +24 -22
  17. ultralytics/data/loaders.py +13 -11
  18. ultralytics/data/split.py +4 -3
  19. ultralytics/data/split_dota.py +14 -12
  20. ultralytics/data/utils.py +29 -23
  21. ultralytics/engine/exporter.py +2 -2
  22. ultralytics/engine/model.py +16 -14
  23. ultralytics/engine/predictor.py +8 -6
  24. ultralytics/engine/results.py +54 -52
  25. ultralytics/engine/trainer.py +8 -3
  26. ultralytics/engine/tuner.py +230 -42
  27. ultralytics/hub/google/__init__.py +7 -6
  28. ultralytics/hub/session.py +8 -6
  29. ultralytics/hub/utils.py +3 -4
  30. ultralytics/models/fastsam/model.py +8 -6
  31. ultralytics/models/nas/model.py +5 -3
  32. ultralytics/models/rtdetr/train.py +4 -3
  33. ultralytics/models/rtdetr/val.py +6 -4
  34. ultralytics/models/sam/amg.py +13 -10
  35. ultralytics/models/sam/model.py +3 -2
  36. ultralytics/models/sam/modules/blocks.py +21 -21
  37. ultralytics/models/sam/modules/decoders.py +11 -11
  38. ultralytics/models/sam/modules/encoders.py +25 -25
  39. ultralytics/models/sam/modules/memory_attention.py +9 -8
  40. ultralytics/models/sam/modules/sam.py +8 -10
  41. ultralytics/models/sam/modules/tiny_encoder.py +21 -20
  42. ultralytics/models/sam/modules/transformer.py +6 -5
  43. ultralytics/models/sam/modules/utils.py +7 -5
  44. ultralytics/models/sam/predict.py +32 -31
  45. ultralytics/models/utils/loss.py +29 -27
  46. ultralytics/models/utils/ops.py +10 -8
  47. ultralytics/models/yolo/classify/train.py +9 -7
  48. ultralytics/models/yolo/classify/val.py +11 -9
  49. ultralytics/models/yolo/detect/predict.py +1 -1
  50. ultralytics/models/yolo/detect/train.py +8 -6
  51. ultralytics/models/yolo/detect/val.py +22 -20
  52. ultralytics/models/yolo/model.py +14 -14
  53. ultralytics/models/yolo/obb/train.py +5 -3
  54. ultralytics/models/yolo/obb/val.py +11 -9
  55. ultralytics/models/yolo/pose/train.py +7 -5
  56. ultralytics/models/yolo/pose/val.py +12 -10
  57. ultralytics/models/yolo/segment/train.py +4 -5
  58. ultralytics/models/yolo/segment/val.py +13 -11
  59. ultralytics/models/yolo/world/train.py +10 -8
  60. ultralytics/models/yolo/yoloe/train.py +10 -10
  61. ultralytics/models/yolo/yoloe/val.py +11 -9
  62. ultralytics/nn/autobackend.py +17 -19
  63. ultralytics/nn/modules/block.py +12 -12
  64. ultralytics/nn/modules/conv.py +4 -3
  65. ultralytics/nn/modules/head.py +41 -37
  66. ultralytics/nn/modules/transformer.py +22 -21
  67. ultralytics/nn/tasks.py +2 -2
  68. ultralytics/nn/text_model.py +6 -5
  69. ultralytics/solutions/analytics.py +7 -5
  70. ultralytics/solutions/config.py +12 -10
  71. ultralytics/solutions/distance_calculation.py +3 -3
  72. ultralytics/solutions/heatmap.py +4 -2
  73. ultralytics/solutions/object_counter.py +5 -3
  74. ultralytics/solutions/parking_management.py +4 -2
  75. ultralytics/solutions/region_counter.py +7 -5
  76. ultralytics/solutions/similarity_search.py +5 -3
  77. ultralytics/solutions/solutions.py +38 -36
  78. ultralytics/solutions/streamlit_inference.py +8 -7
  79. ultralytics/trackers/bot_sort.py +11 -9
  80. ultralytics/trackers/byte_tracker.py +17 -15
  81. ultralytics/trackers/utils/gmc.py +4 -3
  82. ultralytics/utils/__init__.py +16 -88
  83. ultralytics/utils/autobatch.py +3 -2
  84. ultralytics/utils/autodevice.py +10 -10
  85. ultralytics/utils/benchmarks.py +11 -10
  86. ultralytics/utils/callbacks/comet.py +9 -9
  87. ultralytics/utils/checks.py +17 -26
  88. ultralytics/utils/export.py +12 -11
  89. ultralytics/utils/files.py +8 -7
  90. ultralytics/utils/git.py +139 -0
  91. ultralytics/utils/instance.py +8 -7
  92. ultralytics/utils/loss.py +15 -13
  93. ultralytics/utils/metrics.py +62 -62
  94. ultralytics/utils/ops.py +3 -2
  95. ultralytics/utils/patches.py +6 -4
  96. ultralytics/utils/plotting.py +20 -18
  97. ultralytics/utils/torch_utils.py +4 -2
  98. ultralytics/utils/tqdm.py +18 -14
  99. ultralytics/utils/triton.py +3 -2
  100. {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/WHEEL +0 -0
  101. {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/entry_points.txt +0 -0
  102. {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/licenses/LICENSE +0 -0
  103. {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/top_level.txt +0 -0
@@ -14,16 +14,19 @@ Examples:
14
14
  >>> model.tune(data="coco8.yaml", epochs=10, iterations=300, optimizer="AdamW", plots=False, save=False, val=False)
15
15
  """
16
16
 
17
+ from __future__ import annotations
18
+
17
19
  import random
18
20
  import shutil
19
21
  import subprocess
20
22
  import time
21
- from typing import Dict, List, Optional
23
+ from datetime import datetime
22
24
 
23
25
  import numpy as np
24
26
 
25
27
  from ultralytics.cfg import get_cfg, get_save_dir
26
28
  from ultralytics.utils import DEFAULT_CFG, LOGGER, YAML, callbacks, colorstr, remove_colorstr
29
+ from ultralytics.utils.checks import check_requirements
27
30
  from ultralytics.utils.patches import torch_load
28
31
  from ultralytics.utils.plotting import plot_tune_results
29
32
 
@@ -33,15 +36,18 @@ class Tuner:
33
36
  A class for hyperparameter tuning of YOLO models.
34
37
 
35
38
  The class evolves YOLO model hyperparameters over a given number of iterations by mutating them according to the
36
- search space and retraining the model to evaluate their performance.
39
+ search space and retraining the model to evaluate their performance. Supports both local CSV storage and
40
+ distributed MongoDB Atlas coordination for multi-machine hyperparameter optimization.
37
41
 
38
42
  Attributes:
39
- space (Dict[str, tuple]): Hyperparameter search space containing bounds and scaling factors for mutation.
43
+ space (dict[str, tuple]): Hyperparameter search space containing bounds and scaling factors for mutation.
40
44
  tune_dir (Path): Directory where evolution logs and results will be saved.
41
45
  tune_csv (Path): Path to the CSV file where evolution logs are saved.
42
46
  args (dict): Configuration arguments for the tuning process.
43
47
  callbacks (list): Callback functions to be executed during tuning.
44
48
  prefix (str): Prefix string for logging messages.
49
+ mongodb (MongoClient): Optional MongoDB client for distributed tuning.
50
+ collection (Collection): MongoDB collection for storing tuning results.
45
51
 
46
52
  Methods:
47
53
  _mutate: Mutate hyperparameters based on bounds and scaling factors.
@@ -52,20 +58,35 @@ class Tuner:
52
58
  >>> from ultralytics import YOLO
53
59
  >>> model = YOLO("yolo11n.pt")
54
60
  >>> model.tune(
55
- ... data="coco8.yaml", epochs=10, iterations=300, optimizer="AdamW", plots=False, save=False, val=False
56
- ... )
61
+ >>> data="coco8.yaml",
62
+ >>> epochs=10,
63
+ >>> iterations=300,
64
+ >>> plots=False,
65
+ >>> save=False,
66
+ >>> val=False
67
+ >>> )
68
+
69
+ Tune with distributed MongoDB Atlas coordination across multiple machines:
70
+ >>> model.tune(
71
+ >>> data="coco8.yaml",
72
+ >>> epochs=10,
73
+ >>> iterations=300,
74
+ >>> mongodb_uri="mongodb+srv://user:pass@cluster.mongodb.net/",
75
+ >>> mongodb_db="ultralytics",
76
+ >>> mongodb_collection="tune_results"
77
+ >>> )
57
78
 
58
- Tune with custom search space.
59
- >>> model.tune(space={key1: val1, key2: val2}) # custom search space dictionary
79
+ Tune with custom search space:
80
+ >>> model.tune(space={"lr0": (1e-5, 1e-1), "momentum": (0.6, 0.98)})
60
81
  """
61
82
 
62
- def __init__(self, args=DEFAULT_CFG, _callbacks: Optional[List] = None):
83
+ def __init__(self, args=DEFAULT_CFG, _callbacks: list | None = None):
63
84
  """
64
85
  Initialize the Tuner with configurations.
65
86
 
66
87
  Args:
67
88
  args (dict): Configuration for hyperparameter evolution.
68
- _callbacks (List, optional): Callback functions to be executed during tuning.
89
+ _callbacks (list | None, optional): Callback functions to be executed during tuning.
69
90
  """
70
91
  self.space = args.pop("space", None) or { # key: (min, max, gain(optional))
71
92
  # 'optimizer': tune.choice(['SGD', 'Adam', 'AdamW', 'NAdam', 'RAdam', 'RMSProp']),
@@ -94,6 +115,10 @@ class Tuner:
94
115
  "cutmix": (0.0, 1.0), # image cutmix (probability)
95
116
  "copy_paste": (0.0, 1.0), # segment copy-paste (probability)
96
117
  }
118
+ mongodb_uri = args.pop("mongodb_uri", None)
119
+ mongodb_db = args.pop("mongodb_db", "ultralytics")
120
+ mongodb_collection = args.pop("mongodb_collection", "tuner_results")
121
+
97
122
  self.args = get_cfg(overrides=args)
98
123
  self.args.exist_ok = self.args.resume # resume w/ same tune_dir
99
124
  self.tune_dir = get_save_dir(self.args, name=self.args.name or "tune")
@@ -102,14 +127,152 @@ class Tuner:
102
127
  self.callbacks = _callbacks or callbacks.get_default_callbacks()
103
128
  self.prefix = colorstr("Tuner: ")
104
129
  callbacks.add_integration_callbacks(self)
130
+
131
+ # MongoDB Atlas support (optional)
132
+ self.mongodb = None
133
+ if mongodb_uri:
134
+ self._init_mongodb(mongodb_uri, mongodb_db, mongodb_collection)
135
+
105
136
  LOGGER.info(
106
137
  f"{self.prefix}Initialized Tuner instance with 'tune_dir={self.tune_dir}'\n"
107
138
  f"{self.prefix}💡 Learn about tuning at https://docs.ultralytics.com/guides/hyperparameter-tuning"
108
139
  )
109
140
 
141
+ def _connect(self, uri: str = "mongodb+srv://username:password@cluster.mongodb.net/", max_retries: int = 3):
142
+ """
143
+ Create MongoDB client with exponential backoff retry on connection failures.
144
+
145
+ Args:
146
+ uri (str): MongoDB connection string with credentials and cluster information.
147
+ max_retries (int): Maximum number of connection attempts before giving up.
148
+
149
+ Returns:
150
+ (MongoClient): Connected MongoDB client instance.
151
+ """
152
+ check_requirements("pymongo")
153
+
154
+ from pymongo import MongoClient
155
+ from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
156
+
157
+ for attempt in range(max_retries):
158
+ try:
159
+ client = MongoClient(
160
+ uri,
161
+ serverSelectionTimeoutMS=30000,
162
+ connectTimeoutMS=20000,
163
+ socketTimeoutMS=40000,
164
+ retryWrites=True,
165
+ retryReads=True,
166
+ maxPoolSize=30,
167
+ minPoolSize=3,
168
+ maxIdleTimeMS=60000,
169
+ )
170
+ client.admin.command("ping") # Test connection
171
+ LOGGER.info(f"{self.prefix}Connected to MongoDB Atlas (attempt {attempt + 1})")
172
+ return client
173
+ except (ConnectionFailure, ServerSelectionTimeoutError):
174
+ if attempt == max_retries - 1:
175
+ raise
176
+ wait_time = 2**attempt
177
+ LOGGER.warning(
178
+ f"{self.prefix}MongoDB connection failed (attempt {attempt + 1}), retrying in {wait_time}s..."
179
+ )
180
+ time.sleep(wait_time)
181
+
182
+ def _init_mongodb(self, mongodb_uri="", mongodb_db="", mongodb_collection=""):
183
+ """
184
+ Initialize MongoDB connection for distributed tuning.
185
+
186
+ Connects to MongoDB Atlas for distributed hyperparameter optimization across multiple machines.
187
+ Each worker saves results to a shared collection and reads the latest best hyperparameters
188
+ from all workers for evolution.
189
+
190
+ Args:
191
+ mongodb_uri (str): MongoDB connection string, e.g. 'mongodb+srv://username:password@cluster.mongodb.net/'.
192
+ mongodb_db (str, optional): Database name.
193
+ mongodb_collection (str, optional): Collection name.
194
+
195
+ Notes:
196
+ - Creates a fitness index for fast queries of top results
197
+ - Falls back to CSV-only mode if connection fails
198
+ - Uses connection pooling and retry logic for production reliability
199
+ """
200
+ self.mongodb = self._connect(mongodb_uri)
201
+ self.collection = self.mongodb[mongodb_db][mongodb_collection]
202
+ self.collection.create_index([("fitness", -1)], background=True)
203
+ LOGGER.info(f"{self.prefix}Using MongoDB Atlas for distributed tuning")
204
+
205
+ def _get_mongodb_results(self, n: int = 5) -> list:
206
+ """
207
+ Get top N results from MongoDB sorted by fitness.
208
+
209
+ Args:
210
+ n (int): Number of top results to retrieve.
211
+
212
+ Returns:
213
+ (list[dict]): List of result documents with fitness scores and hyperparameters.
214
+ """
215
+ try:
216
+ return list(self.collection.find().sort("fitness", -1).limit(n))
217
+ except Exception:
218
+ return []
219
+
220
+ def _save_to_mongodb(self, fitness: float, hyperparameters: dict[str, float], metrics: dict, iteration: int):
221
+ """
222
+ Save results to MongoDB with proper type conversion.
223
+
224
+ Args:
225
+ fitness (float): Fitness score achieved with these hyperparameters.
226
+ hyperparameters (dict[str, float]): Dictionary of hyperparameter values.
227
+ metrics (dict): Complete training metrics dictionary (mAP, precision, recall, losses, etc.).
228
+ iteration (int): Current iteration number.
229
+ """
230
+ try:
231
+ self.collection.insert_one(
232
+ {
233
+ "fitness": float(fitness),
234
+ "hyperparameters": {k: (v.item() if hasattr(v, "item") else v) for k, v in hyperparameters.items()},
235
+ "metrics": metrics,
236
+ "timestamp": datetime.now(),
237
+ "iteration": iteration,
238
+ }
239
+ )
240
+ except Exception as e:
241
+ LOGGER.warning(f"{self.prefix}MongoDB save failed: {e}")
242
+
243
+ def _sync_mongodb_to_csv(self):
244
+ """
245
+ Sync MongoDB results to CSV for plotting compatibility.
246
+
247
+ Downloads all results from MongoDB and writes them to the local CSV file in chronological order. This enables
248
+ the existing plotting functions to work seamlessly with distributed MongoDB data.
249
+ """
250
+ try:
251
+ # Get all results from MongoDB
252
+ all_results = list(self.collection.find().sort("iteration", 1))
253
+ if not all_results:
254
+ return
255
+
256
+ # Write to CSV
257
+ headers = ",".join(["fitness"] + list(self.space.keys())) + "\n"
258
+ with open(self.tune_csv, "w", encoding="utf-8") as f:
259
+ f.write(headers)
260
+ for result in all_results:
261
+ fitness = result["fitness"]
262
+ hyp_values = [result["hyperparameters"][k] for k in self.space.keys()]
263
+ log_row = [round(fitness, 5)] + hyp_values
264
+ f.write(",".join(map(str, log_row)) + "\n")
265
+
266
+ except Exception as e:
267
+ LOGGER.warning(f"{self.prefix}MongoDB to CSV sync failed: {e}")
268
+
110
269
  def _mutate(
111
- self, parent: str = "single", n: int = 5, mutation: float = 0.8, sigma: float = 0.2
112
- ) -> Dict[str, float]:
270
+ self,
271
+ parent: str = "single",
272
+ n: int = 5,
273
+ mutation: float = 0.8,
274
+ sigma: float = 0.2,
275
+ ) -> dict[str, float]:
113
276
  """
114
277
  Mutate hyperparameters based on bounds and scaling factors specified in `self.space`.
115
278
 
@@ -120,23 +283,36 @@ class Tuner:
120
283
  sigma (float): Standard deviation for Gaussian random number generator.
121
284
 
122
285
  Returns:
123
- (Dict[str, float]): A dictionary containing mutated hyperparameters.
286
+ (dict[str, float]): A dictionary containing mutated hyperparameters.
124
287
  """
125
- if self.tune_csv.exists(): # if CSV file exists: select best hyps and mutate
126
- # Select parent(s)
127
- x = np.loadtxt(self.tune_csv, ndmin=2, delimiter=",", skiprows=1)
128
- fitness = x[:, 0] # first column
129
- n = min(n, len(x)) # number of previous results to consider
130
- x = x[np.argsort(-fitness)][:n] # top n mutations
288
+ x = None
289
+
290
+ # Try MongoDB first if available
291
+ if self.mongodb:
292
+ results = self._get_mongodb_results(n)
293
+ if results:
294
+ # MongoDB already sorted by fitness DESC, so results[0] is best
295
+ x = np.array([[r["fitness"]] + [r["hyperparameters"][k] for k in self.space.keys()] for r in results])
296
+ n = min(n, len(x))
297
+
298
+ # Fall back to CSV if MongoDB unavailable or empty
299
+ if x is None and self.tune_csv.exists():
300
+ csv_data = np.loadtxt(self.tune_csv, ndmin=2, delimiter=",", skiprows=1)
301
+ if len(csv_data) > 0:
302
+ fitness = csv_data[:, 0] # first column
303
+ n = min(n, len(csv_data))
304
+ x = csv_data[np.argsort(-fitness)][:n] # top n sorted by fitness DESC
305
+
306
+ # Mutate if we have data, otherwise use defaults
307
+ if x is not None:
131
308
  w = x[:, 0] - x[:, 0].min() + 1e-6 # weights (sum > 0)
132
- if parent == "single" or len(x) == 1:
133
- # x = x[random.randint(0, n - 1)] # random selection
309
+ if parent == "single" or len(x) <= 1:
134
310
  x = x[random.choices(range(n), weights=w)[0]] # weighted selection
135
311
  elif parent == "weighted":
136
312
  x = (x * w.reshape(n, 1)).sum(0) / w.sum() # weighted combination
137
313
 
138
314
  # Mutate
139
- r = np.random # method
315
+ r = np.random
140
316
  r.seed(int(time.time()))
141
317
  g = np.array([v[2] if len(v) == 3 else 1.0 for v in self.space.values()]) # gains 0-1
142
318
  ng = len(self.space)
@@ -148,9 +324,9 @@ class Tuner:
148
324
  hyp = {k: getattr(self.args, k) for k in self.space.keys()}
149
325
 
150
326
  # Constrain to limits
151
- for k, v in self.space.items():
152
- hyp[k] = max(hyp[k], v[0]) # lower limit
153
- hyp[k] = min(hyp[k], v[1]) # upper limit
327
+ for k, bounds in self.space.items():
328
+ hyp[k] = max(hyp[k], bounds[0]) # lower limit
329
+ hyp[k] = min(hyp[k], bounds[1]) # upper limit
154
330
  hyp[k] = round(hyp[k], 5) # significant digits
155
331
 
156
332
  return hyp
@@ -159,25 +335,26 @@ class Tuner:
159
335
  """
160
336
  Execute the hyperparameter evolution process when the Tuner instance is called.
161
337
 
162
- This method iterates through the number of iterations, performing the following steps in each iteration:
163
-
164
- 1. Load the existing hyperparameters or initialize new ones.
165
- 2. Mutate the hyperparameters using the `_mutate` method.
166
- 3. Train a YOLO model with the mutated hyperparameters.
167
- 4. Log the fitness score and mutated hyperparameters to a CSV file.
338
+ This method iterates through the specified number of iterations, performing the following steps:
339
+ 1. Sync MongoDB results to CSV (if using distributed mode)
340
+ 2. Mutate hyperparameters using the best previous results or defaults
341
+ 3. Train a YOLO model with the mutated hyperparameters
342
+ 4. Log fitness scores and hyperparameters to MongoDB and/or CSV
343
+ 5. Track the best performing configuration across all iterations
168
344
 
169
345
  Args:
170
- model (Model): A pre-initialized YOLO model to be used for training.
346
+ model (Model | None, optional): A pre-initialized YOLO model to be used for training.
171
347
  iterations (int): The number of generations to run the evolution for.
172
- cleanup (bool): Whether to delete iteration weights to reduce storage space used during tuning.
173
-
174
- Note:
175
- The method utilizes the `self.tune_csv` Path object to read and log hyperparameters and fitness scores.
176
- Ensure this path is set correctly in the Tuner instance.
348
+ cleanup (bool): Whether to delete iteration weights to reduce storage space during tuning.
177
349
  """
178
350
  t0 = time.time()
179
351
  best_save_dir, best_metrics = None, None
180
352
  (self.tune_dir / "weights").mkdir(parents=True, exist_ok=True)
353
+
354
+ # Sync MongoDB to CSV at startup for proper resume logic
355
+ if self.mongodb:
356
+ self._sync_mongodb_to_csv()
357
+
181
358
  start = 0
182
359
  if self.tune_csv.exists():
183
360
  x = np.loadtxt(self.tune_csv, ndmin=2, delimiter=",", skiprows=1)
@@ -204,12 +381,23 @@ class Tuner:
204
381
  except Exception as e:
205
382
  LOGGER.error(f"training failure for hyperparameter tuning iteration {i + 1}\n{e}")
206
383
 
207
- # Save results and mutated_hyp to CSV
384
+ # Save results - MongoDB takes precedence
208
385
  fitness = metrics.get("fitness", 0.0)
209
- log_row = [round(fitness, 5)] + [mutated_hyp[k] for k in self.space.keys()]
210
- headers = "" if self.tune_csv.exists() else (",".join(["fitness"] + list(self.space.keys())) + "\n")
211
- with open(self.tune_csv, "a", encoding="utf-8") as f:
212
- f.write(headers + ",".join(map(str, log_row)) + "\n")
386
+ if self.mongodb:
387
+ self._save_to_mongodb(fitness, mutated_hyp, metrics, i + 1)
388
+ self._sync_mongodb_to_csv()
389
+ total_mongo_iterations = self.collection.count_documents({})
390
+ if total_mongo_iterations >= iterations:
391
+ LOGGER.info(
392
+ f"{self.prefix}Target iterations ({iterations}) reached in MongoDB ({total_mongo_iterations}). Stopping."
393
+ )
394
+ break
395
+ else:
396
+ # Save to CSV only if no MongoDB
397
+ log_row = [round(fitness, 5)] + [mutated_hyp[k] for k in self.space.keys()]
398
+ headers = "" if self.tune_csv.exists() else (",".join(["fitness"] + list(self.space.keys())) + "\n")
399
+ with open(self.tune_csv, "a", encoding="utf-8") as f:
400
+ f.write(headers + ",".join(map(str, log_row)) + "\n")
213
401
 
214
402
  # Get best results
215
403
  x = np.loadtxt(self.tune_csv, ndmin=2, delimiter=",", skiprows=1)
@@ -225,7 +413,7 @@ class Tuner:
225
413
  shutil.rmtree(weights_dir, ignore_errors=True) # remove iteration weights/ dir to reduce storage space
226
414
 
227
415
  # Plot tune results
228
- plot_tune_results(self.tune_csv)
416
+ plot_tune_results(str(self.tune_csv))
229
417
 
230
418
  # Save and print tune results
231
419
  header = (
@@ -1,9 +1,10 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import concurrent.futures
4
6
  import statistics
5
7
  import time
6
- from typing import List, Optional, Tuple
7
8
 
8
9
 
9
10
  class GCPRegions:
@@ -71,16 +72,16 @@ class GCPRegions:
71
72
  "us-west4": (2, "Las Vegas", "United States"),
72
73
  }
73
74
 
74
- def tier1(self) -> List[str]:
75
+ def tier1(self) -> list[str]:
75
76
  """Return a list of GCP regions classified as tier 1 based on predefined criteria."""
76
77
  return [region for region, info in self.regions.items() if info[0] == 1]
77
78
 
78
- def tier2(self) -> List[str]:
79
+ def tier2(self) -> list[str]:
79
80
  """Return a list of GCP regions classified as tier 2 based on predefined criteria."""
80
81
  return [region for region, info in self.regions.items() if info[0] == 2]
81
82
 
82
83
  @staticmethod
83
- def _ping_region(region: str, attempts: int = 1) -> Tuple[str, float, float, float, float]:
84
+ def _ping_region(region: str, attempts: int = 1) -> tuple[str, float, float, float, float]:
84
85
  """
85
86
  Ping a specified GCP region and measure network latency statistics.
86
87
 
@@ -122,9 +123,9 @@ class GCPRegions:
122
123
  self,
123
124
  top: int = 1,
124
125
  verbose: bool = False,
125
- tier: Optional[int] = None,
126
+ tier: int | None = None,
126
127
  attempts: int = 1,
127
- ) -> List[Tuple[str, float, float, float, float]]:
128
+ ) -> list[tuple[str, float, float, float, float]]:
128
129
  """
129
130
  Determine the GCP regions with the lowest latency based on ping tests.
130
131
 
@@ -1,11 +1,13 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import shutil
4
6
  import threading
5
7
  import time
6
8
  from http import HTTPStatus
7
9
  from pathlib import Path
8
- from typing import Any, Dict, Optional
10
+ from typing import Any
9
11
  from urllib.parse import parse_qs, urlparse
10
12
 
11
13
  from ultralytics import __version__
@@ -90,7 +92,7 @@ class HUBTrainingSession:
90
92
  )
91
93
 
92
94
  @classmethod
93
- def create_session(cls, identifier: str, args: Optional[Dict[str, Any]] = None):
95
+ def create_session(cls, identifier: str, args: dict[str, Any] | None = None):
94
96
  """
95
97
  Create an authenticated HUBTrainingSession or return None.
96
98
 
@@ -137,7 +139,7 @@ class HUBTrainingSession:
137
139
  self.model.start_heartbeat(self.rate_limits["heartbeat"])
138
140
  LOGGER.info(f"{PREFIX}View model at {self.model_url} 🚀")
139
141
 
140
- def create_model(self, model_args: Dict[str, Any]):
142
+ def create_model(self, model_args: dict[str, Any]):
141
143
  """
142
144
  Initialize a HUB training session with the specified model arguments.
143
145
 
@@ -204,7 +206,7 @@ class HUBTrainingSession:
204
206
  HUBModelError: If the identifier format is not recognized.
205
207
  """
206
208
  api_key, model_id, filename = None, None, None
207
- if str(identifier).endswith((".pt", ".yaml")):
209
+ if identifier.endswith((".pt", ".yaml")):
208
210
  filename = identifier
209
211
  elif identifier.startswith(f"{HUB_WEB_ROOT}/models/"):
210
212
  parsed_url = urlparse(identifier)
@@ -254,8 +256,8 @@ class HUBTrainingSession:
254
256
  timeout: int = 30,
255
257
  thread: bool = True,
256
258
  verbose: bool = True,
257
- progress_total: Optional[int] = None,
258
- stream_response: Optional[bool] = None,
259
+ progress_total: int | None = None,
260
+ stream_response: bool | None = None,
259
261
  *args,
260
262
  **kwargs,
261
263
  ):
ultralytics/hub/utils.py CHANGED
@@ -11,8 +11,8 @@ from ultralytics import __version__
11
11
  from ultralytics.utils import (
12
12
  ARGV,
13
13
  ENVIRONMENT,
14
+ GIT,
14
15
  IS_COLAB,
15
- IS_GIT_DIR,
16
16
  IS_PIP_PACKAGE,
17
17
  LOGGER,
18
18
  ONLINE,
@@ -23,7 +23,6 @@ from ultralytics.utils import (
23
23
  TQDM,
24
24
  TryExcept,
25
25
  colorstr,
26
- get_git_origin_url,
27
26
  )
28
27
  from ultralytics.utils.downloads import GITHUB_ASSETS_NAMES
29
28
  from ultralytics.utils.torch_utils import get_cpu_info
@@ -205,7 +204,7 @@ class Events:
205
204
  self.t = 0.0 # rate limit timer (seconds)
206
205
  self.metadata = {
207
206
  "cli": Path(ARGV[0]).name == "yolo",
208
- "install": "git" if IS_GIT_DIR else "pip" if IS_PIP_PACKAGE else "other",
207
+ "install": "git" if GIT.is_repo else "pip" if IS_PIP_PACKAGE else "other",
209
208
  "python": PYTHON_VERSION.rsplit(".", 1)[0], # i.e. 3.13
210
209
  "CPU": get_cpu_info(),
211
210
  # "GPU": get_gpu_info(index=0) if cuda else None,
@@ -219,7 +218,7 @@ class Events:
219
218
  and RANK in {-1, 0}
220
219
  and not TESTS_RUNNING
221
220
  and ONLINE
222
- and (IS_PIP_PACKAGE or get_git_origin_url() == "https://github.com/ultralytics/ultralytics.git")
221
+ and (IS_PIP_PACKAGE or GIT.origin == "https://github.com/ultralytics/ultralytics.git")
223
222
  )
224
223
 
225
224
  def __call__(self, cfg, device=None):
@@ -1,7 +1,9 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from pathlib import Path
4
- from typing import Any, Dict, List, Optional
6
+ from typing import Any
5
7
 
6
8
  from ultralytics.engine.model import Model
7
9
 
@@ -45,10 +47,10 @@ class FastSAM(Model):
45
47
  self,
46
48
  source,
47
49
  stream: bool = False,
48
- bboxes: Optional[List] = None,
49
- points: Optional[List] = None,
50
- labels: Optional[List] = None,
51
- texts: Optional[List] = None,
50
+ bboxes: list | None = None,
51
+ points: list | None = None,
52
+ labels: list | None = None,
53
+ texts: list | None = None,
52
54
  **kwargs: Any,
53
55
  ):
54
56
  """
@@ -74,6 +76,6 @@ class FastSAM(Model):
74
76
  return super().predict(source, stream, prompts=prompts, **kwargs)
75
77
 
76
78
  @property
77
- def task_map(self) -> Dict[str, Dict[str, Any]]:
79
+ def task_map(self) -> dict[str, dict[str, Any]]:
78
80
  """Returns a dictionary mapping segment task to corresponding predictor and validator classes."""
79
81
  return {"segment": {"predictor": FastSAMPredictor, "validator": FastSAMValidator}}
@@ -1,7 +1,9 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from pathlib import Path
4
- from typing import Any, Dict
6
+ from typing import Any
5
7
 
6
8
  import torch
7
9
 
@@ -80,7 +82,7 @@ class NAS(Model):
80
82
  self.model.args = {**DEFAULT_CFG_DICT, **self.overrides} # for export()
81
83
  self.model.eval()
82
84
 
83
- def info(self, detailed: bool = False, verbose: bool = True) -> Dict[str, Any]:
85
+ def info(self, detailed: bool = False, verbose: bool = True) -> dict[str, Any]:
84
86
  """
85
87
  Log model information.
86
88
 
@@ -94,6 +96,6 @@ class NAS(Model):
94
96
  return model_info(self.model, detailed=detailed, verbose=verbose, imgsz=640)
95
97
 
96
98
  @property
97
- def task_map(self) -> Dict[str, Dict[str, Any]]:
99
+ def task_map(self) -> dict[str, dict[str, Any]]:
98
100
  """Return a dictionary mapping tasks to respective predictor and validator classes."""
99
101
  return {"detect": {"predictor": NASPredictor, "validator": NASValidator}}
@@ -1,7 +1,8 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from copy import copy
4
- from typing import Optional
5
6
 
6
7
  from ultralytics.models.yolo.detect import DetectionTrainer
7
8
  from ultralytics.nn.tasks import RTDETRDetectionModel
@@ -41,7 +42,7 @@ class RTDETRTrainer(DetectionTrainer):
41
42
  >>> trainer.train()
42
43
  """
43
44
 
44
- def get_model(self, cfg: Optional[dict] = None, weights: Optional[str] = None, verbose: bool = True):
45
+ def get_model(self, cfg: dict | None = None, weights: str | None = None, verbose: bool = True):
45
46
  """
46
47
  Initialize and return an RT-DETR model for object detection tasks.
47
48
 
@@ -58,7 +59,7 @@ class RTDETRTrainer(DetectionTrainer):
58
59
  model.load(weights)
59
60
  return model
60
61
 
61
- def build_dataset(self, img_path: str, mode: str = "val", batch: Optional[int] = None):
62
+ def build_dataset(self, img_path: str, mode: str = "val", batch: int | None = None):
62
63
  """
63
64
  Build and return an RT-DETR dataset for training or validation.
64
65
 
@@ -1,7 +1,9 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from pathlib import Path
4
- from typing import Any, Dict, List, Tuple, Union
6
+ from typing import Any
5
7
 
6
8
  import torch
7
9
 
@@ -155,8 +157,8 @@ class RTDETRValidator(DetectionValidator):
155
157
  )
156
158
 
157
159
  def postprocess(
158
- self, preds: Union[torch.Tensor, List[torch.Tensor], Tuple[torch.Tensor]]
159
- ) -> List[Dict[str, torch.Tensor]]:
160
+ self, preds: torch.Tensor | list[torch.Tensor] | tuple[torch.Tensor]
161
+ ) -> list[dict[str, torch.Tensor]]:
160
162
  """
161
163
  Apply Non-maximum suppression to prediction outputs.
162
164
 
@@ -187,7 +189,7 @@ class RTDETRValidator(DetectionValidator):
187
189
 
188
190
  return [{"bboxes": x[:, :4], "conf": x[:, 4], "cls": x[:, 5]} for x in outputs]
189
191
 
190
- def pred_to_json(self, predn: Dict[str, torch.Tensor], pbatch: Dict[str, Any]) -> None:
192
+ def pred_to_json(self, predn: dict[str, torch.Tensor], pbatch: dict[str, Any]) -> None:
191
193
  """
192
194
  Serialize YOLO predictions to COCO json format.
193
195