|
import optuna |
|
import os |
|
from datetime import datetime |
|
import pandas as pd |
|
from pathlib import Path |
|
import json |
|
import math |
|
|
|
import sys |
|
sys.path.append(os.path.dirname(os.path.dirname(__file__))) |
|
from scripts.train import train_and_evaluate |
|
from src.utils.utils import create_run_directory |
|
|
|
def create_hyperparam_directory(): |
|
"""Create a parent directory for all hyperparameter searches""" |
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
base_dir = "runs_hyperparam" |
|
hyperparam_dir = os.path.join(base_dir, f"hyperparam_{timestamp}") |
|
os.makedirs(hyperparam_dir, exist_ok=True) |
|
return hyperparam_dir |
|
|
|
def objective(trial, hyperparam_run_dir, data_path): |
|
"""Objective function for a single dataset""" |
|
|
|
|
|
config = { |
|
"clip_model": trial.suggest_categorical("clip_model", ["openai/clip-vit-base-patch32", "openai/clip-vit-large-patch14"]), |
|
"batch_size": trial.suggest_categorical("batch_size", [8,16,32]), |
|
"unfreeze_layers": trial.suggest_int("unfreeze_layers", 1, 4), |
|
"learning_rate": trial.suggest_float("learning_rate", 1e-6, 1e-4, log=True), |
|
"weight_decay": trial.suggest_float("weight_decay", 1e-8, 1e-1, log=True), |
|
"gradient_clip_max_norm": trial.suggest_float("gradient_clip_max_norm", 0.1, 1.0), |
|
"augmentation_strength": trial.suggest_float("augmentation_strength", 0.0, 1.0), |
|
"crop_scale_min": trial.suggest_float("crop_scale_min", 0.6, 0.9), |
|
"max_frames": trial.suggest_int("max_frames", 5, 15), |
|
"sigma": trial.suggest_float("sigma", 0.1, 0.5), |
|
} |
|
|
|
class_labels = ["windmill", "halo", "swipe", "baby_mill"][:3] |
|
|
|
|
|
config.update({ |
|
"class_labels": class_labels, |
|
"num_classes": len(class_labels), |
|
"data_path": data_path, |
|
"num_epochs": 50, |
|
"patience": 10, |
|
"image_size": 224, |
|
"crop_scale_max": 1.0, |
|
"normalization_mean": [0.485, 0.456, 0.406], |
|
"normalization_std": [0.229, 0.224, 0.225], |
|
"overfitting_threshold": 10, |
|
}) |
|
|
|
|
|
config.update({ |
|
"flip_probability": 0.5 * config["augmentation_strength"], |
|
"rotation_degrees": int(15 * config["augmentation_strength"]), |
|
"brightness_jitter": 0.2 * config["augmentation_strength"], |
|
"contrast_jitter": 0.2 * config["augmentation_strength"], |
|
"saturation_jitter": 0.2 * config["augmentation_strength"], |
|
"hue_jitter": 0.1 * config["augmentation_strength"], |
|
}) |
|
|
|
|
|
dataset_label = '_'.join(Path(data_path).parts[-2:]) |
|
trial_dir = create_run_directory( |
|
prefix=f"trial_{dataset_label}", |
|
parent_dir=hyperparam_run_dir |
|
) |
|
config["run_dir"] = trial_dir |
|
|
|
|
|
|
|
try: |
|
val_accuracy, vis_dir = train_and_evaluate(config) |
|
|
|
if val_accuracy is None or math.isnan(val_accuracy) or math.isinf(val_accuracy): |
|
raise ValueError(f"Invalid accuracy value: {val_accuracy}") |
|
|
|
|
|
trial_info = { |
|
'dataset': data_path, |
|
'dataset_label': dataset_label, |
|
'trial_number': trial.number, |
|
'parameters': trial.params, |
|
'accuracy': val_accuracy, |
|
'visualization_dir': vis_dir, |
|
'trial_dir': trial_dir |
|
} |
|
|
|
with open(os.path.join(trial_dir, 'trial_info.json'), 'w') as f: |
|
json.dump(trial_info, f, indent=4) |
|
|
|
return val_accuracy |
|
|
|
except Exception as e: |
|
print(f"Error in trial for {data_path}: {str(e)}") |
|
|
|
error_log_path = os.path.join(hyperparam_run_dir, 'error_log.txt') |
|
with open(error_log_path, 'a') as f: |
|
f.write(f"\nError in trial at {datetime.now()}:\n") |
|
f.write(f"Dataset: {data_path}\n") |
|
f.write(f"Error: {str(e)}\n") |
|
f.write(f"Trial params: {trial.params}\n") |
|
f.write("Stack trace:\n") |
|
import traceback |
|
f.write(traceback.format_exc()) |
|
f.write("\n" + "="*50 + "\n") |
|
|
|
return float('-inf') |
|
|
|
def run_hyperparameter_search(data_paths, n_trials=100): |
|
"""Run hyperparameter search for multiple datasets""" |
|
|
|
|
|
parent_hyperparam_dir = create_hyperparam_directory() |
|
|
|
|
|
all_results = {} |
|
|
|
for data_path in data_paths: |
|
print(f"\nStarting hyperparameter search for dataset: {data_path}") |
|
|
|
|
|
dataset_label = '_'.join(Path(data_path).parts[-2:]) |
|
dataset_dir = os.path.join(parent_hyperparam_dir, f"search_{dataset_label}") |
|
os.makedirs(dataset_dir, exist_ok=True) |
|
|
|
|
|
study = optuna.create_study(direction="maximize") |
|
completed_trials = 0 |
|
failed_trials = [] |
|
total_attempts = 0 |
|
max_attempts = n_trials * 2 |
|
while completed_trials < n_trials and total_attempts < max_attempts: |
|
try: |
|
total_attempts += 1 |
|
study.optimize( |
|
lambda trial: objective(trial, dataset_dir, data_path), |
|
n_trials=1 |
|
) |
|
|
|
if study.trials[-1].value != float('-inf'): |
|
completed_trials += 1 |
|
print(f"Completed trial {completed_trials}/{n_trials} for {dataset_label}") |
|
else: |
|
error_info = { |
|
'trial_number': completed_trials + len(failed_trials) + 1, |
|
'error': "Trial returned -inf", |
|
'timestamp': datetime.now().isoformat() |
|
} |
|
failed_trials.append(error_info) |
|
print(f"Failed trial for {dataset_label}: returned -inf") |
|
|
|
except Exception as e: |
|
error_info = { |
|
'trial_number': completed_trials + len(failed_trials) + 1, |
|
'error': str(e), |
|
'timestamp': datetime.now().isoformat() |
|
} |
|
failed_trials.append(error_info) |
|
print(f"Error in trial for {dataset_label}: {str(e)}") |
|
|
|
|
|
with open(os.path.join(dataset_dir, 'failed_trials.json'), 'w') as f: |
|
json.dump(failed_trials, f, indent=4) |
|
if total_attempts >= max_attempts: |
|
print(f"Warning: Reached maximum attempts ({max_attempts}) for {dataset_label}") |
|
|
|
|
|
results_df = study.trials_dataframe() |
|
results_df.to_csv(os.path.join(dataset_dir, 'study_results.csv')) |
|
|
|
|
|
trial_stats = { |
|
'completed_trials': completed_trials, |
|
'failed_trials': len(failed_trials), |
|
'total_attempts': completed_trials + len(failed_trials) |
|
} |
|
with open(os.path.join(dataset_dir, 'trial_statistics.json'), 'w') as f: |
|
json.dump(trial_stats, f, indent=4) |
|
|
|
|
|
best_trial = study.best_trial |
|
best_params_path = os.path.join(dataset_dir, 'best_params.txt') |
|
with open(best_params_path, 'w') as f: |
|
f.write(f"Best trial value: {best_trial.value}\n\n") |
|
f.write("Best parameters:\n") |
|
for key, value in best_trial.params.items(): |
|
f.write(f"{key}: {value}\n") |
|
|
|
|
|
all_results[data_path] = { |
|
'best_value': best_trial.value, |
|
'best_params': best_trial.params, |
|
'study': study, |
|
'results_df': results_df, |
|
'failed_trials': failed_trials, |
|
'trial_stats': trial_stats |
|
} |
|
|
|
print(f"\nResults for {data_path}:") |
|
print(f"Completed trials: {completed_trials}") |
|
print(f"Failed trials: {len(failed_trials)}") |
|
print(f"Best trial value: {best_trial.value}") |
|
print("Best parameters:") |
|
for key, value in best_trial.params.items(): |
|
print(f" {key}: {value}") |
|
|
|
|
|
summary_data = [] |
|
for data_path, result in all_results.items(): |
|
summary_data.append({ |
|
'dataset': data_path, |
|
'best_accuracy': result['best_value'], |
|
'completed_trials': result['trial_stats']['completed_trials'], |
|
'failed_trials': result['trial_stats']['failed_trials'], |
|
**result['best_params'] |
|
}) |
|
|
|
summary_df = pd.DataFrame(summary_data) |
|
summary_df.to_csv(os.path.join(parent_hyperparam_dir, 'overall_summary.csv'), index=False) |
|
|
|
return parent_hyperparam_dir, all_results |
|
|
|
if __name__ == "__main__": |
|
|
|
data_paths = [ |
|
'./data/blog/datasets/bryant/random', |
|
'./data/blog/datasets/bryant/adjusted', |
|
'./data/blog/datasets/youtube/random', |
|
'./data/blog/datasets/youtube/adjusted', |
|
'./data/blog/datasets/combined/random', |
|
'./data/blog/datasets/combined/adjusted', |
|
'./data/blog/datasets/bryant_train_youtube_val/default' |
|
] |
|
|
|
|
|
hyperparam_dir, results = run_hyperparameter_search( |
|
data_paths, |
|
n_trials=8 |
|
) |
|
|
|
print(f"\nHyperparameter search complete!") |
|
print(f"Results are saved in: {hyperparam_dir}") |
|
|