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:
		
							
								
								
									
										13
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							@ -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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
 | 
				
			|||||||
@ -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}'
 | 
				
			||||||
 | 
				
			|||||||
@ -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.
 | 
				
			||||||
 | 
				
			|||||||
@ -27,14 +27,16 @@ 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
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
    for f in files:
 | 
					    task = Task.current_task()
 | 
				
			||||||
        if f.exists():
 | 
					    if task:
 | 
				
			||||||
            it = re.search(r'_batch(\d+)', f.name)
 | 
					        for f in files:
 | 
				
			||||||
            iteration = int(it.groups()[0]) if it else 0
 | 
					            if f.exists():
 | 
				
			||||||
            Task.current_task().get_logger().report_image(title=title,
 | 
					                it = re.search(r'_batch(\d+)', f.name)
 | 
				
			||||||
                                                          series=f.name.replace(it.group(), ''),
 | 
					                iteration = int(it.groups()[0]) if it else 0
 | 
				
			||||||
                                                          local_path=str(f),
 | 
					                task.get_logger().report_image(title=title,
 | 
				
			||||||
                                                          iteration=iteration)
 | 
					                                               series=f.name.replace(it.group(), ''),
 | 
				
			||||||
 | 
					                                               local_path=str(f),
 | 
				
			||||||
 | 
					                                               iteration=iteration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _log_plot(title, plot_path):
 | 
					def _log_plot(title, plot_path):
 | 
				
			||||||
@ -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,43 +80,46 @@ 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):
 | 
				
			||||||
    # You should have access to the validation bboxes under jdict
 | 
					    task = Task.current_task()
 | 
				
			||||||
    Task.current_task().get_logger().report_scalar(title='Epoch Time',
 | 
					    if task:
 | 
				
			||||||
                                                   series='Epoch Time',
 | 
					        # You should have access to the validation bboxes under jdict
 | 
				
			||||||
                                                   value=trainer.epoch_time,
 | 
					        task.get_logger().report_scalar(title='Epoch Time',
 | 
				
			||||||
                                                   iteration=trainer.epoch)
 | 
					                                        series='Epoch Time',
 | 
				
			||||||
    if trainer.epoch == 0:
 | 
					                                        value=trainer.epoch_time,
 | 
				
			||||||
        model_info = {
 | 
					                                        iteration=trainer.epoch)
 | 
				
			||||||
            'model/parameters': get_num_params(trainer.model),
 | 
					        if trainer.epoch == 0:
 | 
				
			||||||
            'model/GFLOPs': round(get_flops(trainer.model), 3),
 | 
					            model_info = {
 | 
				
			||||||
            'model/speed(ms)': round(trainer.validator.speed['inference'], 3)}
 | 
					                'model/parameters': get_num_params(trainer.model),
 | 
				
			||||||
        for k, v in model_info.items():
 | 
					                'model/GFLOPs': round(get_flops(trainer.model), 3),
 | 
				
			||||||
            Task.current_task().get_logger().report_single_value(k, v)
 | 
					                'model/speed(ms)': round(trainer.validator.speed['inference'], 3)}
 | 
				
			||||||
 | 
					            for k, v in model_info.items():
 | 
				
			||||||
 | 
					                task.get_logger().report_single_value(k, v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def on_val_end(validator):
 | 
					def on_val_end(validator):
 | 
				
			||||||
    # Log val_labels and val_pred
 | 
					    if Task.current_task():
 | 
				
			||||||
    _log_debug_samples(sorted(validator.save_dir.glob('val*.jpg')), 'Validation')
 | 
					        # Log val_labels and val_pred
 | 
				
			||||||
 | 
					        _log_debug_samples(sorted(validator.save_dir.glob('val*.jpg')), 'Validation')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def on_train_end(trainer):
 | 
					def on_train_end(trainer):
 | 
				
			||||||
    # Log final results, CM matrix + PR plots
 | 
					    task = Task.current_task()
 | 
				
			||||||
    files = ['results.png', 'confusion_matrix.png', *(f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R'))]
 | 
					    if task:
 | 
				
			||||||
    files = [(trainer.save_dir / f) for f in files if (trainer.save_dir / f).exists()]  # filter
 | 
					        # Log final results, CM matrix + PR plots
 | 
				
			||||||
    for f in files:
 | 
					        files = ['results.png', 'confusion_matrix.png', *(f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R'))]
 | 
				
			||||||
        _log_plot(title=f.stem, plot_path=f)
 | 
					        files = [(trainer.save_dir / f) for f in files if (trainer.save_dir / f).exists()]  # filter
 | 
				
			||||||
    # Report final metrics
 | 
					        for f in files:
 | 
				
			||||||
    for k, v in trainer.validator.metrics.results_dict.items():
 | 
					            _log_plot(title=f.stem, plot_path=f)
 | 
				
			||||||
        Task.current_task().get_logger().report_single_value(k, v)
 | 
					        # Report final metrics
 | 
				
			||||||
    # Log the final model
 | 
					        for k, v in trainer.validator.metrics.results_dict.items():
 | 
				
			||||||
    Task.current_task().update_output_model(model_path=str(trainer.best),
 | 
					            task.get_logger().report_single_value(k, v)
 | 
				
			||||||
                                            model_name=trainer.args.name,
 | 
					        # Log the final model
 | 
				
			||||||
                                            auto_delete_file=False)
 | 
					        task.update_output_model(model_path=str(trainer.best), model_name=trainer.args.name, auto_delete_file=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
callbacks = {
 | 
					callbacks = {
 | 
				
			||||||
 | 
				
			|||||||
@ -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()))
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user