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.
- {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/METADATA +1 -1
- {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/RECORD +103 -102
- tests/test_cuda.py +6 -5
- tests/test_exports.py +1 -6
- tests/test_python.py +1 -4
- tests/test_solutions.py +1 -1
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/__init__.py +16 -14
- ultralytics/cfg/datasets/SKU-110K.yaml +1 -1
- ultralytics/cfg/datasets/VisDrone.yaml +4 -4
- ultralytics/data/annotator.py +6 -6
- ultralytics/data/augment.py +53 -51
- ultralytics/data/base.py +15 -13
- ultralytics/data/build.py +7 -4
- ultralytics/data/converter.py +9 -10
- ultralytics/data/dataset.py +24 -22
- ultralytics/data/loaders.py +13 -11
- ultralytics/data/split.py +4 -3
- ultralytics/data/split_dota.py +14 -12
- ultralytics/data/utils.py +29 -23
- ultralytics/engine/exporter.py +2 -2
- ultralytics/engine/model.py +16 -14
- ultralytics/engine/predictor.py +8 -6
- ultralytics/engine/results.py +54 -52
- ultralytics/engine/trainer.py +8 -3
- ultralytics/engine/tuner.py +230 -42
- ultralytics/hub/google/__init__.py +7 -6
- ultralytics/hub/session.py +8 -6
- ultralytics/hub/utils.py +3 -4
- ultralytics/models/fastsam/model.py +8 -6
- ultralytics/models/nas/model.py +5 -3
- ultralytics/models/rtdetr/train.py +4 -3
- ultralytics/models/rtdetr/val.py +6 -4
- ultralytics/models/sam/amg.py +13 -10
- ultralytics/models/sam/model.py +3 -2
- ultralytics/models/sam/modules/blocks.py +21 -21
- ultralytics/models/sam/modules/decoders.py +11 -11
- ultralytics/models/sam/modules/encoders.py +25 -25
- ultralytics/models/sam/modules/memory_attention.py +9 -8
- ultralytics/models/sam/modules/sam.py +8 -10
- ultralytics/models/sam/modules/tiny_encoder.py +21 -20
- ultralytics/models/sam/modules/transformer.py +6 -5
- ultralytics/models/sam/modules/utils.py +7 -5
- ultralytics/models/sam/predict.py +32 -31
- ultralytics/models/utils/loss.py +29 -27
- ultralytics/models/utils/ops.py +10 -8
- ultralytics/models/yolo/classify/train.py +9 -7
- ultralytics/models/yolo/classify/val.py +11 -9
- ultralytics/models/yolo/detect/predict.py +1 -1
- ultralytics/models/yolo/detect/train.py +8 -6
- ultralytics/models/yolo/detect/val.py +22 -20
- ultralytics/models/yolo/model.py +14 -14
- ultralytics/models/yolo/obb/train.py +5 -3
- ultralytics/models/yolo/obb/val.py +11 -9
- ultralytics/models/yolo/pose/train.py +7 -5
- ultralytics/models/yolo/pose/val.py +12 -10
- ultralytics/models/yolo/segment/train.py +4 -5
- ultralytics/models/yolo/segment/val.py +13 -11
- ultralytics/models/yolo/world/train.py +10 -8
- ultralytics/models/yolo/yoloe/train.py +10 -10
- ultralytics/models/yolo/yoloe/val.py +11 -9
- ultralytics/nn/autobackend.py +17 -19
- ultralytics/nn/modules/block.py +12 -12
- ultralytics/nn/modules/conv.py +4 -3
- ultralytics/nn/modules/head.py +41 -37
- ultralytics/nn/modules/transformer.py +22 -21
- ultralytics/nn/tasks.py +2 -2
- ultralytics/nn/text_model.py +6 -5
- ultralytics/solutions/analytics.py +7 -5
- ultralytics/solutions/config.py +12 -10
- ultralytics/solutions/distance_calculation.py +3 -3
- ultralytics/solutions/heatmap.py +4 -2
- ultralytics/solutions/object_counter.py +5 -3
- ultralytics/solutions/parking_management.py +4 -2
- ultralytics/solutions/region_counter.py +7 -5
- ultralytics/solutions/similarity_search.py +5 -3
- ultralytics/solutions/solutions.py +38 -36
- ultralytics/solutions/streamlit_inference.py +8 -7
- ultralytics/trackers/bot_sort.py +11 -9
- ultralytics/trackers/byte_tracker.py +17 -15
- ultralytics/trackers/utils/gmc.py +4 -3
- ultralytics/utils/__init__.py +16 -88
- ultralytics/utils/autobatch.py +3 -2
- ultralytics/utils/autodevice.py +10 -10
- ultralytics/utils/benchmarks.py +11 -10
- ultralytics/utils/callbacks/comet.py +9 -9
- ultralytics/utils/checks.py +17 -26
- ultralytics/utils/export.py +12 -11
- ultralytics/utils/files.py +8 -7
- ultralytics/utils/git.py +139 -0
- ultralytics/utils/instance.py +8 -7
- ultralytics/utils/loss.py +15 -13
- ultralytics/utils/metrics.py +62 -62
- ultralytics/utils/ops.py +3 -2
- ultralytics/utils/patches.py +6 -4
- ultralytics/utils/plotting.py +20 -18
- ultralytics/utils/torch_utils.py +4 -2
- ultralytics/utils/tqdm.py +18 -14
- ultralytics/utils/triton.py +3 -2
- {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/WHEEL +0 -0
- {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/entry_points.txt +0 -0
- {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/licenses/LICENSE +0 -0
- {dgenerate_ultralytics_headless-8.3.190.dist-info → dgenerate_ultralytics_headless-8.3.192.dist-info}/top_level.txt +0 -0
ultralytics/engine/tuner.py
CHANGED
@@ -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
|
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 (
|
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
|
-
|
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={
|
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:
|
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 (
|
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,
|
112
|
-
|
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
|
-
(
|
286
|
+
(dict[str, float]): A dictionary containing mutated hyperparameters.
|
124
287
|
"""
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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)
|
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
|
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,
|
152
|
-
hyp[k] = max(hyp[k],
|
153
|
-
hyp[k] = min(hyp[k],
|
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
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
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
|
384
|
+
# Save results - MongoDB takes precedence
|
208
385
|
fitness = metrics.get("fitness", 0.0)
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
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) ->
|
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) ->
|
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) ->
|
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:
|
126
|
+
tier: int | None = None,
|
126
127
|
attempts: int = 1,
|
127
|
-
) ->
|
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
|
|
ultralytics/hub/session.py
CHANGED
@@ -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
|
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:
|
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:
|
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
|
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:
|
258
|
-
stream_response:
|
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
|
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
|
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
|
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:
|
49
|
-
points:
|
50
|
-
labels:
|
51
|
-
texts:
|
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) ->
|
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}}
|
ultralytics/models/nas/model.py
CHANGED
@@ -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
|
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) ->
|
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) ->
|
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:
|
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:
|
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
|
|
ultralytics/models/rtdetr/val.py
CHANGED
@@ -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
|
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:
|
159
|
-
) ->
|
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:
|
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
|
|