shap_vals.py 8.5 KB
Newer Older
1 2 3 4
# Libraries
# --------------------------------------------------------------------------------------------------------
import pandas as pd
import numpy as np
5
import shap
6
import ast
7

8 9 10 11 12 13
from xgboost import XGBClassifier
from sklearn.ensemble import RandomForestClassifier, BaggingClassifier, AdaBoostClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.svm import SVC
from sklearn.linear_model import  LogisticRegression
from sklearn.tree import DecisionTreeClassifier
14 15 16 17
# --------------------------------------------------------------------------------------------------------

# Reading test and training data
# --------------------------------------------------------------------------------------------------------
18
def read_data(attribute_names):
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
    # Load test data
    X_test_pre = np.load('../gen_train_data/data/output/pre/X_test_pre.npy', allow_pickle=True)
    y_test_pre = np.load('../gen_train_data/data/output/pre/y_test_pre.npy', allow_pickle=True)
    X_test_post = np.load('../gen_train_data/data/output/post/X_test_post.npy', allow_pickle=True)
    y_test_post = np.load('../gen_train_data/data/output/post/y_test_post.npy', allow_pickle=True)

    # Load ORIGINAL training data
    X_train_pre = np.load('../gen_train_data/data/output/pre/X_train_pre.npy', allow_pickle=True)
    y_train_pre = np.load('../gen_train_data/data/output/pre/y_train_pre.npy', allow_pickle=True)
    X_train_post = np.load('../gen_train_data/data/output/post/X_train_post.npy', allow_pickle=True)
    y_train_post = np.load('../gen_train_data/data/output/post/y_train_post.npy', allow_pickle=True)

    # Load oversampled training data
    X_train_over_pre = np.load('../gen_train_data/data/output/pre/X_train_over_pre.npy', allow_pickle=True)
    y_train_over_pre = np.load('../gen_train_data/data/output/pre/y_train_over_pre.npy', allow_pickle=True)
    X_train_over_post = np.load('../gen_train_data/data/output/post/X_train_over_post.npy', allow_pickle=True)
    y_train_over_post = np.load('../gen_train_data/data/output/post/y_train_over_post.npy', allow_pickle=True)

    # Load undersampled training data
    X_train_under_pre = np.load('../gen_train_data/data/output/pre/X_train_under_pre.npy', allow_pickle=True)
    y_train_under_pre = np.load('../gen_train_data/data/output/pre/y_train_under_pre.npy', allow_pickle=True)
    X_train_under_post = np.load('../gen_train_data/data/output/post/X_train_under_post.npy', allow_pickle=True)
    y_train_under_post = np.load('../gen_train_data/data/output/post/y_train_under_post.npy', allow_pickle=True)

43
    # Type conversion needed    
44
    data_dic = {
45
        "X_test_pre": pd.DataFrame(X_test_pre, columns=attribute_names).convert_dtypes(),
46
        "y_test_pre": y_test_pre,
47
        "X_test_post": pd.DataFrame(X_test_post, columns=attribute_names).convert_dtypes(),
48
        "y_test_post": y_test_post,
49
        "X_train_pre": pd.DataFrame(X_train_pre, columns=attribute_names).convert_dtypes(),
50
        "y_train_pre": y_train_pre,
51
        "X_train_post": pd.DataFrame(X_train_post, columns=attribute_names).convert_dtypes(),
52
        "y_train_post": y_train_post,
53
        "X_train_over_pre": pd.DataFrame(X_train_over_pre, columns=attribute_names).convert_dtypes(),
54
        "y_train_over_pre": y_train_over_pre,
55
        "X_train_over_post": pd.DataFrame(X_train_over_post, columns=attribute_names).convert_dtypes(),
56
        "y_train_over_post": y_train_over_post,
57
        "X_train_under_pre": pd.DataFrame(X_train_under_pre, columns=attribute_names).convert_dtypes(),
58
        "y_train_under_pre": y_train_under_pre,
59
        "X_train_under_post": pd.DataFrame(X_train_under_post, columns=attribute_names).convert_dtypes(),
60 61 62 63 64
        "y_train_under_post": y_train_under_post,
    }
    return data_dic
