mirror of
https://github.com/ultralytics/ultralytics
synced 2026-04-21 14:07:18 +00:00
ultralytics 8.3.243 Deduplicate ConsoleLogger progress bars (#23082)
Signed-off-by: Glenn Jocher <glenn.jocher@ultralytics.com> Co-authored-by: UltralyticsAssistant <web@ultralytics.com>
This commit is contained in:
parent
7b1ba7cab8
commit
e3a987c2a7
5 changed files with 103 additions and 8 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -199,3 +199,7 @@ calibration_*.npy
|
||||||
*.heic
|
*.heic
|
||||||
*.ico
|
*.ico
|
||||||
*.raw
|
*.raw
|
||||||
|
|
||||||
|
# Training logs
|
||||||
|
args.yaml
|
||||||
|
results.csv
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@ keywords: platform callbacks, training callbacks, console logging, YOLO11 traini
|
||||||
|
|
||||||
<br><br><hr><br>
|
<br><br><hr><br>
|
||||||
|
|
||||||
|
## ::: ultralytics.utils.callbacks.platform._get_environment_info
|
||||||
|
|
||||||
|
<br><br><hr><br>
|
||||||
|
|
||||||
## ::: ultralytics.utils.callbacks.platform.on_pretrain_routine_start
|
## ::: ultralytics.utils.callbacks.platform.on_pretrain_routine_start
|
||||||
|
|
||||||
<br><br><hr><br>
|
<br><br><hr><br>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||||
|
|
||||||
__version__ = "8.3.242"
|
__version__ = "8.3.243"
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from ultralytics.utils import LOGGER, RANK, SETTINGS, TESTS_RUNNING
|
from ultralytics.utils import ENVIRONMENT, GIT, LOGGER, PYTHON_VERSION, RANK, SETTINGS, TESTS_RUNNING
|
||||||
|
|
||||||
_last_upload = 0 # Rate limit model uploads
|
_last_upload = 0 # Rate limit model uploads
|
||||||
_console_logger = None # Global console logger instance
|
_console_logger = None # Global console logger instance
|
||||||
|
|
@ -85,13 +88,58 @@ def _upload_model_async(model_path, project, name):
|
||||||
_executor.submit(_upload_model, model_path, project, name)
|
_executor.submit(_upload_model, model_path, project, name)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_environment_info():
|
||||||
|
"""Collect comprehensive environment info using existing ultralytics utilities."""
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from ultralytics import __version__
|
||||||
|
from ultralytics.utils.torch_utils import get_cpu_info, get_gpu_info
|
||||||
|
|
||||||
|
env = {
|
||||||
|
"ultralyticsVersion": __version__,
|
||||||
|
"hostname": socket.gethostname(),
|
||||||
|
"os": platform.platform(),
|
||||||
|
"environment": ENVIRONMENT,
|
||||||
|
"pythonVersion": PYTHON_VERSION,
|
||||||
|
"pythonExecutable": sys.executable,
|
||||||
|
"cpuCount": os.cpu_count() or 0,
|
||||||
|
"cpu": get_cpu_info(),
|
||||||
|
"command": " ".join(sys.argv),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Git info using cached GIT singleton (no subprocess calls)
|
||||||
|
try:
|
||||||
|
if GIT.is_repo:
|
||||||
|
if GIT.origin:
|
||||||
|
env["gitRepository"] = GIT.origin
|
||||||
|
if GIT.branch:
|
||||||
|
env["gitBranch"] = GIT.branch
|
||||||
|
if GIT.commit:
|
||||||
|
env["gitCommit"] = GIT.commit[:12] # Short hash
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# GPU info
|
||||||
|
try:
|
||||||
|
if torch.cuda.is_available():
|
||||||
|
env["gpuCount"] = torch.cuda.device_count()
|
||||||
|
env["gpuType"] = get_gpu_info(0) if torch.cuda.device_count() > 0 else None
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
def on_pretrain_routine_start(trainer):
|
def on_pretrain_routine_start(trainer):
|
||||||
"""Initialize Platform logging at training start."""
|
"""Initialize Platform logging at training start."""
|
||||||
global _console_logger
|
global _console_logger, _last_upload
|
||||||
|
|
||||||
if RANK not in {-1, 0} or not trainer.args.project:
|
if RANK not in {-1, 0} or not trainer.args.project:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Initialize upload timer to now so first checkpoint waits 15 min from training start
|
||||||
|
_last_upload = time()
|
||||||
|
|
||||||
project, name = str(trainer.args.project), str(trainer.args.name or "train")
|
project, name = str(trainer.args.project), str(trainer.args.name or "train")
|
||||||
LOGGER.info(f"Platform: Streaming to project '{project}' as '{name}'")
|
LOGGER.info(f"Platform: Streaming to project '{project}' as '{name}'")
|
||||||
|
|
||||||
|
|
@ -104,12 +152,29 @@ def on_pretrain_routine_start(trainer):
|
||||||
_console_logger = ConsoleLogger(batch_size=5, flush_interval=5.0, on_flush=send_console_output)
|
_console_logger = ConsoleLogger(batch_size=5, flush_interval=5.0, on_flush=send_console_output)
|
||||||
_console_logger.start_capture()
|
_console_logger.start_capture()
|
||||||
|
|
||||||
|
# Gather model info for richer metadata
|
||||||
|
model_info = {}
|
||||||
|
try:
|
||||||
|
info = model_info_for_loggers(trainer)
|
||||||
|
model_info = {
|
||||||
|
"parameters": info.get("model/parameters", 0),
|
||||||
|
"gflops": info.get("model/GFLOPs", 0),
|
||||||
|
"classes": getattr(trainer.model, "yaml", {}).get("nc", 0), # number of classes
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Collect environment info (W&B-style metadata)
|
||||||
|
environment = _get_environment_info()
|
||||||
|
|
||||||
_send_async(
|
_send_async(
|
||||||
"training_started",
|
"training_started",
|
||||||
{
|
{
|
||||||
"trainArgs": {k: str(v) for k, v in vars(trainer.args).items()},
|
"trainArgs": {k: str(v) for k, v in vars(trainer.args).items()},
|
||||||
"epochs": trainer.epochs,
|
"epochs": trainer.epochs,
|
||||||
"device": str(trainer.device),
|
"device": str(trainer.device),
|
||||||
|
"modelInfo": model_info,
|
||||||
|
"environment": environment,
|
||||||
},
|
},
|
||||||
project,
|
project,
|
||||||
name,
|
name,
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class ConsoleLogger:
|
||||||
# Deduplication state
|
# Deduplication state
|
||||||
self.last_line = ""
|
self.last_line = ""
|
||||||
self.last_time = 0.0
|
self.last_time = 0.0
|
||||||
self.last_progress_line = "" # Track last progress line for deduplication
|
self.last_progress_line = "" # Track progress sequence key for deduplication
|
||||||
self.last_was_progress = False # Track if last line was a progress bar
|
self.last_was_progress = False # Track if last line was a progress bar
|
||||||
|
|
||||||
def start_capture(self):
|
def start_capture(self):
|
||||||
|
|
@ -146,12 +146,34 @@ class ConsoleLogger:
|
||||||
if "─" in line: # Has thin lines but no thick lines
|
if "─" in line: # Has thin lines but no thick lines
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Deduplicate completed progress bars only if they match the previous progress line
|
# Only show 100% completion lines for progress bars
|
||||||
if " ━━" in line:
|
if " ━━" in line:
|
||||||
progress_core = line.split(" ━━")[0].strip()
|
is_complete = "100%" in line
|
||||||
if progress_core == self.last_progress_line and self.last_was_progress:
|
|
||||||
|
# Skip ALL non-complete progress lines
|
||||||
|
if not is_complete:
|
||||||
continue
|
continue
|
||||||
self.last_progress_line = progress_core
|
|
||||||
|
# Extract sequence key to deduplicate multiple 100% lines for same sequence
|
||||||
|
parts = line.split()
|
||||||
|
seq_key = ""
|
||||||
|
if parts:
|
||||||
|
# Check for epoch pattern (X/Y at start)
|
||||||
|
if "/" in parts[0] and parts[0].replace("/", "").isdigit():
|
||||||
|
seq_key = parts[0] # e.g., "1/3"
|
||||||
|
elif parts[0] == "Class" and len(parts) > 1:
|
||||||
|
seq_key = f"{parts[0]}_{parts[1]}" # e.g., "Class_train:" or "Class_val:"
|
||||||
|
elif parts[0] in ("train:", "val:"):
|
||||||
|
seq_key = parts[0] # Phase identifier
|
||||||
|
|
||||||
|
# Skip if we already showed 100% for this sequence
|
||||||
|
if seq_key and self.last_progress_line == f"{seq_key}:done":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Mark this sequence as done
|
||||||
|
if seq_key:
|
||||||
|
self.last_progress_line = f"{seq_key}:done"
|
||||||
|
|
||||||
self.last_was_progress = True
|
self.last_was_progress = True
|
||||||
else:
|
else:
|
||||||
# Skip empty line after progress bar
|
# Skip empty line after progress bar
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue