<jupyter_start><jupyter_text>Model explainability<jupyter_code>%matplotlib inline
import pandas as pd
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn.ensemble import RandomForestClassifier
import eli5
from eli5.sklearn import PermutationImportance
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
import graphviz
from matplotlib import pyplot as plt
from pdpbox import pdp, get_dataset, info_plots
import shap
import multiprocessing

random_state = 1
num_cpu = multiprocessing.cpu_count()
num_cpu
def read_data():
    df = pd.read_csv('data/fifa.csv')
    y = (df['Man of the Match'] == "Yes")
    feature_names = [i for i in df.columns if df[i].dtype in [np.int64]]
    X = df[feature_names]
    
    return train_test_split(X, y, random_state=random_state)<jupyter_output><empty_output><jupyter_text>Feature importance with permutationTrain a model, find feature importance by permuting values in a column and see the impactCreates simple numeric measures to see which features mattered to a model but doesn't tell how each feature matter. If a feature has medium permutation importance, that could mean* large effect for a few predictions but no effect in general* medium effect for all predictions<jupyter_code>X_train, X_test, y_train, y_test = read_data()
model = RandomForestClassifier(n_estimators=100, random_state=random_state)
model.fit(X_train, y_train)
perm = PermutationImportance(model, random_state=random_state)
perm.fit(X_test, y_test)
eli5.explain_weights_df(model, feature_names=X_test.columns.tolist())<jupyter_output><empty_output><jupyter_text>Partial Dependence Plotscalculated after a model has been fit. Repeatedly alter the value for one variable and make a series of predictions. We trace out predicted outomes on the vertical axis as we move from small value of a variable to large value (horizontal axis).The plot exists in 1D or 2D.<jupyter_code>X_train, X_test, y_train, y_test = read_data()
model = DecisionTreeClassifier(max_depth=5, min_samples_split=5, random_state=random_state)
model.fit(X_train, y_train)
feature_names = X_test.columns.tolist()
tree_graph = tree.export_graphviz(model, out_file=None, feature_names=feature_names)
graphviz.Source(tree_graph)
pdp_goals = pdp.pdp_isolate(model=model, dataset=X_test, model_features=feature_names, feature='Goal Scored')

pdp.pdp_plot(pdp_goals, 'Goal Scored')
plt.show()<jupyter_output>findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.<jupyter_text>The y axis is interpreted as change in the prediction from what it would be predicted at the baseline or left most value.The blue shaded area indicates the level of confidence<jupyter_code>feature_to_plot = 'Distance Covered (Kms)'
pdp_dist = pdp.pdp_isolate(model=model, dataset=X_test, model_features=feature_names, feature=feature_to_plot)

pdp.pdp_plot(pdp_dist, feature_to_plot)
plt.show()
rf_model = RandomForestClassifier(random_state=random_state).fit(X_train, y_train)

pdp_dist = pdp.pdp_isolate(model=rf_model, dataset=X_test, model_features=feature_names, feature=feature_to_plot)

pdp.pdp_plot(pdp_dist, feature_to_plot)
plt.show()
features_to_plot = ['Goal Scored', 'Distance Covered (Kms)']
inter1  =  pdp.pdp_interact(model=model, dataset=X_test, model_features=feature_names, features=features_to_plot)

pdp.pdp_interact_plot(pdp_interact_out=inter1, feature_names=features_to_plot, plot_type='contour')
plt.show()<jupyter_output><empty_output><jupyter_text>This graph shows predictions for any combination of Goals Scored and Distance covered.For example, we see the highest predictions when a team scores at least 1 goal and they run a total distance close to 100km. If they score 0 goals, distance covered doesn't matter.We can seee this by tracing through the decision tree with 0 goal but distance can impact predictions if they score goals.  Shapley additive explanations (SHAP)break down a prediction to show the impact of each feature -> used to explain a single prediction. Shap values show how much a given feature changed our prediction compared to if we made that prediction at some baseline value of that feature.shap.TreeExplainer for trees explainationsshap.DeepExplainer works well with Deep Learning modelsshap.KernelExplainer works with all models but sloweer than other explainers (approximation instead of exact shap values)<jupyter_code>X_train, X_test, y_train, y_test = read_data()
model = RandomForestClassifier(random_state=random_state).fit(X_train, y_train)
row_to_show = 5
data_for_prediction = X_test.iloc[row_to_show]
data_for_prediction_array = data_for_prediction.values.reshape(1, -1)

model.predict_proba(data_for_prediction_array)
explainer = shap.TreeExplainer(model)

# Calculate Shap values
shap_values = explainer.shap_values(data_for_prediction)
shap.initjs()
shap.force_plot(explainer.expected_value[1], shap_values[1], data_for_prediction)
k_explainer = shap.KernelExplainer(model.predict_proba, X_train)
k_shap_values = k_explainer.shap_values(data_for_prediction)
shap.force_plot(k_explainer.expected_value[1], k_shap_values[1], data_for_prediction)<jupyter_output><empty_output><jupyter_text>Shap summary plotsCalculate shap values can be slow, xgboost model has some optimization for that.<jupyter_code>X_train, X_test, y_train, y_test = read_data()
model = RandomForestClassifier(random_state=random_state).fit(X_train, y_train)
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
shap.summary_plot(shap_values[1], X_test)<jupyter_output><empty_output><jupyter_text>Vertical location shows what feature it is depictingColor shows whether that feature was high or low for that row of the datasetHorizontal location shows whether the effect of that value caused a higher or lower prediction  Shap Dependence Contribution PlotsPartial dependence plots showw how a single feature impacts predictions but they don't shoow distribution of effects, is the effect of having a certain value pretty constant, or does it vary a lot depending on the values of other feaures. SHAP dependence contribution plots provide a similar insight to PDP's, but they add a lot more detail.<jupyter_code>X_train, X_test, y_train, y_test = read_data()
model = RandomForestClassifier(random_state=random_state).fit(X_train, y_train)
explainer = shap.TreeExplainer(model)

X = pd.concat([X_train, X_test])
shap_values = explainer.shap_values(X)

shap.dependence_plot('Ball Possession %', shap_values[1], X, interaction_index="Goal Scored")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0725258493353028, 'fraction_numerical': 0.004431314623338257, 'mean_word_length': 7.217233009708738, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '295695', 'license': 'MIT', 'n_tokens_mistral': 2180, 'n_tokens_neox': 2114, 'n_words': 657, 'path': 'notebooks/008. model explainability.ipynb', 'repo_name': 'gillouche/exploratory-data-analysis', 'size': '6770'}