File size: 5,330 Bytes
7bc5fde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"""

Train and evaluate multimodal machine learning models for Parkinson's disease classification.

This script combines traditional ML, transformer models, and ensemble methods.

"""

import os
import sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import classification_report, confusion_matrix
import warnings
warnings.filterwarnings('ignore')

# Add src directory to path
sys.path.append(os.path.join(os.path.dirname(__file__)))
sys.path.append(os.path.join(os.path.dirname(__file__), 'models'))

from data_preprocessing import DataPreprocessor
from models.multimodal_ml import MultimodalEnsemble, AdvancedFeatureEngineering, create_multimodal_pipeline


def main():
    """Main function to run multimodal ML pipeline."""
    print("Multimodal Machine Learning for Parkinson's Disease Classification")
    print("=" * 70)

    # --- Load and prepare data ---
    preprocessor = DataPreprocessor()
    base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    file_paths = [
        os.path.join(base_dir, "PPMI_Curated_Data_Cut_Public_20241211.csv"),
        os.path.join(base_dir, "PPMI_Curated_Data_Cut_Public_20250321.csv"),
        os.path.join(base_dir, "PPMI_Curated_Data_Cut_Public_20250714.csv"),
    ]

    print("\nLoading and preparing data with patient-level split...")
    X_train, X_test, y_train, y_test = preprocessor.prepare_data(
        file_paths,
        test_size=0.2,
        use_patient_split=True,
    )
    print(f"  Train: {X_train.shape}, Test: {X_test.shape}")

    # Convert to numpy arrays if they are DataFrames
    if isinstance(X_train, pd.DataFrame):
        X_train_np = X_train.values
        X_test_np = X_test.values
    else:
        X_train_np = X_train
        X_test_np = X_test

    if isinstance(y_train, pd.Series):
        y_train_np = y_train.values
        y_test_np = y_test.values
    else:
        y_train_np = y_train
        y_test_np = y_test

    # Create multimodal pipeline
    print("\nCreating multimodal ML pipeline...")
    ensemble, results = create_multimodal_pipeline(X_train, X_test, y_train_np, y_test_np)

    # Detailed evaluation of ensemble
    if ensemble.ensemble_model is not None:
        print("\nDetailed Ensemble Evaluation:")
        print("-" * 40)

        ensemble_results = ensemble.evaluate_ensemble(X_test, y_test_np)

        print(f"Ensemble Accuracy: {ensemble_results['accuracy']:.4f}")
        print("\nClassification Report:")
        print(ensemble_results['classification_report'])

        # Plot confusion matrix
        os.makedirs('notebooks', exist_ok=True)
        plt.figure(figsize=(10, 8))
        sns.heatmap(ensemble_results['confusion_matrix'],
                   annot=True, fmt='d', cmap='Blues',
                   xticklabels=['HC', 'PD', 'SWEDD', 'PRODROMAL'],
                   yticklabels=['HC', 'PD', 'SWEDD', 'PRODROMAL'])
        plt.title('Multimodal Ensemble - Confusion Matrix')
        plt.ylabel('True Label')
        plt.xlabel('Predicted Label')
        plt.tight_layout()
        plt.savefig('notebooks/multimodal_confusion_matrix.png', dpi=300, bbox_inches='tight')
        plt.close()

        print("Confusion matrix saved to notebooks/multimodal_confusion_matrix.png")

    # Advanced analysis
    print("\nAdvanced Multimodal Analysis:")
    print("-" * 40)

    # Feature importance analysis (if available)
    if hasattr(ensemble.ensemble_model, 'coef_'):
        feature_importance = np.abs(ensemble.ensemble_model.coef_).mean(axis=0)
        print("Top 10 most important ensemble features:")
        top_indices = np.argsort(feature_importance)[-10:][::-1]
        for i, idx in enumerate(top_indices):
            print(f"{i+1:2d}. Feature {idx}: {feature_importance[idx]:.4f}")

    # Model diversity analysis
    print("\nModel Diversity Analysis:")
    if len(ensemble.traditional_models) > 0 and len(ensemble.transformer_models) > 0:
        trad_preds, _ = ensemble.get_traditional_predictions(X_test)
        trans_preds, _ = ensemble.get_transformer_predictions(X_test_np)

        if trad_preds and trans_preds:
            trad_pred = list(trad_preds.values())[0]
            trans_pred = list(trans_preds.values())[0]

            agreement = np.mean(trad_pred == trans_pred)
            print(f"Agreement between traditional and transformer models: {agreement:.4f}")

    # Performance summary
    print("\nFinal Performance Summary:")
    print("-" * 40)
    best_model = max(results.items(), key=lambda x: x[1])
    print(f"Best performing model: {best_model[0]} (Accuracy: {best_model[1]:.4f})")

    if 'Ensemble' in results:
        ensemble_acc = results['Ensemble']
        individual_accs = [acc for name, acc in results.items() if name != 'Ensemble']
        if individual_accs:
            avg_individual = np.mean(individual_accs)
            improvement = ensemble_acc - avg_individual
            print(f"Ensemble improvement over average individual model: {improvement:.4f}")

    print("\nMultimodal ML pipeline completed successfully!")
    print("Results and visualizations saved to notebooks/ directory")


if __name__ == "__main__":
    main()