| import gc |
| import os |
|
|
| import numpy as np |
| import optuna |
| import pandas as pd |
| import torch |
| from sklearn.preprocessing import LabelEncoder |
| from torch.utils.data import DataLoader |
|
|
| from Ramandataset import RamanDataset |
| from Raman_Task import ( |
| augment_minority_classes, |
| load_mae_model_for_classification, |
| load_real_data, |
| stratified_split_with_minimum_samples, |
| train_predictor, |
| unique, |
| ) |
|
|
|
|
| def evaluate_classifier_accuracy(classifier, val_loader, device): |
| classifier.eval() |
| correct = 0 |
| total = 0 |
| with torch.no_grad(): |
| for inputs, _, labels in val_loader: |
| inputs = inputs.to(device) |
| labels = labels.to(device) |
| if labels.dim() > 1: |
| labels = labels.squeeze() |
| labels = labels.long() |
| logits, _ = classifier(inputs) |
| preds = torch.argmax(logits, dim=1) |
| total += labels.size(0) |
| correct += (preds == labels).sum().item() |
| return correct / max(1, total) |
|
|
|
|
| def run_downstream_hpo(num_trials=25, epochs_per_trial=30, pretrained_path=None): |
| base_path = "/home/lion/Desktop/splendid_lion/project/2025/multi_source/Raman/support_file/data/" |
| data_path = os.path.join(base_path, "rruff/classifier_0_3500_spectra.npy") |
| labels_path = os.path.join(base_path, "rruff/classifier_0_3500_labels.npy") |
| wavenumbers_path = os.path.join(base_path, "rruff/classifier_0_3500_wavenumbers.npy") |
|
|
| spectra, labels, _ = load_real_data( |
| data_path, labels_path=labels_path, wavenumbers_path=wavenumbers_path, normalize=True |
| ) |
| input_length = spectra.shape[1] |
| num_classes = len(unique(labels)) |
|
|
| y = np.array(labels) |
| le = LabelEncoder() |
| y_encoded = le.fit_transform(y) |
|
|
| x_train, x_val, x_test, y_train, y_val, y_test = stratified_split_with_minimum_samples( |
| spectra, y_encoded, test_size=0.15, val_size=0.15, min_samples_per_class=1, random_state=42 |
| ) |
|
|
| x_train_aug, y_train_aug = augment_minority_classes(x_train, y_train, min_samples=30, target_samples=159) |
|
|
| para = {"embedding_dim": 512, "num_heads": 16, "num_layers": 12, "patch_num": 100} |
| if pretrained_path is None: |
| pretrained_path = ( |
| "/home/lion/Desktop/splendid_lion/project/2025/multi_source/Raman/support_file/model/" |
| "0.0001_0.5_512_16_12_100/Fine_tuned_baseonALL.pth" |
| ) |
|
|
| if not os.path.exists(pretrained_path): |
| raise FileNotFoundError(f"Pretrained model not found: {pretrained_path}") |
|
|
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| print(f"Using device: {device}") |
|
|
| def objective(trial): |
| gc.collect() |
| if torch.cuda.is_available(): |
| torch.cuda.empty_cache() |
|
|
| lr = trial.suggest_categorical("lr", [1e-3, 5e-4, 1e-4, 5e-5, 1e-5]) |
| batch_size = trial.suggest_categorical("batch_size", [16, 32]) |
| weight_decay = trial.suggest_categorical("weight_decay", [1e-3, 5e-4, 1e-4, 5e-5, 1e-5]) |
| freeze_encoder = trial.suggest_categorical("freeze_encoder", [True, False]) |
|
|
| trial_dir = os.path.join(base_path, "optimization_results", f"downstream_trial_{trial.number}") |
| os.makedirs(trial_dir, exist_ok=True) |
|
|
| train_ds = RamanDataset(x_train_aug, None, labels=y_train_aug, transform=None, is_train=True) |
| val_ds = RamanDataset(x_val, None, labels=y_val, transform=None, is_train=False) |
| test_ds = RamanDataset(x_test, None, labels=y_test, transform=None, is_train=False) |
|
|
| train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True, drop_last=True, num_workers=0) |
| val_loader = DataLoader(val_ds, batch_size=batch_size, shuffle=False, num_workers=0) |
| test_loader = DataLoader(test_ds, batch_size=batch_size, shuffle=False, num_workers=0) |
|
|
| classifier, _, _, mae_model = load_mae_model_for_classification( |
| pretrained_path, |
| input_length, |
| para["patch_num"], |
| para["embedding_dim"], |
| para["num_layers"], |
| para["num_heads"], |
| num_classes, |
| device, |
| ) |
|
|
| train_predictor( |
| classifier=classifier, |
| mae_model=mae_model, |
| train_loader=train_loader, |
| val_loader=val_loader, |
| test_loader=test_loader, |
| device=device, |
| epochs=epochs_per_trial, |
| lr=lr, |
| weight_decay=weight_decay, |
| patience=10, |
| save_dir=trial_dir, |
| model_name=f"downstream_trial_{trial.number}", |
| freeze_encoder=freeze_encoder, |
| ) |
|
|
| best_ckpt = os.path.join(trial_dir, f"downstream_trial_{trial.number}_best_class.pth") |
| if os.path.exists(best_ckpt): |
| ckpt = torch.load(best_ckpt, map_location=device) |
| classifier.load_state_dict(ckpt["model_state_dict"]) |
|
|
| val_acc = evaluate_classifier_accuracy(classifier, val_loader, device) |
|
|
| row = { |
| "trial_id": trial.number, |
| "val_accuracy": val_acc, |
| "lr": lr, |
| "batch_size": batch_size, |
| "weight_decay": weight_decay, |
| "freeze_encoder": freeze_encoder, |
| } |
| pd.DataFrame([row]).to_csv( |
| "optuna_downstream_results.csv", |
| mode="a", |
| header=not os.path.exists("optuna_downstream_results.csv"), |
| index=False, |
| ) |
| return val_acc |
|
|
| study = optuna.create_study( |
| direction="maximize", |
| study_name=f"raman_downstream_optimization_{pd.Timestamp.now().strftime('%Y%m%d_%H%M%S')}", |
| storage="sqlite:///raman_optimization.db", |
| load_if_exists=False, |
| ) |
| study.optimize(objective, n_trials=num_trials, n_jobs=1, gc_after_trial=True) |
|
|
| print("=" * 60) |
| print("DOWNSTREAM HPO SUMMARY") |
| print(f"Best val acc: {study.best_value:.4f}") |
| print(f"Best params: {study.best_params}") |
| print("=" * 60) |
|
|
| best_trial_id = study.best_trial.number |
| best_trial_dir = os.path.join(base_path, "optimization_results", f"downstream_trial_{best_trial_id}") |
| summary = { |
| "best_trial_id": best_trial_id, |
| "best_value": float(study.best_value), |
| "best_params": dict(study.best_params), |
| "best_trial_dir": best_trial_dir, |
| "pretrained_path": pretrained_path, |
| } |
| return summary |
|
|
|
|
| if __name__ == "__main__": |
| run_downstream_hpo(num_trials=25, epochs_per_trial=30) |
|
|