ultralytics 8.0.69 HUB CI and ClearML fixes (#1888)

Co-authored-by: Victor Sonck <victor.sonck@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Glenn Jocher 2023-04-07 15:16:20 +02:00 committed by GitHub
parent d3f097314f
commit c2cd3fd20e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 88 deletions

View File

@ -46,17 +46,14 @@ jobs:
- name: Test HUB training - name: Test HUB training
shell: python shell: python
env: env:
APIKEY: ${{ secrets.ULTRALYTICS_HUB_APIKEY }} API_KEY: ${{ secrets.ULTRALYTICS_HUB_API_KEY }}
MODEL_ID: ${{ secrets.ULTRALYTICS_HUB_MODEL_ID }}
run: | run: |
import os import os
from pathlib import Path
from ultralytics import YOLO, hub from ultralytics import YOLO, hub
from ultralytics.yolo.utils import USER_CONFIG_DIR api_key, model_id = os.environ['API_KEY'], os.environ['MODEL_ID']
Path(USER_CONFIG_DIR / 'settings.yaml').unlink() hub.login(api_key)
key = os.environ['APIKEY'] hub.reset_model(model_id)
hub.reset_model(key)
key, model_id = key.split('_')
hub.login(key)
model = YOLO('https://hub.ultralytics.com/models/' + model_id) model = YOLO('https://hub.ultralytics.com/models/' + model_id)
model.train() model.train()

View File

@ -1,6 +1,6 @@
# Ultralytics YOLO 🚀, GPL-3.0 license # Ultralytics YOLO 🚀, GPL-3.0 license
__version__ = '8.0.68' __version__ = '8.0.69'
from ultralytics.hub import start from ultralytics.hub import start
from ultralytics.yolo.engine.model import YOLO from ultralytics.yolo.engine.model import YOLO

View File

@ -2,7 +2,8 @@
import requests import requests
from ultralytics.hub.utils import PREFIX, split_key from ultralytics.hub.auth import Auth
from ultralytics.hub.utils import PREFIX
from ultralytics.yolo.utils import LOGGER, SETTINGS, USER_CONFIG_DIR, yaml_save from ultralytics.yolo.utils import LOGGER, SETTINGS, USER_CONFIG_DIR, yaml_save
@ -17,7 +18,6 @@ def login(api_key=''):
from ultralytics import hub from ultralytics import hub
hub.login('API_KEY') hub.login('API_KEY')
""" """
from ultralytics.hub.auth import Auth
Auth(api_key) Auth(api_key)
@ -42,20 +42,20 @@ def start(key=''):
key (str, optional): A string containing either the API key and model ID combination (apikey_modelid), key (str, optional): A string containing either the API key and model ID combination (apikey_modelid),
or the full model URL (https://hub.ultralytics.com/models/apikey_modelid). or the full model URL (https://hub.ultralytics.com/models/apikey_modelid).
""" """
api_key, model_id = key.split('_')
LOGGER.warning(f""" LOGGER.warning(f"""
WARNING ultralytics.start() is deprecated in 8.0.60. Updated usage to train your Ultralytics HUB model is below: WARNING ultralytics.start() is deprecated after 8.0.60. Updated usage to train Ultralytics HUB models is:
from ultralytics import YOLO from ultralytics import YOLO, hub
model = YOLO('https://hub.ultralytics.com/models/{key}') hub.login('{api_key}')
model = YOLO('https://hub.ultralytics.com/models/{model_id}')
model.train()""") model.train()""")
def reset_model(key=''): def reset_model(model_id=''):
# Reset a trained model to an untrained state # Reset a trained model to an untrained state
api_key, model_id = split_key(key) r = requests.post('https://api.ultralytics.com/model-reset', json={'apiKey': Auth().api_key, 'modelId': model_id})
r = requests.post('https://api.ultralytics.com/model-reset', json={'apiKey': api_key, 'modelId': model_id})
if r.status_code == 200: if r.status_code == 200:
LOGGER.info(f'{PREFIX}Model reset successfully') LOGGER.info(f'{PREFIX}Model reset successfully')
return return
@ -68,26 +68,24 @@ def export_fmts_hub():
return list(export_formats()['Argument'][1:]) + ['ultralytics_tflite', 'ultralytics_coreml'] return list(export_formats()['Argument'][1:]) + ['ultralytics_tflite', 'ultralytics_coreml']
def export_model(key='', format='torchscript'): def export_model(model_id='', format='torchscript'):
# Export a model to all formats # Export a model to all formats
assert format in export_fmts_hub(), f"Unsupported export format '{format}', valid formats are {export_fmts_hub()}" assert format in export_fmts_hub(), f"Unsupported export format '{format}', valid formats are {export_fmts_hub()}"
api_key, model_id = split_key(key)
r = requests.post('https://api.ultralytics.com/export', r = requests.post('https://api.ultralytics.com/export',
json={ json={
'apiKey': api_key, 'apiKey': Auth().api_key,
'modelId': model_id, 'modelId': model_id,
'format': format}) 'format': format})
assert r.status_code == 200, f'{PREFIX}{format} export failure {r.status_code} {r.reason}' assert r.status_code == 200, f'{PREFIX}{format} export failure {r.status_code} {r.reason}'
LOGGER.info(f'{PREFIX}{format} export started ✅') LOGGER.info(f'{PREFIX}{format} export started ✅')
def get_export(key='', format='torchscript'): def get_export(model_id='', format='torchscript'):
# Get an exported model dictionary with download URL # Get an exported model dictionary with download URL
assert format in export_fmts_hub, f"Unsupported export format '{format}', valid formats are {export_fmts_hub}" assert format in export_fmts_hub, f"Unsupported export format '{format}', valid formats are {export_fmts_hub}"
api_key, model_id = split_key(key)
r = requests.post('https://api.ultralytics.com/get-export', r = requests.post('https://api.ultralytics.com/get-export',
json={ json={
'apiKey': api_key, 'apiKey': Auth().api_key,
'modelId': model_id, 'modelId': model_id,
'format': format}) 'format': format})
assert r.status_code == 200, f'{PREFIX}{format} get_export failure {r.status_code} {r.reason}' assert r.status_code == 200, f'{PREFIX}{format} get_export failure {r.status_code} {r.reason}'

View File

@ -13,7 +13,7 @@ import requests
from tqdm import tqdm from tqdm import tqdm
from ultralytics.yolo.utils import (ENVIRONMENT, LOGGER, ONLINE, RANK, SETTINGS, TESTS_RUNNING, TQDM_BAR_FORMAT, from ultralytics.yolo.utils import (ENVIRONMENT, LOGGER, ONLINE, RANK, SETTINGS, TESTS_RUNNING, TQDM_BAR_FORMAT,
TryExcept, __version__, colorstr, emojis, get_git_origin_url, is_colab, is_git_dir, TryExcept, __version__, colorstr, get_git_origin_url, is_colab, is_git_dir,
is_pip_package) is_pip_package)
PREFIX = colorstr('Ultralytics HUB: ') PREFIX = colorstr('Ultralytics HUB: ')
@ -80,29 +80,6 @@ def request_with_credentials(url: str) -> any:
return output.eval_js('_hub_tmp') return output.eval_js('_hub_tmp')
def split_key(key=''):
"""
Verify and split a 'api_key[sep]model_id' string, sep is one of '.' or '_'
Args:
key (str): The model key to split. If not provided, the user will be prompted to enter it.
Returns:
Tuple[str, str]: A tuple containing the API key and model ID.
"""
import getpass
error_string = emojis(f'{PREFIX}Invalid API key ⚠️\n') # error string
if not key:
key = getpass.getpass('Enter model key: ')
sep = '_' if '_' in key else None # separator
assert sep, error_string
api_key, model_id = key.split(sep)
assert len(api_key) and len(model_id), error_string
return api_key, model_id
def requests_with_progress(method, url, **kwargs): def requests_with_progress(method, url, **kwargs):
""" """
Make an HTTP request using the specified method and URL, with an optional progress bar. Make an HTTP request using the specified method and URL, with an optional progress bar.

View File

@ -27,11 +27,13 @@ def _log_debug_samples(files, title='Debug Samples'):
files (List(PosixPath)) a list of file paths in PosixPath format files (List(PosixPath)) a list of file paths in PosixPath format
title (str) A title that groups together images with the same values title (str) A title that groups together images with the same values
""" """
task = Task.current_task()
if task:
for f in files: for f in files:
if f.exists(): if f.exists():
it = re.search(r'_batch(\d+)', f.name) it = re.search(r'_batch(\d+)', f.name)
iteration = int(it.groups()[0]) if it else 0 iteration = int(it.groups()[0]) if it else 0
Task.current_task().get_logger().report_image(title=title, task.get_logger().report_image(title=title,
series=f.name.replace(it.group(), ''), series=f.name.replace(it.group(), ''),
local_path=str(f), local_path=str(f),
iteration=iteration) iteration=iteration)
@ -54,11 +56,9 @@ def _log_plot(title, plot_path):
def on_pretrain_routine_start(trainer): def on_pretrain_routine_start(trainer):
# TODO: reuse existing task
try: try:
if Task.current_task():
task = Task.current_task() task = Task.current_task()
if task:
# Make sure the automatic pytorch and matplotlib bindings are disabled! # Make sure the automatic pytorch and matplotlib bindings are disabled!
# We are logging these plots and model files manually in the integration # We are logging these plots and model files manually in the integration
PatchPyTorchModelIO.update_current_task(None) PatchPyTorchModelIO.update_current_task(None)
@ -80,13 +80,15 @@ def on_pretrain_routine_start(trainer):
def on_train_epoch_end(trainer): def on_train_epoch_end(trainer):
if trainer.epoch == 1: if trainer.epoch == 1 and Task.current_task():
_log_debug_samples(sorted(trainer.save_dir.glob('train_batch*.jpg')), 'Mosaic') _log_debug_samples(sorted(trainer.save_dir.glob('train_batch*.jpg')), 'Mosaic')
def on_fit_epoch_end(trainer): def on_fit_epoch_end(trainer):
task = Task.current_task()
if task:
# You should have access to the validation bboxes under jdict # You should have access to the validation bboxes under jdict
Task.current_task().get_logger().report_scalar(title='Epoch Time', task.get_logger().report_scalar(title='Epoch Time',
series='Epoch Time', series='Epoch Time',
value=trainer.epoch_time, value=trainer.epoch_time,
iteration=trainer.epoch) iteration=trainer.epoch)
@ -96,15 +98,18 @@ def on_fit_epoch_end(trainer):
'model/GFLOPs': round(get_flops(trainer.model), 3), 'model/GFLOPs': round(get_flops(trainer.model), 3),
'model/speed(ms)': round(trainer.validator.speed['inference'], 3)} 'model/speed(ms)': round(trainer.validator.speed['inference'], 3)}
for k, v in model_info.items(): for k, v in model_info.items():
Task.current_task().get_logger().report_single_value(k, v) task.get_logger().report_single_value(k, v)
def on_val_end(validator): def on_val_end(validator):
if Task.current_task():
# Log val_labels and val_pred # Log val_labels and val_pred
_log_debug_samples(sorted(validator.save_dir.glob('val*.jpg')), 'Validation') _log_debug_samples(sorted(validator.save_dir.glob('val*.jpg')), 'Validation')
def on_train_end(trainer): def on_train_end(trainer):
task = Task.current_task()
if task:
# Log final results, CM matrix + PR plots # Log final results, CM matrix + PR plots
files = ['results.png', 'confusion_matrix.png', *(f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R'))] files = ['results.png', 'confusion_matrix.png', *(f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R'))]
files = [(trainer.save_dir / f) for f in files if (trainer.save_dir / f).exists()] # filter files = [(trainer.save_dir / f) for f in files if (trainer.save_dir / f).exists()] # filter
@ -112,11 +117,9 @@ def on_train_end(trainer):
_log_plot(title=f.stem, plot_path=f) _log_plot(title=f.stem, plot_path=f)
# Report final metrics # Report final metrics
for k, v in trainer.validator.metrics.results_dict.items(): for k, v in trainer.validator.metrics.results_dict.items():
Task.current_task().get_logger().report_single_value(k, v) task.get_logger().report_single_value(k, v)
# Log the final model # Log the final model
Task.current_task().update_output_model(model_path=str(trainer.best), task.update_output_model(model_path=str(trainer.best), model_name=trainer.args.name, auto_delete_file=False)
model_name=trainer.args.name,
auto_delete_file=False)
callbacks = { callbacks = {

View File

@ -337,6 +337,10 @@ def git_describe(path=ROOT): # path must be a directory
def print_args(args: Optional[dict] = None, show_file=True, show_func=False): def print_args(args: Optional[dict] = None, show_file=True, show_func=False):
# Print function arguments (optional args dict) # Print function arguments (optional args dict)
def strip_auth(v):
# Clean longer Ultralytics HUB URLs by stripping potential authentication information
return clean_url(v) if (isinstance(v, str) and v.startswith('http') and len(v) > 100) else v
x = inspect.currentframe().f_back # previous frame x = inspect.currentframe().f_back # previous frame
file, _, func, _, _ = inspect.getframeinfo(x) file, _, func, _, _ = inspect.getframeinfo(x)
if args is None: # get args automatically if args is None: # get args automatically
@ -347,4 +351,4 @@ def print_args(args: Optional[dict] = None, show_file=True, show_func=False):
except ValueError: except ValueError:
file = Path(file).stem file = Path(file).stem
s = (f'{file}: ' if show_file else '') + (f'{func}: ' if show_func else '') s = (f'{file}: ' if show_file else '') + (f'{func}: ' if show_func else '')
LOGGER.info(colorstr(s) + ', '.join(f'{k}={v}' for k, v in args.items())) LOGGER.info(colorstr(s) + ', '.join(f'{k}={strip_auth(v)}' for k, v in args.items()))