-
Notifications
You must be signed in to change notification settings - Fork 18
[DOC] Gallery rework: basic examples implementation #684
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
5c3249b
97bff14
b8440dc
31c8889
6cb2dc9
98cc035
4dabe81
1f636a7
392d8b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Getting Started | ||
| =============== | ||
| Examples to illustrate the basic usage of HiDimStat. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| """ | ||
| General pipeline to assess feature importance | ||
| ================================================================================== | ||
| This example demonstrates how to measure feature importance using hidimstat, and the | ||
| general pipeline of functions to call when doing so. | ||
| The functions used in the following are generic to any of the feature importance assessment | ||
| methods existing in the library. Here, we will use the Conditional Feature Importance | ||
| (CFI) [:footcite:t:`Chamma_NeurIPS2023`] on a simulated regression dataset. | ||
| """ | ||
|
|
||
| # %% | ||
| # Loading and preparing the data | ||
| # ------------------------------ | ||
| # We begin by simulating a regression dataset with 10 features, 5 of which | ||
| # are in the support set, meaning they contribute to generating the outcome. In this example, | ||
| # we use a simulated dataset to have access to the true support set of features and | ||
| # evaluate how well the different models identify these important features. | ||
| # The data is then split into training and test sets. These sets are used both to fit | ||
| # the predictive models and within the LOCO procedure, which refits models on subsets | ||
| # of features that exclude the feature of interest. | ||
|
|
||
| from sklearn.datasets import make_regression | ||
| from sklearn.model_selection import train_test_split | ||
|
|
||
| X, y, beta = make_regression( | ||
| n_samples=300, | ||
| n_features=10, | ||
| n_informative=5, | ||
| random_state=0, | ||
| coef=True, | ||
| noise=10.0, | ||
| ) | ||
|
|
||
| # We convert the coefficients of the data-generating process into a binary array | ||
| # indicating the true support set of features. | ||
| beta = beta != 0 | ||
|
|
||
| X_train, X_test, y_train, y_test = train_test_split( | ||
| X, | ||
| y, | ||
| test_size=0.2, | ||
| random_state=0, | ||
| shuffle=True, | ||
| ) | ||
|
|
||
| # %% | ||
| # Fitting the model and computing feature importance | ||
| # ------------------------------------------------------ | ||
| # To solve the classification task, we use a pipeline that first standardizes the features with StandardScaler, | ||
| # followed by a neural network (MLPClassifier) with one hidden layer of 8 neurons. | ||
| # Before measuring feature importance, we evaluate the estimator's performance by reporting its :math:`R^2` score. | ||
|
|
||
| from sklearn.ensemble import RandomForestRegressor | ||
|
|
||
| clf = RandomForestRegressor(n_estimators=150, random_state=0) | ||
|
|
||
| clf.fit(X_train, y_train) | ||
| y_pred = clf.predict(X_test) | ||
| print(f"Accuracy: {clf.score(X_test, y_test):.3f}") | ||
|
|
||
| # %% | ||
| # Next, we use the CFI class to measure feature importance. Here, we use a RidgeCV | ||
| # model to estimate the conditional expectation :math:`\mathbb{E}[X^j | X^{-j}]`. | ||
| # Since this is a regression task, we use mean_squared_error. | ||
|
|
||
| from sklearn.linear_model import RidgeCV | ||
| from sklearn.metrics import mean_squared_error | ||
|
|
||
| from hidimstat import CFI | ||
|
|
||
| cfi = CFI( | ||
| estimator=clf, | ||
| loss=mean_squared_error, | ||
| imputation_model_continuous=RidgeCV(), | ||
| features_groups={f"Feat {i}": [i] for i in range(X.shape[1])}, | ||
| random_state=0, | ||
| ) | ||
| cfi.fit( | ||
| X_train, | ||
| y_train, | ||
| ) | ||
| importances = cfi.importance(X_test, y_test) | ||
|
|
||
| # %% | ||
| # An alternative is to call the `fit_importance` method which conveniently combines both functions. | ||
| # Please keep in mind that both the fit and feature importance will subsequently use the same data. | ||
|
|
||
|
|
||
| # %% | ||
| # Visualization of CFI feature importance and feature selection | ||
| # ---------------------------------------------------------------- | ||
| # Finally, we visualize the importance of each feature using a bar plot | ||
| # thanks to a built-in importance visualization function, and perform | ||
| # feature selection with statistical guarantees. | ||
|
|
||
| import matplotlib.pyplot as plt | ||
| import numpy as np | ||
|
|
||
| _, ax = plt.subplots(figsize=(6, 3)) | ||
| ax = cfi.plot_importance(ax=ax) | ||
| ax.set_xlabel("Feature Importance") | ||
|
|
||
| # Since the figure displays importance measures in a descending order, | ||
| # we need to sort betas accordingly: | ||
|
|
||
| sorted_beta = beta[np.argsort(importances)[::-1]] | ||
|
|
||
| for i, support in enumerate(sorted_beta): | ||
| if support != 0: | ||
| ax.axhspan( | ||
| i - 0.45, | ||
| i + 0.45, | ||
| color="tab:olive", | ||
| alpha=0.3, | ||
| zorder=-1, | ||
| label="True Support" if i == 1 else None, | ||
| ) | ||
| ax.legend() | ||
|
|
||
| plt.tight_layout() | ||
| plt.show() | ||
|
|
||
|
|
||
| # %% | ||
| # Next, we can make a function call to select the most important features. This can be done through | ||
| # one of three difference control mechanisms: through p-values, on False Discovery Rate (FDR) | ||
| # or on Family-Wise Error Rate (FWER). Each function returns a boolean mask that indicates us | ||
| # which features to keep based on the selected control mechanism. | ||
| # Here, we will use select importance through p_values and set a standard threshold of 0.05. | ||
|
|
||
| selection = cfi.pvalue_selection(threshold_max=0.05) | ||
| important_features = X[:, selection] | ||
|
|
||
| # %% | ||
| # This is the basic pipeline for feature importance assessment and feature selection. | ||
| # We demonstrated the general workflow using CFI, but other implemented methods can be used | ||
| # in a similar manner. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering whether its would be useful to plot error bars on the estimated importance ? |
||
|
|
||
| # %% | ||
| # References | ||
| # ---------- | ||
| # .. footbibliography:: | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| """ | ||
| Measuring Individual and Group Variable Importance for Classification | ||
| ====================================================================== | ||
|
|
||
| In this example, we show on the Iris dataset how to measure variable importance for | ||
| classification tasks. This time, we use the Permutation Feature Importance (PFI) method | ||
| with a Support Vector Classifier (SVC). We start by measuring the importance of | ||
| individual variables and then show how to measure the importance of groups of variables. | ||
|
|
||
| To briefly summarize, PFI (Permutation Feature Importance) shuffles the values of | ||
| a feature and measures the increase in the loss when predicting (using om the same | ||
| full model) on the shuffled data. | ||
|
|
||
| """ | ||
|
|
||
| import matplotlib.pyplot as plt | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we avoid having all imports at the beginning, and rather have them when needed along the script ?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I think it makes more sense as it's closer to a notebook. I'll make the change over all the scripts |
||
| import numpy as np | ||
| from sklearn.datasets import load_wine | ||
| from sklearn.linear_model import RidgeCV | ||
| from sklearn.metrics import log_loss | ||
| from sklearn.model_selection import train_test_split | ||
| from sklearn.neural_network import MLPClassifier | ||
| from sklearn.pipeline import make_pipeline | ||
| from sklearn.preprocessing import StandardScaler | ||
|
|
||
| from hidimstat import CFI | ||
|
|
||
| # Define the seeds for the reproducibility of the example | ||
| rng = np.random.default_rng(0) | ||
|
|
||
| # %% | ||
| # Load the iris dataset and add a spurious feature | ||
| # ------------------------------------------------ | ||
| # We start by loading the iris dataset as is, and splitting for training and testing. | ||
|
|
||
| dataset = load_wine() | ||
| X, y = dataset.data, dataset.target | ||
| feature_names = np.array(dataset.feature_names) | ||
| print(feature_names) | ||
| X_train, X_test, y_train, y_test = train_test_split( | ||
| X, | ||
| y, | ||
| test_size=0.2, | ||
| random_state=0, | ||
| shuffle=True, | ||
| ) | ||
|
|
||
| # %% | ||
| # We use a Multi-Layer Perceptron with one hidden layer of size 100. | ||
| # We fit the estimator, compute the feature importance, and use the | ||
| # built-in importance plot function. | ||
|
|
||
| model = make_pipeline( | ||
| StandardScaler(), | ||
| MLPClassifier(hidden_layer_sizes=(100), random_state=0, max_iter=500), | ||
| ) | ||
| model.fit(X_train, y_train) | ||
|
|
||
| cfi = CFI( | ||
| estimator=model, | ||
| imputation_model_continuous=RidgeCV(), | ||
| n_permutations=50, | ||
| random_state=2, | ||
| method="predict_proba", | ||
| loss=log_loss, | ||
| ) | ||
|
|
||
| cfi.fit(X_train, y_train) | ||
| importance = cfi.importance(X_test, y_test) | ||
| selection = cfi.pvalue_selection(threshold_max=0.05) | ||
|
|
||
| _, ax = plt.subplots(figsize=(6, 3)) | ||
| ax = cfi.plot_importance(ax=ax) | ||
| ax.set_xlabel("Feature Importance") | ||
| ax.set_yticklabels(feature_names[np.argsort(importance)[::-1]]) | ||
| # ax.text(0, 0, f"Selected features: {feature_names[selection]}") | ||
| plt.tight_layout() | ||
| plt.show() | ||
|
|
||
|
|
||
| # %% | ||
| # Measuring the importance of groups of features | ||
| # ---------------------------------------------- | ||
| # In the example above, CFI did not select some features. This is because it | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where can this be found ? |
||
| # measures conditional importance, which is the additional independent information a | ||
| # feature provides knowing all the other features. When features are highly correlated, | ||
| # this additional information decreases, resulting in lower importance rankings. To | ||
| # mitigate this issue, we can group correlated features together and measure the | ||
| # importance of these feature groups. Here, we regroup variables into the following groups: | ||
| # - "acid": `malic_acid` and `proline` | ||
| # - "ash": `ash` and `alcalinity of ash` | ||
| # - "phenols": `total_phenols`, `flavanoids`, `nonflavanoid_phenols`, and `proanthocyanins` | ||
| # - "color": `hue`, and `color_intensity` | ||
|
|
||
| features_groups = { | ||
| "acid": [1, 12], | ||
| "ash": [2, 3], | ||
| "phenols": [5, 6, 7, 8], | ||
| "color": [9, 10], | ||
| } | ||
|
|
||
|
|
||
| cfi = CFI( | ||
| estimator=model, | ||
| imputation_model_continuous=RidgeCV(), | ||
| n_permutations=50, | ||
| random_state=2, | ||
| method="predict_proba", | ||
| loss=log_loss, | ||
| features_groups=features_groups, | ||
| ) | ||
|
|
||
| importance = cfi.fit_importance(X_test, y_test) | ||
|
|
||
| _, ax = plt.subplots(figsize=(6, 3)) | ||
| ax = cfi.plot_importance(ax=ax) | ||
| ax.set_xlabel("Feature Importance") | ||
| plt.tight_layout() | ||
| plt.show() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.