BBPlease / src /hyperparameter_tuner.py
hamza-ks's picture
Relaunch deploy: RF v5 (72.6%), voc2vec fallback, telemetry removed, secrets docs removed
84e6d52
Raw
History Blame Contribute Delete
11.7 kB
"""
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')
# Add parent directory to path
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):
# Suggest hyperparameters
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])
# Create model
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
)
# Scale features
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Cross-validation score
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):
# Suggest hyperparameters
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)
# Create and train model
model = NeuralModel()
model.input_shape = X[0].shape
# Build custom model with suggested parameters
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']
)
# Train with early stopping
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
)
# Evaluate
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):
# Suggest preprocessing parameters
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)
# Create processor with suggested parameters
processor = AudioProcessor()
# Note: These would need to be configurable in AudioProcessor
# For now, we'll use a simplified approach
# Extract features and evaluate
try:
features_list = []
labels_list = []
for file_path, label in zip(file_paths[:50], labels[:50]): # Limit for speed
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)
# Quick evaluation with simple model
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']:
# Load data for Random Forest
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']:
# Load data for Neural Network
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)) # Limit NN trials
tuner.save_results()