# --------------------------------------------------------------------------------------------------------

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
# Retrieving parameters for chosen models
# --------------------------------------------------------------------------------------------------------
def get_chosen_model(group_str, method_str, model_name):
    # Read sheet corresponding to group and method with tuned models and their hyperparameters
    tuned_models_df = pd.read_excel("../model_selection/output_hyperparam/hyperparamers.xlsx", sheet_name=f"{group_str}_{method_str}")
    tuned_models_df.columns = ['Model', 'Best Parameters']
    
    # Define the mapping from model abbreviations to sklearn model classes
    model_mapping = {
        'DT': DecisionTreeClassifier,
        'RF': RandomForestClassifier,
        'Bagging': BaggingClassifier,
        'AB': AdaBoostClassifier,
        'XGB': XGBClassifier,
        'LR': LogisticRegression,
        'SVM': SVC,
        'MLP': MLPClassifier
    }
    
    # Access the row for the given model name by checking the first column (index 0)
    row = tuned_models_df[tuned_models_df['Model'] == model_name].iloc[0]

    # Parse the dictionary of parameters from the 'Best Parameters' column
    parameters = ast.literal_eval(row['Best Parameters'])
    
    # Modify parameters based on model specifics or methods if necessary
    if model_name == 'AB':
        parameters['algorithm'] = 'SAMME'
    elif model_name == 'LR':
        parameters['max_iter'] = 1000
    elif model_name == 'SVM':
        parameters['max_iter'] = 1000
        parameters['probability'] = True
    elif model_name == "MLP":
        parameters['max_iter'] = 500
    
    # Add class_weight argument for cost-sensitive learning method
    if 'CW' in method_str:
        if model_name in ['Bagging', 'AB']:
            parameters['estimator'] = DecisionTreeClassifier(class_weight='balanced')
        else:
            parameters['class_weight'] = 'balanced'

    # Fetch the class of the model
    model_class = model_mapping[model_name]

    # Initialize the model with the parameters
    chosen_model = model_class(**parameters)
113 114
    # Return if it is a tree model, for SHAP
    is_tree = model_name not in ['LR', 'SVM', 'MLP']
115
    
116
    return chosen_model, is_tree
117 118
# --------------------------------------------------------------------------------------------------------

119 120 121 122
if __name__ == "__main__":

    # Setup
    # --------------------------------------------------------------------------------------------------------
123 124
    # Retrieve attribute names in order
    attribute_names = list(np.load('../gen_train_data/data/output/attributes.npy', allow_pickle=True))
125
    # Reading data
126
    data_dic = read_data(attribute_names)
127 128 129 130 131 132
    method_names = {
        0: "ORIG",
        1: "ORIG_CW",
        2: "OVER",
        3: "UNDER"
    }
133 134 135 136 137
    model_choices = {
        "ORIG": "XGB",
        "ORIG_CW": "RF",
        "OVER": "XGB",
        "UNDER": "XGB"
138 139 140 141 142 143
    }
    # --------------------------------------------------------------------------------------------------------

    # Shap value generation
    # --------------------------------------------------------------------------------------------------------
    for i, group in enumerate(['pre', 'post']):
144
        # Get test dataset based on group, add column names
145
        X_test = data_dic['X_test_' + group]
146 147 148 149
        y_test = data_dic['y_test_' + group]
        for j, method in enumerate(['', '', 'over_', 'under_']):
            print(f"{group}-{method_names[j]}")
            # Get train dataset based on group and method
150
            X_train = data_dic['X_train_' + method + group]
151
            y_train = data_dic['y_train_' + method + group]
152 153
            method_name = method_names[j]
            # Get chosen tuned model for this group and method context
154 155
            model, is_tree = get_chosen_model(group_str=group, method_str=method_name, model_name=model_choices[method_name])
            # --------------------------------------------------------------------------------------------------------
156
            fitted_model = model.fit(X_train[:50], y_train[:50])
157
            # # Check if we are dealing with a tree vs nn model
158
            if is_tree:
159 160 161
                 explainer = shap.TreeExplainer(fitted_model)
            # else:
            #     explainer = shap.KernelExplainer(fitted_model.predict_proba, X_test[:500])
162
            # Compute shap values
163
            shap_vals = explainer.shap_values(X_test[:50], check_additivity=False) # Change to true for final results
164
            # ---------------------------------------------------------------------------------------------------------
165
            # Save results
166
            np.save(f"./output/shap_values/{group}_{method_names[j]}", shap_vals)
167
            print(f'Shape of numpy array: {shap_vals.shape}')
168
    # --------------------------------------------------------------------------------------------------------