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
|
||||
*.ico
|
||||
*.raw
|
||||
|
||||
# Training logs
|
||||
args.yaml
|
||||
results.csv
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ keywords: platform callbacks, training callbacks, console logging, YOLO11 traini
|
|||
|
||||
<br><br><hr><br>
|
||||
|
||||
## ::: ultralytics.utils.callbacks.platform._get_environment_info
|
||||
|
||||
<br><br><hr><br>
|
||||
|
||||
## ::: ultralytics.utils.callbacks.platform.on_pretrain_routine_start
|
||||
|
||||
<br><br><hr><br>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
__version__ = "8.3.242"
|
||||
__version__ = "8.3.243"
|
||||
|
||||
import importlib
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
import os
|
||||
import platform
|
||||
import socket
|
||||
import sys
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
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
|
||||
_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)
|
||||
|
||||
|
||||
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):
|
||||
"""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:
|
||||
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")
|
||||
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.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(
|
||||
"training_started",
|
||||
{
|
||||
"trainArgs": {k: str(v) for k, v in vars(trainer.args).items()},
|
||||
"epochs": trainer.epochs,
|
||||
"device": str(trainer.device),
|
||||
"modelInfo": model_info,
|
||||
"environment": environment,
|
||||
},
|
||||
project,
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class ConsoleLogger:
|
|||
# Deduplication state
|
||||
self.last_line = ""
|
||||
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
|
||||
|
||||
def start_capture(self):
|
||||
|
|
@ -146,12 +146,34 @@ class ConsoleLogger:
|
|||
if "─" in line: # Has thin lines but no thick lines
|
||||
continue
|
||||
|
||||
# Deduplicate completed progress bars only if they match the previous progress line
|
||||
# Only show 100% completion lines for progress bars
|
||||
if " ━━" in line:
|
||||
progress_core = line.split(" ━━")[0].strip()
|
||||
if progress_core == self.last_progress_line and self.last_was_progress:
|
||||
is_complete = "100%" in line
|
||||
|
||||
# Skip ALL non-complete progress lines
|
||||
if not is_complete:
|
||||
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
|
||||
else:
|
||||
# Skip empty line after progress bar
|
||||
|
|
|
|||
Loading…
Reference in a new issue