ultralytics 8.3.191__py3-none-any.whl → 8.3.193__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/__init__.py +7 -5
- ultralytics/cfg/datasets/SKU-110K.yaml +1 -1
- ultralytics/cfg/datasets/xView.yaml +1 -1
- ultralytics/data/utils.py +1 -1
- ultralytics/engine/exporter.py +5 -4
- ultralytics/engine/model.py +4 -4
- ultralytics/engine/predictor.py +7 -3
- ultralytics/engine/trainer.py +5 -5
- ultralytics/engine/tuner.py +227 -40
- ultralytics/models/yolo/classify/train.py +2 -2
- ultralytics/models/yolo/classify/val.py +1 -1
- ultralytics/models/yolo/detect/val.py +1 -1
- ultralytics/models/yolo/pose/val.py +1 -1
- ultralytics/models/yolo/segment/val.py +14 -14
- ultralytics/models/yolo/world/train.py +1 -1
- ultralytics/models/yolo/yoloe/train.py +3 -4
- ultralytics/models/yolo/yoloe/val.py +3 -3
- ultralytics/nn/__init__.py +2 -4
- ultralytics/nn/autobackend.py +2 -2
- ultralytics/nn/tasks.py +2 -51
- ultralytics/utils/__init__.py +5 -1
- ultralytics/utils/checks.py +2 -1
- ultralytics/utils/plotting.py +2 -2
- ultralytics/utils/tal.py +2 -2
- ultralytics/utils/torch_utils.py +7 -6
- ultralytics/utils/tqdm.py +50 -74
- ultralytics/utils/tuner.py +1 -1
- {ultralytics-8.3.191.dist-info → ultralytics-8.3.193.dist-info}/METADATA +1 -1
- {ultralytics-8.3.191.dist-info → ultralytics-8.3.193.dist-info}/RECORD +34 -34
- {ultralytics-8.3.191.dist-info → ultralytics-8.3.193.dist-info}/WHEEL +0 -0
- {ultralytics-8.3.191.dist-info → ultralytics-8.3.193.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.3.191.dist-info → ultralytics-8.3.193.dist-info}/licenses/LICENSE +0 -0
- {ultralytics-8.3.191.dist-info → ultralytics-8.3.193.dist-info}/top_level.txt +0 -0
@@ -63,7 +63,7 @@ class SegmentationValidator(DetectionValidator):
|
|
63
63
|
(Dict[str, Any]): Preprocessed batch.
|
64
64
|
"""
|
65
65
|
batch = super().preprocess(batch)
|
66
|
-
batch["masks"] = batch["masks"].to(self.device).float()
|
66
|
+
batch["masks"] = batch["masks"].to(self.device, non_blocking=True).float()
|
67
67
|
return batch
|
68
68
|
|
69
69
|
def init_metrics(self, model: torch.nn.Module) -> None:
|
@@ -133,8 +133,17 @@ class SegmentationValidator(DetectionValidator):
|
|
133
133
|
(Dict[str, Any]): Prepared batch with processed annotations.
|
134
134
|
"""
|
135
135
|
prepared_batch = super()._prepare_batch(si, batch)
|
136
|
-
|
137
|
-
|
136
|
+
nl = len(prepared_batch["cls"])
|
137
|
+
if self.args.overlap_mask:
|
138
|
+
masks = batch["masks"][si]
|
139
|
+
index = torch.arange(1, nl + 1, device=masks.device).view(nl, 1, 1)
|
140
|
+
masks = (masks == index).float()
|
141
|
+
else:
|
142
|
+
masks = batch["masks"][batch["batch_idx"] == si]
|
143
|
+
if nl and self.process is ops.process_mask_native:
|
144
|
+
masks = F.interpolate(masks[None], prepared_batch["imgsz"], mode="bilinear", align_corners=False)[0]
|
145
|
+
masks = masks.gt_(0.5)
|
146
|
+
prepared_batch["masks"] = masks
|
138
147
|
return prepared_batch
|
139
148
|
|
140
149
|
def _process_batch(self, preds: dict[str, torch.Tensor], batch: dict[str, Any]) -> dict[str, np.ndarray]:
|
@@ -158,20 +167,11 @@ class SegmentationValidator(DetectionValidator):
|
|
158
167
|
>>> correct_preds = validator._process_batch(preds, batch)
|
159
168
|
"""
|
160
169
|
tp = super()._process_batch(preds, batch)
|
161
|
-
gt_cls
|
170
|
+
gt_cls = batch["cls"]
|
162
171
|
if len(gt_cls) == 0 or len(preds["cls"]) == 0:
|
163
172
|
tp_m = np.zeros((len(preds["cls"]), self.niou), dtype=bool)
|
164
173
|
else:
|
165
|
-
|
166
|
-
if self.args.overlap_mask:
|
167
|
-
nl = len(gt_cls)
|
168
|
-
index = torch.arange(nl, device=gt_masks.device).view(nl, 1, 1) + 1
|
169
|
-
gt_masks = gt_masks.repeat(nl, 1, 1) # shape(1,640,640) -> (n,640,640)
|
170
|
-
gt_masks = torch.where(gt_masks == index, 1.0, 0.0)
|
171
|
-
if gt_masks.shape[1:] != pred_masks.shape[1:]:
|
172
|
-
gt_masks = F.interpolate(gt_masks[None], pred_masks.shape[1:], mode="bilinear", align_corners=False)[0]
|
173
|
-
gt_masks = gt_masks.gt_(0.5)
|
174
|
-
iou = mask_iou(gt_masks.view(gt_masks.shape[0], -1), pred_masks.view(pred_masks.shape[0], -1))
|
174
|
+
iou = mask_iou(batch["masks"].flatten(1), preds["masks"].flatten(1))
|
175
175
|
tp_m = self.match_predictions(preds["cls"], gt_cls, iou).cpu().numpy()
|
176
176
|
tp.update({"tp_m": tp_m}) # update tp with mask IoU
|
177
177
|
return tp
|
@@ -171,7 +171,7 @@ class WorldTrainer(DetectionTrainer):
|
|
171
171
|
|
172
172
|
# Add text features
|
173
173
|
texts = list(itertools.chain(*batch["texts"]))
|
174
|
-
txt_feats = torch.stack([self.text_embeddings[text] for text in texts]).to(self.device)
|
174
|
+
txt_feats = torch.stack([self.text_embeddings[text] for text in texts]).to(self.device, non_blocking=True)
|
175
175
|
txt_feats = txt_feats / txt_feats.norm(p=2, dim=-1, keepdim=True)
|
176
176
|
batch["txt_feats"] = txt_feats.reshape(len(batch["texts"]), -1, txt_feats.shape[-1])
|
177
177
|
return batch
|
@@ -197,7 +197,7 @@ class YOLOETrainerFromScratch(YOLOETrainer, WorldTrainerFromScratch):
|
|
197
197
|
batch = DetectionTrainer.preprocess_batch(self, batch)
|
198
198
|
|
199
199
|
texts = list(itertools.chain(*batch["texts"]))
|
200
|
-
txt_feats = torch.stack([self.text_embeddings[text] for text in texts]).to(self.device)
|
200
|
+
txt_feats = torch.stack([self.text_embeddings[text] for text in texts]).to(self.device, non_blocking=True)
|
201
201
|
txt_feats = txt_feats.reshape(len(batch["texts"]), -1, txt_feats.shape[-1])
|
202
202
|
batch["txt_feats"] = txt_feats
|
203
203
|
return batch
|
@@ -251,8 +251,7 @@ class YOLOEPEFreeTrainer(YOLOEPETrainer, YOLOETrainerFromScratch):
|
|
251
251
|
|
252
252
|
def preprocess_batch(self, batch):
|
253
253
|
"""Preprocess a batch of images for YOLOE training, adjusting formatting and dimensions as needed."""
|
254
|
-
|
255
|
-
return batch
|
254
|
+
return DetectionTrainer.preprocess_batch(self, batch)
|
256
255
|
|
257
256
|
def set_text_embeddings(self, datasets, batch: int):
|
258
257
|
"""
|
@@ -318,5 +317,5 @@ class YOLOEVPTrainer(YOLOETrainerFromScratch):
|
|
318
317
|
def preprocess_batch(self, batch):
|
319
318
|
"""Preprocess a batch of images for YOLOE training, moving visual prompts to the appropriate device."""
|
320
319
|
batch = super().preprocess_batch(batch)
|
321
|
-
batch["visuals"] = batch["visuals"].to(self.device)
|
320
|
+
batch["visuals"] = batch["visuals"].to(self.device, non_blocking=True)
|
322
321
|
return batch
|
@@ -102,7 +102,7 @@ class YOLOEDetectValidator(DetectionValidator):
|
|
102
102
|
"""Preprocess batch data, ensuring visuals are on the same device as images."""
|
103
103
|
batch = super().preprocess(batch)
|
104
104
|
if "visuals" in batch:
|
105
|
-
batch["visuals"] = batch["visuals"].to(batch["img"].device)
|
105
|
+
batch["visuals"] = batch["visuals"].to(batch["img"].device, non_blocking=True)
|
106
106
|
return batch
|
107
107
|
|
108
108
|
def get_vpe_dataloader(self, data: dict[str, Any]) -> torch.utils.data.DataLoader:
|
@@ -186,9 +186,9 @@ class YOLOEDetectValidator(DetectionValidator):
|
|
186
186
|
self.device = select_device(self.args.device, verbose=False)
|
187
187
|
|
188
188
|
if isinstance(model, (str, Path)):
|
189
|
-
from ultralytics.nn.tasks import
|
189
|
+
from ultralytics.nn.tasks import load_checkpoint
|
190
190
|
|
191
|
-
model =
|
191
|
+
model, _ = load_checkpoint(model, device=self.device) # model, ckpt
|
192
192
|
model.eval().to(self.device)
|
193
193
|
data = check_det_dataset(refer_data or self.args.data)
|
194
194
|
names = [name.split("/", 1)[0] for name in list(data["names"].values())]
|
ultralytics/nn/__init__.py
CHANGED
@@ -5,18 +5,16 @@ from .tasks import (
|
|
5
5
|
ClassificationModel,
|
6
6
|
DetectionModel,
|
7
7
|
SegmentationModel,
|
8
|
-
attempt_load_one_weight,
|
9
|
-
attempt_load_weights,
|
10
8
|
guess_model_scale,
|
11
9
|
guess_model_task,
|
10
|
+
load_checkpoint,
|
12
11
|
parse_model,
|
13
12
|
torch_safe_load,
|
14
13
|
yaml_model_load,
|
15
14
|
)
|
16
15
|
|
17
16
|
__all__ = (
|
18
|
-
"
|
19
|
-
"attempt_load_weights",
|
17
|
+
"load_checkpoint",
|
20
18
|
"parse_model",
|
21
19
|
"yaml_model_load",
|
22
20
|
"guess_model_task",
|
ultralytics/nn/autobackend.py
CHANGED
@@ -203,9 +203,9 @@ class AutoBackend(nn.Module):
|
|
203
203
|
model = model.fuse(verbose=verbose)
|
204
204
|
model = model.to(device)
|
205
205
|
else: # pt file
|
206
|
-
from ultralytics.nn.tasks import
|
206
|
+
from ultralytics.nn.tasks import load_checkpoint
|
207
207
|
|
208
|
-
model, _ =
|
208
|
+
model, _ = load_checkpoint(model, device=device, fuse=fuse) # load model, ckpt
|
209
209
|
|
210
210
|
# Common PyTorch model processing
|
211
211
|
if hasattr(model, "kpt_shape"):
|
ultralytics/nn/tasks.py
CHANGED
@@ -1483,61 +1483,12 @@ def torch_safe_load(weight, safe_only=False):
|
|
1483
1483
|
return ckpt, file
|
1484
1484
|
|
1485
1485
|
|
1486
|
-
def
|
1487
|
-
"""
|
1488
|
-
Load an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a.
|
1489
|
-
|
1490
|
-
Args:
|
1491
|
-
weights (str | List[str]): Model weights path(s).
|
1492
|
-
device (torch.device, optional): Device to load model to.
|
1493
|
-
inplace (bool): Whether to do inplace operations.
|
1494
|
-
fuse (bool): Whether to fuse model.
|
1495
|
-
|
1496
|
-
Returns:
|
1497
|
-
(torch.nn.Module): Loaded model.
|
1498
|
-
"""
|
1499
|
-
ensemble = Ensemble()
|
1500
|
-
for w in weights if isinstance(weights, list) else [weights]:
|
1501
|
-
ckpt, w = torch_safe_load(w) # load ckpt
|
1502
|
-
args = {**DEFAULT_CFG_DICT, **ckpt["train_args"]} if "train_args" in ckpt else None # combined args
|
1503
|
-
model = (ckpt.get("ema") or ckpt["model"]).float() # FP32 model
|
1504
|
-
|
1505
|
-
# Model compatibility updates
|
1506
|
-
model.args = args # attach args to model
|
1507
|
-
model.pt_path = w # attach *.pt file path to model
|
1508
|
-
model.task = getattr(model, "task", guess_model_task(model))
|
1509
|
-
if not hasattr(model, "stride"):
|
1510
|
-
model.stride = torch.tensor([32.0])
|
1511
|
-
|
1512
|
-
# Append
|
1513
|
-
ensemble.append((model.fuse().eval() if fuse and hasattr(model, "fuse") else model.eval()).to(device))
|
1514
|
-
|
1515
|
-
# Module updates
|
1516
|
-
for m in ensemble.modules():
|
1517
|
-
if hasattr(m, "inplace"):
|
1518
|
-
m.inplace = inplace
|
1519
|
-
elif isinstance(m, torch.nn.Upsample) and not hasattr(m, "recompute_scale_factor"):
|
1520
|
-
m.recompute_scale_factor = None # torch 1.11.0 compatibility
|
1521
|
-
|
1522
|
-
# Return model
|
1523
|
-
if len(ensemble) == 1:
|
1524
|
-
return ensemble[-1]
|
1525
|
-
|
1526
|
-
# Return ensemble
|
1527
|
-
LOGGER.info(f"Ensemble created with {weights}\n")
|
1528
|
-
for k in "names", "nc", "yaml":
|
1529
|
-
setattr(ensemble, k, getattr(ensemble[0], k))
|
1530
|
-
ensemble.stride = ensemble[int(torch.argmax(torch.tensor([m.stride.max() for m in ensemble])))].stride
|
1531
|
-
assert all(ensemble[0].nc == m.nc for m in ensemble), f"Models differ in class counts {[m.nc for m in ensemble]}"
|
1532
|
-
return ensemble
|
1533
|
-
|
1534
|
-
|
1535
|
-
def attempt_load_one_weight(weight, device=None, inplace=True, fuse=False):
|
1486
|
+
def load_checkpoint(weight, device=None, inplace=True, fuse=False):
|
1536
1487
|
"""
|
1537
1488
|
Load a single model weights.
|
1538
1489
|
|
1539
1490
|
Args:
|
1540
|
-
weight (str): Model weight path.
|
1491
|
+
weight (str | Path): Model weight path.
|
1541
1492
|
device (torch.device, optional): Device to load model to.
|
1542
1493
|
inplace (bool): Whether to do inplace operations.
|
1543
1494
|
fuse (bool): Whether to fuse model.
|
ultralytics/utils/__init__.py
CHANGED
@@ -49,7 +49,7 @@ MACOS_VERSION = platform.mac_ver()[0] if MACOS else None
|
|
49
49
|
NOT_MACOS14 = not (MACOS and MACOS_VERSION.startswith("14."))
|
50
50
|
ARM64 = platform.machine() in {"arm64", "aarch64"} # ARM64 booleans
|
51
51
|
PYTHON_VERSION = platform.python_version()
|
52
|
-
TORCH_VERSION = torch.__version__
|
52
|
+
TORCH_VERSION = str(torch.__version__) # Normalize torch.__version__ (PyTorch>1.9 returns TorchVersion objects)
|
53
53
|
TORCHVISION_VERSION = importlib.metadata.version("torchvision") # faster than importing torchvision
|
54
54
|
IS_VSCODE = os.environ.get("TERM_PROGRAM", False) == "vscode"
|
55
55
|
RKNN_CHIPS = frozenset(
|
@@ -132,6 +132,10 @@ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # suppress verbose TF compiler warning
|
|
132
132
|
os.environ["TORCH_CPP_LOG_LEVEL"] = "ERROR" # suppress "NNPACK.cpp could not initialize NNPACK" warnings
|
133
133
|
os.environ["KINETO_LOG_LEVEL"] = "5" # suppress verbose PyTorch profiler output when computing FLOPs
|
134
134
|
|
135
|
+
# Precompiled type tuples for faster isinstance() checks
|
136
|
+
FLOAT_OR_INT = (float, int)
|
137
|
+
STR_OR_PATH = (str, Path)
|
138
|
+
|
135
139
|
|
136
140
|
class DataExportMixin:
|
137
141
|
"""
|
ultralytics/utils/checks.py
CHANGED
@@ -36,6 +36,7 @@ from ultralytics.utils import (
|
|
36
36
|
PYTHON_VERSION,
|
37
37
|
RKNN_CHIPS,
|
38
38
|
ROOT,
|
39
|
+
TORCH_VERSION,
|
39
40
|
TORCHVISION_VERSION,
|
40
41
|
USER_CONFIG_DIR,
|
41
42
|
WINDOWS,
|
@@ -464,7 +465,7 @@ def check_torchvision():
|
|
464
465
|
}
|
465
466
|
|
466
467
|
# Check major and minor versions
|
467
|
-
v_torch = ".".join(
|
468
|
+
v_torch = ".".join(TORCH_VERSION.split("+", 1)[0].split(".")[:2])
|
468
469
|
if v_torch in compatibility_table:
|
469
470
|
compatible_versions = compatibility_table[v_torch]
|
470
471
|
v_torchvision = ".".join(TORCHVISION_VERSION.split("+", 1)[0].split(".")[:2])
|
ultralytics/utils/plotting.py
CHANGED
@@ -893,7 +893,7 @@ def plot_results(
|
|
893
893
|
assert len(files), f"No results.csv files found in {save_dir.resolve()}, nothing to plot."
|
894
894
|
for f in files:
|
895
895
|
try:
|
896
|
-
data = pl.read_csv(f)
|
896
|
+
data = pl.read_csv(f, infer_schema_length=None)
|
897
897
|
s = [x.strip() for x in data.columns]
|
898
898
|
x = data.select(data.columns[0]).to_numpy().flatten()
|
899
899
|
for i, j in enumerate(index):
|
@@ -971,7 +971,7 @@ def plot_tune_results(csv_file: str = "tune_results.csv"):
|
|
971
971
|
|
972
972
|
# Scatter plots for each hyperparameter
|
973
973
|
csv_file = Path(csv_file)
|
974
|
-
data = pl.read_csv(csv_file)
|
974
|
+
data = pl.read_csv(csv_file, infer_schema_length=None)
|
975
975
|
num_metrics_columns = 1
|
976
976
|
keys = [x.strip() for x in data.columns][num_metrics_columns:]
|
977
977
|
x = data.to_numpy()
|
ultralytics/utils/tal.py
CHANGED
@@ -3,12 +3,12 @@
|
|
3
3
|
import torch
|
4
4
|
import torch.nn as nn
|
5
5
|
|
6
|
-
from . import LOGGER
|
6
|
+
from . import LOGGER, TORCH_VERSION
|
7
7
|
from .checks import check_version
|
8
8
|
from .metrics import bbox_iou, probiou
|
9
9
|
from .ops import xywhr2xyxyxyxy
|
10
10
|
|
11
|
-
TORCH_1_10 = check_version(
|
11
|
+
TORCH_1_10 = check_version(TORCH_VERSION, "1.10.0")
|
12
12
|
|
13
13
|
|
14
14
|
class TaskAlignedAssigner(nn.Module):
|
ultralytics/utils/torch_utils.py
CHANGED
@@ -27,6 +27,7 @@ from ultralytics.utils import (
|
|
27
27
|
LOGGER,
|
28
28
|
NUM_THREADS,
|
29
29
|
PYTHON_VERSION,
|
30
|
+
TORCH_VERSION,
|
30
31
|
TORCHVISION_VERSION,
|
31
32
|
WINDOWS,
|
32
33
|
colorstr,
|
@@ -35,15 +36,15 @@ from ultralytics.utils.checks import check_version
|
|
35
36
|
from ultralytics.utils.patches import torch_load
|
36
37
|
|
37
38
|
# Version checks (all default to version>=min_version)
|
38
|
-
TORCH_1_9 = check_version(
|
39
|
-
TORCH_1_13 = check_version(
|
40
|
-
TORCH_2_0 = check_version(
|
41
|
-
TORCH_2_4 = check_version(
|
39
|
+
TORCH_1_9 = check_version(TORCH_VERSION, "1.9.0")
|
40
|
+
TORCH_1_13 = check_version(TORCH_VERSION, "1.13.0")
|
41
|
+
TORCH_2_0 = check_version(TORCH_VERSION, "2.0.0")
|
42
|
+
TORCH_2_4 = check_version(TORCH_VERSION, "2.4.0")
|
42
43
|
TORCHVISION_0_10 = check_version(TORCHVISION_VERSION, "0.10.0")
|
43
44
|
TORCHVISION_0_11 = check_version(TORCHVISION_VERSION, "0.11.0")
|
44
45
|
TORCHVISION_0_13 = check_version(TORCHVISION_VERSION, "0.13.0")
|
45
46
|
TORCHVISION_0_18 = check_version(TORCHVISION_VERSION, "0.18.0")
|
46
|
-
if WINDOWS and check_version(
|
47
|
+
if WINDOWS and check_version(TORCH_VERSION, "==2.4.0"): # reject version 2.4.0 on Windows
|
47
48
|
LOGGER.warning(
|
48
49
|
"Known issue with torch==2.4.0 on Windows with CPU, recommend upgrading to torch>=2.4.1 to resolve "
|
49
50
|
"https://github.com/ultralytics/ultralytics/issues/15049"
|
@@ -165,7 +166,7 @@ def select_device(device="", batch=0, newline=False, verbose=True):
|
|
165
166
|
if isinstance(device, torch.device) or str(device).startswith(("tpu", "intel")):
|
166
167
|
return device
|
167
168
|
|
168
|
-
s = f"Ultralytics {__version__} 🚀 Python-{PYTHON_VERSION} torch-{
|
169
|
+
s = f"Ultralytics {__version__} 🚀 Python-{PYTHON_VERSION} torch-{TORCH_VERSION} "
|
169
170
|
device = str(device).lower()
|
170
171
|
for remove in "cuda:", "none", "(", ")", "[", "]", "'", " ":
|
171
172
|
device = device.replace(remove, "") # to string, 'cuda:0' -> '0' and '(0, 1)' -> '0,1'
|
ultralytics/utils/tqdm.py
CHANGED
@@ -88,11 +88,11 @@ class TQDM:
|
|
88
88
|
mininterval: float = 0.1,
|
89
89
|
disable: bool | None = None,
|
90
90
|
unit: str = "it",
|
91
|
-
unit_scale: bool =
|
91
|
+
unit_scale: bool = True,
|
92
92
|
unit_divisor: int = 1000,
|
93
|
-
bar_format: str | None = None,
|
93
|
+
bar_format: str | None = None, # kept for API compatibility; not used for formatting
|
94
94
|
initial: int = 0,
|
95
|
-
**kwargs,
|
95
|
+
**kwargs,
|
96
96
|
) -> None:
|
97
97
|
"""
|
98
98
|
Initialize the TQDM progress bar with specified configuration options.
|
@@ -138,11 +138,8 @@ class TQDM:
|
|
138
138
|
self.mininterval = max(mininterval, self.NONINTERACTIVE_MIN_INTERVAL) if self.noninteractive else mininterval
|
139
139
|
self.initial = initial
|
140
140
|
|
141
|
-
#
|
142
|
-
|
143
|
-
self.bar_format = bar_format or "{desc}: {percent:.0f}% {bar} {n}/{total} {rate} {elapsed}<{remaining}"
|
144
|
-
else:
|
145
|
-
self.bar_format = bar_format or "{desc}: {bar} {n} {rate} {elapsed}"
|
141
|
+
# Kept for API compatibility (unused for f-string formatting)
|
142
|
+
self.bar_format = bar_format
|
146
143
|
|
147
144
|
self.file = file or sys.stdout
|
148
145
|
|
@@ -151,48 +148,31 @@ class TQDM:
|
|
151
148
|
self.last_print_n = self.initial
|
152
149
|
self.last_print_t = time.time()
|
153
150
|
self.start_t = time.time()
|
154
|
-
self.last_rate = 0
|
151
|
+
self.last_rate = 0.0
|
155
152
|
self.closed = False
|
153
|
+
self.is_bytes = unit_scale and unit in ("B", "bytes")
|
154
|
+
self.scales = (
|
155
|
+
[(1073741824, "GB/s"), (1048576, "MB/s"), (1024, "KB/s")]
|
156
|
+
if self.is_bytes
|
157
|
+
else [(1e9, f"G{self.unit}/s"), (1e6, f"M{self.unit}/s"), (1e3, f"K{self.unit}/s")]
|
158
|
+
)
|
156
159
|
|
157
|
-
# Display initial bar if we have total and not disabled
|
158
160
|
if not self.disable and self.total and not self.noninteractive:
|
159
161
|
self._display()
|
160
162
|
|
161
163
|
def _format_rate(self, rate: float) -> str:
|
162
|
-
"""Format rate with
|
164
|
+
"""Format rate with units."""
|
163
165
|
if rate <= 0:
|
164
166
|
return ""
|
167
|
+
fallback = f"{rate:.1f}B/s" if self.is_bytes else f"{rate:.1f}{self.unit}/s"
|
168
|
+
return next((f"{rate / t:.1f}{u}" for t, u in self.scales if rate >= t), fallback)
|
165
169
|
|
166
|
-
|
167
|
-
if self.unit in ("B", "bytes") and self.unit_scale:
|
168
|
-
return next(
|
169
|
-
(
|
170
|
-
f"{rate / threshold:.1f}{unit}"
|
171
|
-
for threshold, unit in [
|
172
|
-
(1073741824, "GB/s"),
|
173
|
-
(1048576, "MB/s"),
|
174
|
-
(1024, "KB/s"),
|
175
|
-
]
|
176
|
-
if rate >= threshold
|
177
|
-
),
|
178
|
-
f"{rate:.1f}B/s",
|
179
|
-
)
|
180
|
-
# For other scalable units, use decimal units
|
181
|
-
if self.unit_scale and self.unit in ("it", "items", ""):
|
182
|
-
for threshold, prefix in [(1000000, "M"), (1000, "K")]:
|
183
|
-
if rate >= threshold:
|
184
|
-
return f"{rate / threshold:.1f}{prefix}{self.unit}/s"
|
185
|
-
|
186
|
-
# Default formatting
|
187
|
-
precision = ".1f" if rate >= 1 else ".2f"
|
188
|
-
return f"{rate:{precision}}{self.unit}/s"
|
189
|
-
|
190
|
-
def _format_num(self, num: int) -> str:
|
170
|
+
def _format_num(self, num: int | float) -> str:
|
191
171
|
"""Format number with optional unit scaling."""
|
192
|
-
if not self.unit_scale or self.
|
172
|
+
if not self.unit_scale or not self.is_bytes:
|
193
173
|
return str(num)
|
194
174
|
|
195
|
-
for unit in
|
175
|
+
for unit in ("", "K", "M", "G", "T"):
|
196
176
|
if abs(num) < self.unit_divisor:
|
197
177
|
return f"{num:3.1f}{unit}B" if unit else f"{num:.0f}B"
|
198
178
|
num /= self.unit_divisor
|
@@ -224,8 +204,7 @@ class TQDM:
|
|
224
204
|
"""Check if display should update."""
|
225
205
|
if self.noninteractive:
|
226
206
|
return False
|
227
|
-
|
228
|
-
return True if self.total and self.n >= self.total else dt >= self.mininterval
|
207
|
+
return (self.total is not None and self.n >= self.total) or (dt >= self.mininterval)
|
229
208
|
|
230
209
|
def _display(self, final: bool = False) -> None:
|
231
210
|
"""Display progress bar."""
|
@@ -240,8 +219,8 @@ class TQDM:
|
|
240
219
|
return
|
241
220
|
|
242
221
|
# Calculate rate (avoid crazy numbers)
|
243
|
-
if dt > self.MIN_RATE_CALC_INTERVAL:
|
244
|
-
rate = dn / dt
|
222
|
+
if dt > self.MIN_RATE_CALC_INTERVAL:
|
223
|
+
rate = dn / dt if dt else 0.0
|
245
224
|
# Smooth rate for reasonable values, use raw rate for very high values
|
246
225
|
if rate < self.MAX_SMOOTHED_RATE:
|
247
226
|
self.last_rate = self.RATE_SMOOTHING_FACTOR * rate + (1 - self.RATE_SMOOTHING_FACTOR) * self.last_rate
|
@@ -249,8 +228,8 @@ class TQDM:
|
|
249
228
|
else:
|
250
229
|
rate = self.last_rate
|
251
230
|
|
252
|
-
# At completion, use
|
253
|
-
if self.
|
231
|
+
# At completion, use overall rate
|
232
|
+
if self.total and self.n >= self.total:
|
254
233
|
overall_elapsed = current_time - self.start_t
|
255
234
|
if overall_elapsed > 0:
|
256
235
|
rate = self.n / overall_elapsed
|
@@ -260,44 +239,41 @@ class TQDM:
|
|
260
239
|
self.last_print_t = current_time
|
261
240
|
elapsed = current_time - self.start_t
|
262
241
|
|
263
|
-
#
|
242
|
+
# Remaining time
|
264
243
|
remaining_str = ""
|
265
|
-
if self.total and 0 < self.n < self.total and
|
266
|
-
|
244
|
+
if self.total and 0 < self.n < self.total and elapsed > 0:
|
245
|
+
est_rate = rate or (self.n / elapsed)
|
246
|
+
remaining_str = f"<{self._format_time((self.total - self.n) / est_rate)}"
|
267
247
|
|
268
|
-
#
|
248
|
+
# Numbers and percent
|
269
249
|
if self.total:
|
270
250
|
percent = (self.n / self.total) * 100
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
251
|
+
n_str = self._format_num(self.n)
|
252
|
+
t_str = self._format_num(self.total)
|
253
|
+
if self.is_bytes:
|
254
|
+
# Collapse suffix only when identical (e.g. "5.4/5.4MB")
|
255
|
+
if n_str[-2] == t_str[-2]:
|
256
|
+
n_str = n_str.rstrip("KMGTPB") # Remove unit suffix from current if different than total
|
276
257
|
else:
|
277
|
-
percent = 0
|
278
|
-
|
279
|
-
total = "?"
|
258
|
+
percent = 0.0
|
259
|
+
n_str, t_str = self._format_num(self.n), "?"
|
280
260
|
|
281
261
|
elapsed_str = self._format_time(elapsed)
|
262
|
+
rate_str = self._format_rate(rate) or (self._format_rate(self.n / elapsed) if elapsed > 0 else "")
|
282
263
|
|
283
|
-
|
284
|
-
|
285
|
-
|
264
|
+
bar = self._generate_bar()
|
265
|
+
|
266
|
+
# Compose progress line via f-strings (two shapes: with/without total)
|
267
|
+
if self.total:
|
268
|
+
if self.is_bytes and self.n >= self.total:
|
269
|
+
# Completed bytes: show only final size
|
270
|
+
progress_str = f"{self.desc}: {percent:.0f}% {bar} {t_str} {rate_str} {elapsed_str}"
|
271
|
+
else:
|
272
|
+
progress_str = (
|
273
|
+
f"{self.desc}: {percent:.0f}% {bar} {n_str}/{t_str} {rate_str} {elapsed_str}{remaining_str}"
|
274
|
+
)
|
286
275
|
else:
|
287
|
-
|
288
|
-
|
289
|
-
# Format progress string
|
290
|
-
progress_str = format_str.format(
|
291
|
-
desc=self.desc,
|
292
|
-
percent=percent,
|
293
|
-
bar=self._generate_bar(),
|
294
|
-
n=n,
|
295
|
-
total=total,
|
296
|
-
rate=self._format_rate(rate) or (self._format_rate(self.n / elapsed) if elapsed > 0 else ""),
|
297
|
-
remaining=remaining_str,
|
298
|
-
elapsed=elapsed_str,
|
299
|
-
unit=self.unit,
|
300
|
-
)
|
276
|
+
progress_str = f"{self.desc}: {bar} {n_str} {rate_str} {elapsed_str}"
|
301
277
|
|
302
278
|
# Write to output
|
303
279
|
try:
|
@@ -335,7 +311,7 @@ class TQDM:
|
|
335
311
|
if self.closed:
|
336
312
|
return
|
337
313
|
|
338
|
-
self.closed = True
|
314
|
+
self.closed = True
|
339
315
|
|
340
316
|
if not self.disable:
|
341
317
|
# Final display
|
ultralytics/utils/tuner.py
CHANGED
@@ -129,7 +129,7 @@ def run_ray_tune(
|
|
129
129
|
{**train_args, **{"exist_ok": train_args.pop("resume", False)}}, # resume w/ same tune_dir
|
130
130
|
),
|
131
131
|
name=train_args.pop("name", "tune"), # runs/{task}/{tune_dir}
|
132
|
-
)
|
132
|
+
) # must be absolute dir
|
133
133
|
tune_dir.mkdir(parents=True, exist_ok=True)
|
134
134
|
if tune.Tuner.can_restore(tune_dir):
|
135
135
|
LOGGER.info(f"{colorstr('Tuner: ')} Resuming tuning run {tune_dir}...")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ultralytics
|
3
|
-
Version: 8.3.
|
3
|
+
Version: 8.3.193
|
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>
|