| """ |
| Baby Cry AI - Hyperparameter Tuning |
| Uses Optuna for systematic hyperparameter optimization |
| """ |
|
|
| import os |
| import sys |
| import numpy as np |
| import optuna |
| from sklearn.model_selection import cross_val_score |
| from sklearn.ensemble import RandomForestClassifier |
| from sklearn.preprocessing import StandardScaler |
| import warnings |
| warnings.filterwarnings('ignore') |
|
|
| |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) |
|
|
| from models.neural_model import NeuralModel |
| from models.baseline_model import BaselineModel |
| from audio_processor import AudioProcessor |
|
|
|
|
| class HyperparameterTuner: |
| """Hyperparameter tuning for models""" |
| |
| def __init__(self, data_dir="../data"): |
| """ |
| Initialize tuner |
| |
| Args: |
| data_dir: Path to data directory |
| """ |
| self.data_dir = data_dir |
| self.processor = AudioProcessor() |
| self.best_params = {} |
| self.best_score = 0 |
| |
| def tune_random_forest(self, X, y, n_trials=50): |
| """ |
| Tune Random Forest hyperparameters |
| |
| Args: |
| X: Feature matrix |
| y: Labels |
| n_trials: Number of optimization trials |
| """ |
| print("๐ Tuning Random Forest hyperparameters...") |
| |
| def objective(trial): |
| |
| n_estimators = trial.suggest_int('n_estimators', 50, 300, step=50) |
| max_depth = trial.suggest_int('max_depth', 5, 30, step=5) |
| min_samples_split = trial.suggest_int('min_samples_split', 2, 10) |
| min_samples_leaf = trial.suggest_int('min_samples_leaf', 1, 5) |
| max_features = trial.suggest_categorical('max_features', ['sqrt', 'log2', None]) |
| |
| |
| model = RandomForestClassifier( |
| n_estimators=n_estimators, |
| max_depth=max_depth, |
| min_samples_split=min_samples_split, |
| min_samples_leaf=min_samples_leaf, |
| max_features=max_features, |
| class_weight='balanced', |
| random_state=42, |
| n_jobs=-1 |
| ) |
| |
| |
| scaler = StandardScaler() |
| X_scaled = scaler.fit_transform(X) |
| |
| |
| scores = cross_val_score(model, X_scaled, y, cv=5, scoring='accuracy', n_jobs=-1) |
| return scores.mean() |
| |
| study = optuna.create_study(direction='maximize') |
| study.optimize(objective, n_trials=n_trials, show_progress_bar=True) |
| |
| self.best_params['random_forest'] = study.best_params |
| self.best_score = study.best_value |
| |
| print(f"โ
Best Random Forest score: {study.best_value:.4f}") |
| print(f"๐ Best parameters: {study.best_params}") |
| |
| return study.best_params, study.best_value |
| |
| def tune_neural_network(self, X, y, n_trials=20): |
| """ |
| Tune Neural Network hyperparameters |
| |
| Args: |
| X: Mel-spectrograms |
| y: Labels |
| n_trials: Number of optimization trials |
| """ |
| print("๐ Tuning Neural Network hyperparameters...") |
| |
| def objective(trial): |
| |
| dropout_rate = trial.suggest_float('dropout_rate', 0.3, 0.7) |
| learning_rate = trial.suggest_loguniform('learning_rate', 1e-5, 1e-2) |
| batch_size = trial.suggest_categorical('batch_size', [16, 32, 64]) |
| num_conv_filters_1 = trial.suggest_int('num_conv_filters_1', 16, 64, step=16) |
| num_conv_filters_2 = trial.suggest_int('num_conv_filters_2', 32, 128, step=32) |
| num_dense_units = trial.suggest_int('num_dense_units', 128, 512, step=128) |
| |
| |
| model = NeuralModel() |
| model.input_shape = X[0].shape |
| |
| |
| import tensorflow as tf |
| from tensorflow import keras |
| from tensorflow.keras import layers |
| |
| num_classes = len(np.unique(y)) |
| from sklearn.preprocessing import LabelEncoder |
| le = LabelEncoder() |
| y_encoded = le.fit_transform(y) |
| |
| nn_model = keras.Sequential([ |
| layers.Conv2D(num_conv_filters_1, (3, 3), activation='relu', input_shape=X[0].shape), |
| layers.BatchNormalization(), |
| layers.MaxPooling2D((2, 2)), |
| layers.Dropout(dropout_rate * 0.5), |
| |
| layers.Conv2D(num_conv_filters_2, (3, 3), activation='relu'), |
| layers.BatchNormalization(), |
| layers.MaxPooling2D((2, 2)), |
| layers.Dropout(dropout_rate), |
| |
| layers.GlobalAveragePooling2D(), |
| layers.Dense(num_dense_units, activation='relu'), |
| layers.BatchNormalization(), |
| layers.Dropout(dropout_rate), |
| layers.Dense(num_classes, activation='softmax') |
| ]) |
| |
| nn_model.compile( |
| optimizer=keras.optimizers.Adam(learning_rate=learning_rate), |
| loss='sparse_categorical_crossentropy', |
| metrics=['accuracy'] |
| ) |
| |
| |
| from sklearn.model_selection import train_test_split |
| X_train, X_val, y_train, y_val = train_test_split( |
| X, y_encoded, test_size=0.2, random_state=42, stratify=y_encoded |
| ) |
| |
| from tensorflow.keras import callbacks |
| early_stop = callbacks.EarlyStopping( |
| monitor='val_loss', |
| patience=5, |
| restore_best_weights=True, |
| verbose=0 |
| ) |
| |
| try: |
| nn_model.fit( |
| X_train, y_train, |
| batch_size=batch_size, |
| epochs=20, |
| validation_data=(X_val, y_val), |
| callbacks=[early_stop], |
| verbose=0 |
| ) |
| |
| |
| val_loss, val_acc = nn_model.evaluate(X_val, y_val, verbose=0) |
| return val_acc |
| except Exception as e: |
| print(f" โ ๏ธ Trial failed: {e}") |
| return 0.0 |
| |
| study = optuna.create_study(direction='maximize') |
| study.optimize(objective, n_trials=n_trials, show_progress_bar=True) |
| |
| self.best_params['neural_network'] = study.best_params |
| self.best_score = study.best_value |
| |
| print(f"โ
Best Neural Network score: {study.best_value:.4f}") |
| print(f"๐ Best parameters: {study.best_params}") |
| |
| return study.best_params, study.best_value |
| |
| def tune_audio_processor(self, file_paths, labels, n_trials=30): |
| """ |
| Tune audio processing parameters |
| |
| Args: |
| file_paths: List of audio file paths |
| labels: Corresponding labels |
| n_trials: Number of optimization trials |
| """ |
| print("๐ Tuning Audio Processor hyperparameters...") |
| |
| def objective(trial): |
| |
| highpass_cutoff = trial.suggest_int('highpass_cutoff', 50, 200, step=50) |
| trim_top_db = trial.suggest_int('trim_top_db', 20, 40, step=5) |
| n_mfcc = trial.suggest_int('n_mfcc', 10, 20, step=2) |
| |
| |
| processor = AudioProcessor() |
| |
| |
| |
| |
| try: |
| features_list = [] |
| labels_list = [] |
| |
| for file_path, label in zip(file_paths[:50], labels[:50]): |
| features = processor.extract_features_from_file(str(file_path)) |
| if features is not None: |
| features_list.append(list(features.values())) |
| labels_list.append(label) |
| |
| if len(features_list) < 10: |
| return 0.0 |
| |
| X = np.array(features_list) |
| y = np.array(labels_list) |
| |
| |
| from sklearn.ensemble import RandomForestClassifier |
| from sklearn.model_selection import cross_val_score |
| from sklearn.preprocessing import StandardScaler |
| |
| scaler = StandardScaler() |
| X_scaled = scaler.fit_transform(X) |
| |
| model = RandomForestClassifier(n_estimators=50, random_state=42, n_jobs=-1) |
| scores = cross_val_score(model, X_scaled, y, cv=3, scoring='accuracy', n_jobs=-1) |
| |
| return scores.mean() |
| except Exception as e: |
| return 0.0 |
| |
| study = optuna.create_study(direction='maximize') |
| study.optimize(objective, n_trials=n_trials, show_progress_bar=True) |
| |
| self.best_params['audio_processor'] = study.best_params |
| self.best_score = study.best_value |
| |
| print(f"โ
Best Audio Processor score: {study.best_value:.4f}") |
| print(f"๐ Best parameters: {study.best_params}") |
| |
| return study.best_params, study.best_value |
| |
| def get_best_params(self): |
| """Get best parameters found""" |
| return self.best_params |
| |
| def save_results(self, output_path="hyperparameter_tuning_results.json"): |
| """Save tuning results""" |
| import json |
| from datetime import datetime |
| |
| results = { |
| 'timestamp': datetime.now().isoformat(), |
| 'best_params': self.best_params, |
| 'best_score': self.best_score |
| } |
| |
| with open(output_path, 'w') as f: |
| json.dump(results, f, indent=2) |
| |
| print(f"๐พ Results saved to: {output_path}") |
|
|
|
|
| if __name__ == "__main__": |
| import argparse |
| |
| parser = argparse.ArgumentParser(description='Hyperparameter tuning') |
| parser.add_argument('--data-dir', type=str, default='../data', |
| help='Path to data directory') |
| parser.add_argument('--model', type=str, choices=['rf', 'nn', 'both'], |
| default='both', help='Model to tune') |
| parser.add_argument('--n-trials', type=int, default=50, |
| help='Number of optimization trials') |
| |
| args = parser.parse_args() |
| |
| tuner = HyperparameterTuner(data_dir=args.data_dir) |
| |
| if args.model in ['rf', 'both']: |
| |
| baseline_model = BaselineModel() |
| X, y = baseline_model.load_data_from_directory(args.data_dir, balance_data=True) |
| if X is not None and y is not None: |
| tuner.tune_random_forest(X, y, n_trials=args.n_trials) |
| |
| if args.model in ['nn', 'both']: |
| |
| neural_model = NeuralModel() |
| X, y = neural_model.load_data_from_directory(args.data_dir, balance_data=True) |
| if X is not None and y is not None: |
| tuner.tune_neural_network(X, y, n_trials=min(args.n_trials, 20)) |
| |
| tuner.save_results() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|