ultralytics 8.0.239 Ultralytics Actions and hub-sdk adoption (#7431)
Signed-off-by: Glenn Jocher <glenn.jocher@ultralytics.com> Co-authored-by: UltralyticsAssistant <web@ultralytics.com> Co-authored-by: Burhan <62214284+Burhan-Q@users.noreply.github.com> Co-authored-by: Kayzwer <68285002+Kayzwer@users.noreply.github.com>
This commit is contained in:
parent
e795277391
commit
fe27db2f6e
139 changed files with 6870 additions and 5125 deletions
|
|
@ -73,40 +73,43 @@ class Tuner:
|
|||
Args:
|
||||
args (dict, optional): Configuration for hyperparameter evolution.
|
||||
"""
|
||||
self.space = args.pop('space', None) or { # key: (min, max, gain(optional))
|
||||
self.space = args.pop("space", None) or { # key: (min, max, gain(optional))
|
||||
# 'optimizer': tune.choice(['SGD', 'Adam', 'AdamW', 'NAdam', 'RAdam', 'RMSProp']),
|
||||
'lr0': (1e-5, 1e-1), # initial learning rate (i.e. SGD=1E-2, Adam=1E-3)
|
||||
'lrf': (0.0001, 0.1), # final OneCycleLR learning rate (lr0 * lrf)
|
||||
'momentum': (0.7, 0.98, 0.3), # SGD momentum/Adam beta1
|
||||
'weight_decay': (0.0, 0.001), # optimizer weight decay 5e-4
|
||||
'warmup_epochs': (0.0, 5.0), # warmup epochs (fractions ok)
|
||||
'warmup_momentum': (0.0, 0.95), # warmup initial momentum
|
||||
'box': (1.0, 20.0), # box loss gain
|
||||
'cls': (0.2, 4.0), # cls loss gain (scale with pixels)
|
||||
'dfl': (0.4, 6.0), # dfl loss gain
|
||||
'hsv_h': (0.0, 0.1), # image HSV-Hue augmentation (fraction)
|
||||
'hsv_s': (0.0, 0.9), # image HSV-Saturation augmentation (fraction)
|
||||
'hsv_v': (0.0, 0.9), # image HSV-Value augmentation (fraction)
|
||||
'degrees': (0.0, 45.0), # image rotation (+/- deg)
|
||||
'translate': (0.0, 0.9), # image translation (+/- fraction)
|
||||
'scale': (0.0, 0.95), # image scale (+/- gain)
|
||||
'shear': (0.0, 10.0), # image shear (+/- deg)
|
||||
'perspective': (0.0, 0.001), # image perspective (+/- fraction), range 0-0.001
|
||||
'flipud': (0.0, 1.0), # image flip up-down (probability)
|
||||
'fliplr': (0.0, 1.0), # image flip left-right (probability)
|
||||
'mosaic': (0.0, 1.0), # image mixup (probability)
|
||||
'mixup': (0.0, 1.0), # image mixup (probability)
|
||||
'copy_paste': (0.0, 1.0)} # segment copy-paste (probability)
|
||||
"lr0": (1e-5, 1e-1), # initial learning rate (i.e. SGD=1E-2, Adam=1E-3)
|
||||
"lrf": (0.0001, 0.1), # final OneCycleLR learning rate (lr0 * lrf)
|
||||
"momentum": (0.7, 0.98, 0.3), # SGD momentum/Adam beta1
|
||||
"weight_decay": (0.0, 0.001), # optimizer weight decay 5e-4
|
||||
"warmup_epochs": (0.0, 5.0), # warmup epochs (fractions ok)
|
||||
"warmup_momentum": (0.0, 0.95), # warmup initial momentum
|
||||
"box": (1.0, 20.0), # box loss gain
|
||||
"cls": (0.2, 4.0), # cls loss gain (scale with pixels)
|
||||
"dfl": (0.4, 6.0), # dfl loss gain
|
||||
"hsv_h": (0.0, 0.1), # image HSV-Hue augmentation (fraction)
|
||||
"hsv_s": (0.0, 0.9), # image HSV-Saturation augmentation (fraction)
|
||||
"hsv_v": (0.0, 0.9), # image HSV-Value augmentation (fraction)
|
||||
"degrees": (0.0, 45.0), # image rotation (+/- deg)
|
||||
"translate": (0.0, 0.9), # image translation (+/- fraction)
|
||||
"scale": (0.0, 0.95), # image scale (+/- gain)
|
||||
"shear": (0.0, 10.0), # image shear (+/- deg)
|
||||
"perspective": (0.0, 0.001), # image perspective (+/- fraction), range 0-0.001
|
||||
"flipud": (0.0, 1.0), # image flip up-down (probability)
|
||||
"fliplr": (0.0, 1.0), # image flip left-right (probability)
|
||||
"mosaic": (0.0, 1.0), # image mixup (probability)
|
||||
"mixup": (0.0, 1.0), # image mixup (probability)
|
||||
"copy_paste": (0.0, 1.0), # segment copy-paste (probability)
|
||||
}
|
||||
self.args = get_cfg(overrides=args)
|
||||
self.tune_dir = get_save_dir(self.args, name='tune')
|
||||
self.tune_csv = self.tune_dir / 'tune_results.csv'
|
||||
self.tune_dir = get_save_dir(self.args, name="tune")
|
||||
self.tune_csv = self.tune_dir / "tune_results.csv"
|
||||
self.callbacks = _callbacks or callbacks.get_default_callbacks()
|
||||
self.prefix = colorstr('Tuner: ')
|
||||
self.prefix = colorstr("Tuner: ")
|
||||
callbacks.add_integration_callbacks(self)
|
||||
LOGGER.info(f"{self.prefix}Initialized Tuner instance with 'tune_dir={self.tune_dir}'\n"
|
||||
f'{self.prefix}💡 Learn about tuning at https://docs.ultralytics.com/guides/hyperparameter-tuning')
|
||||
LOGGER.info(
|
||||
f"{self.prefix}Initialized Tuner instance with 'tune_dir={self.tune_dir}'\n"
|
||||
f"{self.prefix}💡 Learn about tuning at https://docs.ultralytics.com/guides/hyperparameter-tuning"
|
||||
)
|
||||
|
||||
def _mutate(self, parent='single', n=5, mutation=0.8, sigma=0.2):
|
||||
def _mutate(self, parent="single", n=5, mutation=0.8, sigma=0.2):
|
||||
"""
|
||||
Mutates the hyperparameters based on bounds and scaling factors specified in `self.space`.
|
||||
|
||||
|
|
@ -121,15 +124,15 @@ class Tuner:
|
|||
"""
|
||||
if self.tune_csv.exists(): # if CSV file exists: select best hyps and mutate
|
||||
# Select parent(s)
|
||||
x = np.loadtxt(self.tune_csv, ndmin=2, delimiter=',', skiprows=1)
|
||||
x = np.loadtxt(self.tune_csv, ndmin=2, delimiter=",", skiprows=1)
|
||||
fitness = x[:, 0] # first column
|
||||
n = min(n, len(x)) # number of previous results to consider
|
||||
x = x[np.argsort(-fitness)][:n] # top n mutations
|
||||
w = x[:, 0] - x[:, 0].min() + 1E-6 # weights (sum > 0)
|
||||
if parent == 'single' or len(x) == 1:
|
||||
w = x[:, 0] - x[:, 0].min() + 1e-6 # weights (sum > 0)
|
||||
if parent == "single" or len(x) == 1:
|
||||
# x = x[random.randint(0, n - 1)] # random selection
|
||||
x = x[random.choices(range(n), weights=w)[0]] # weighted selection
|
||||
elif parent == 'weighted':
|
||||
elif parent == "weighted":
|
||||
x = (x * w.reshape(n, 1)).sum(0) / w.sum() # weighted combination
|
||||
|
||||
# Mutate
|
||||
|
|
@ -174,44 +177,44 @@ class Tuner:
|
|||
|
||||
t0 = time.time()
|
||||
best_save_dir, best_metrics = None, None
|
||||
(self.tune_dir / 'weights').mkdir(parents=True, exist_ok=True)
|
||||
(self.tune_dir / "weights").mkdir(parents=True, exist_ok=True)
|
||||
for i in range(iterations):
|
||||
# Mutate hyperparameters
|
||||
mutated_hyp = self._mutate()
|
||||
LOGGER.info(f'{self.prefix}Starting iteration {i + 1}/{iterations} with hyperparameters: {mutated_hyp}')
|
||||
LOGGER.info(f"{self.prefix}Starting iteration {i + 1}/{iterations} with hyperparameters: {mutated_hyp}")
|
||||
|
||||
metrics = {}
|
||||
train_args = {**vars(self.args), **mutated_hyp}
|
||||
save_dir = get_save_dir(get_cfg(train_args))
|
||||
weights_dir = save_dir / 'weights'
|
||||
ckpt_file = weights_dir / ('best.pt' if (weights_dir / 'best.pt').exists() else 'last.pt')
|
||||
weights_dir = save_dir / "weights"
|
||||
ckpt_file = weights_dir / ("best.pt" if (weights_dir / "best.pt").exists() else "last.pt")
|
||||
try:
|
||||
# Train YOLO model with mutated hyperparameters (run in subprocess to avoid dataloader hang)
|
||||
cmd = ['yolo', 'train', *(f'{k}={v}' for k, v in train_args.items())]
|
||||
cmd = ["yolo", "train", *(f"{k}={v}" for k, v in train_args.items())]
|
||||
return_code = subprocess.run(cmd, check=True).returncode
|
||||
metrics = torch.load(ckpt_file)['train_metrics']
|
||||
assert return_code == 0, 'training failed'
|
||||
metrics = torch.load(ckpt_file)["train_metrics"]
|
||||
assert return_code == 0, "training failed"
|
||||
|
||||
except Exception as e:
|
||||
LOGGER.warning(f'WARNING ❌️ training failure for hyperparameter tuning iteration {i + 1}\n{e}')
|
||||
LOGGER.warning(f"WARNING ❌️ training failure for hyperparameter tuning iteration {i + 1}\n{e}")
|
||||
|
||||
# Save results and mutated_hyp to CSV
|
||||
fitness = metrics.get('fitness', 0.0)
|
||||
fitness = metrics.get("fitness", 0.0)
|
||||
log_row = [round(fitness, 5)] + [mutated_hyp[k] for k in self.space.keys()]
|
||||
headers = '' if self.tune_csv.exists() else (','.join(['fitness'] + list(self.space.keys())) + '\n')
|
||||
with open(self.tune_csv, 'a') as f:
|
||||
f.write(headers + ','.join(map(str, log_row)) + '\n')
|
||||
headers = "" if self.tune_csv.exists() else (",".join(["fitness"] + list(self.space.keys())) + "\n")
|
||||
with open(self.tune_csv, "a") as f:
|
||||
f.write(headers + ",".join(map(str, log_row)) + "\n")
|
||||
|
||||
# Get best results
|
||||
x = np.loadtxt(self.tune_csv, ndmin=2, delimiter=',', skiprows=1)
|
||||
x = np.loadtxt(self.tune_csv, ndmin=2, delimiter=",", skiprows=1)
|
||||
fitness = x[:, 0] # first column
|
||||
best_idx = fitness.argmax()
|
||||
best_is_current = best_idx == i
|
||||
if best_is_current:
|
||||
best_save_dir = save_dir
|
||||
best_metrics = {k: round(v, 5) for k, v in metrics.items()}
|
||||
for ckpt in weights_dir.glob('*.pt'):
|
||||
shutil.copy2(ckpt, self.tune_dir / 'weights')
|
||||
for ckpt in weights_dir.glob("*.pt"):
|
||||
shutil.copy2(ckpt, self.tune_dir / "weights")
|
||||
elif cleanup:
|
||||
shutil.rmtree(ckpt_file.parent) # remove iteration weights/ dir to reduce storage space
|
||||
|
||||
|
|
@ -219,15 +222,19 @@ class Tuner:
|
|||
plot_tune_results(self.tune_csv)
|
||||
|
||||
# Save and print tune results
|
||||
header = (f'{self.prefix}{i + 1}/{iterations} iterations complete ✅ ({time.time() - t0:.2f}s)\n'
|
||||
f'{self.prefix}Results saved to {colorstr("bold", self.tune_dir)}\n'
|
||||
f'{self.prefix}Best fitness={fitness[best_idx]} observed at iteration {best_idx + 1}\n'
|
||||
f'{self.prefix}Best fitness metrics are {best_metrics}\n'
|
||||
f'{self.prefix}Best fitness model is {best_save_dir}\n'
|
||||
f'{self.prefix}Best fitness hyperparameters are printed below.\n')
|
||||
LOGGER.info('\n' + header)
|
||||
header = (
|
||||
f'{self.prefix}{i + 1}/{iterations} iterations complete ✅ ({time.time() - t0:.2f}s)\n'
|
||||
f'{self.prefix}Results saved to {colorstr("bold", self.tune_dir)}\n'
|
||||
f'{self.prefix}Best fitness={fitness[best_idx]} observed at iteration {best_idx + 1}\n'
|
||||
f'{self.prefix}Best fitness metrics are {best_metrics}\n'
|
||||
f'{self.prefix}Best fitness model is {best_save_dir}\n'
|
||||
f'{self.prefix}Best fitness hyperparameters are printed below.\n'
|
||||
)
|
||||
LOGGER.info("\n" + header)
|
||||
data = {k: float(x[best_idx, i + 1]) for i, k in enumerate(self.space.keys())}
|
||||
yaml_save(self.tune_dir / 'best_hyperparameters.yaml',
|
||||
data=data,
|
||||
header=remove_colorstr(header.replace(self.prefix, '# ')) + '\n')
|
||||
yaml_print(self.tune_dir / 'best_hyperparameters.yaml')
|
||||
yaml_save(
|
||||
self.tune_dir / "best_hyperparameters.yaml",
|
||||
data=data,
|
||||
header=remove_colorstr(header.replace(self.prefix, "# ")) + "\n",
|
||||
)
|
||||
yaml_print(self.tune_dir / "best_hyperparameters.yaml")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue