From 654a5a3f609bf59eee59dacd5710963aae7a0915 Mon Sep 17 00:00:00 2001 From: mgarbacz Date: Fri, 20 Nov 2020 17:09:36 +0100 Subject: [PATCH 1/2] Implement verbose to ShapRSECV --- .../nb_shap_feature_elimination.ipynb | 100 +++++++++--------- .../feature_elimination.py | 75 +++++++++---- probatus/interpret/model_interpret.py | 1 - .../test_feature_elimination.py | 2 +- 4 files changed, 106 insertions(+), 72 deletions(-) diff --git a/docs/tutorials/nb_shap_feature_elimination.ipynb b/docs/tutorials/nb_shap_feature_elimination.ipynb index 9fa5c7fb..4b665e9c 100644 --- a/docs/tutorials/nb_shap_feature_elimination.ipynb +++ b/docs/tutorials/nb_shap_feature_elimination.ipynb @@ -138,7 +138,7 @@ " \n", " 1\n", " -25.0\n", - " 0.772855\n", + " NaN\n", " 0\n", " 0.302824\n", " 0.729950\n", @@ -207,7 +207,7 @@ " \n", " 4\n", " -10.0\n", - " 1.505766\n", + " NaN\n", " 0\n", " -0.576209\n", " -0.790525\n", @@ -234,10 +234,10 @@ "text/plain": [ " f1_categorical f2_missing f3_static f4 f5 f6 \\\n", "0 34.0 -3.902230 0 0.037207 -0.211075 2.378358 \n", - "1 -25.0 0.772855 0 0.302824 0.729950 0.815054 \n", + "1 -25.0 NaN 0 0.302824 0.729950 0.815054 \n", "2 -7.0 1.350847 0 1.837895 -0.745689 0.327826 \n", "3 -53.0 4.559465 0 -1.277930 3.688404 -2.369522 \n", - "4 -10.0 1.505766 0 -0.576209 -0.790525 -0.585126 \n", + "4 -10.0 NaN 0 -0.576209 -0.790525 -0.585126 \n", "\n", " f7 f8 f9 f10 f11 f12 f13 \\\n", "0 0.474059 -0.580471 2.523367 1.265063 -0.698129 0.320310 0.373186 \n", @@ -336,7 +336,7 @@ "output_type": "stream", "text": [ "Removing static features ['f3_static'].\n", - "The following variables contain missing values ['f2_missing']. Make sure to imputemissing or apply a model that handles them automatically.\n", + "The following variables contain missing values ['f2_missing']. Make sure to impute missing or apply a model that handles them automatically.\n", "Changing dtype of ['f1_categorical'] from \"object\" to \"category\". Treating it as categorical variable. Make sure that the model handles categorical variables, or encode them first.\n" ] }, @@ -344,38 +344,38 @@ "name": "stdout", "output_type": "stream", "text": [ - "Round: 1, Current number of features: 19, Current performance: Train 0.947 +/- 0.005, CV Validation 0.905 +/- 0.024. \n", + "Round: 1, Current number of features: 19, Current performance: Train 0.963 +/- 0.006, CV Validation 0.913 +/- 0.026. \n", "Num of features left: 16. Removed features at the end of the round: ['f6', 'f17', 'f4']\n", - "Round: 2, Current number of features: 16, Current performance: Train 0.947 +/- 0.005, CV Validation 0.905 +/- 0.024. \n", - "Num of features left: 13. Removed features at the end of the round: ['f2_missing', 'f7', 'f13']\n", - "Round: 3, Current number of features: 13, Current performance: Train 0.964 +/- 0.006, CV Validation 0.913 +/- 0.027. \n", + "Round: 2, Current number of features: 16, Current performance: Train 0.963 +/- 0.007, CV Validation 0.914 +/- 0.023. \n", + "Num of features left: 13. Removed features at the end of the round: ['f2_missing', 'f13', 'f7']\n", + "Round: 3, Current number of features: 13, Current performance: Train 0.964 +/- 0.006, CV Validation 0.914 +/- 0.023. \n", "Num of features left: 11. Removed features at the end of the round: ['f18', 'f12']\n", - "Round: 4, Current number of features: 11, Current performance: Train 0.951 +/- 0.009, CV Validation 0.905 +/- 0.037. \n", - "Num of features left: 9. Removed features at the end of the round: ['f10', 'f11']\n", + "Round: 4, Current number of features: 11, Current performance: Train 0.961 +/- 0.007, CV Validation 0.914 +/- 0.027. \n", + "Num of features left: 9. Removed features at the end of the round: ['f11', 'f10']\n", "Round: 5, Current number of features: 9, Current performance: Train 0.959 +/- 0.008, CV Validation 0.914 +/- 0.031. \n", "Num of features left: 8. Removed features at the end of the round: ['f20']\n", - "Round: 6, Current number of features: 8, Current performance: Train 0.958 +/- 0.007, CV Validation 0.91 +/- 0.027. \n", + "Round: 6, Current number of features: 8, Current performance: Train 0.939 +/- 0.008, CV Validation 0.896 +/- 0.03. \n", "Num of features left: 7. Removed features at the end of the round: ['f8']\n", "Round: 7, Current number of features: 7, Current performance: Train 0.951 +/- 0.009, CV Validation 0.901 +/- 0.029. \n", "Num of features left: 6. Removed features at the end of the round: ['f5']\n", - "Round: 8, Current number of features: 6, Current performance: Train 0.949 +/- 0.01, CV Validation 0.9 +/- 0.03. \n", + "Round: 8, Current number of features: 6, Current performance: Train 0.947 +/- 0.01, CV Validation 0.901 +/- 0.031. \n", "Num of features left: 5. Removed features at the end of the round: ['f14']\n", - "Round: 9, Current number of features: 5, Current performance: Train 0.923 +/- 0.005, CV Validation 0.883 +/- 0.035. \n", + "Round: 9, Current number of features: 5, Current performance: Train 0.937 +/- 0.007, CV Validation 0.894 +/- 0.023. \n", "Num of features left: 4. Removed features at the end of the round: ['f15']\n", - "Round: 10, Current number of features: 4, Current performance: Train 0.916 +/- 0.003, CV Validation 0.868 +/- 0.029. \n", + "Round: 10, Current number of features: 4, Current performance: Train 0.917 +/- 0.003, CV Validation 0.869 +/- 0.03. \n", "Num of features left: 3. Removed features at the end of the round: ['f1_categorical']\n", "Round: 11, Current number of features: 3, Current performance: Train 0.891 +/- 0.005, CV Validation 0.867 +/- 0.028. \n", "Num of features left: 2. Removed features at the end of the round: ['f9']\n", "Round: 12, Current number of features: 2, Current performance: Train 0.842 +/- 0.005, CV Validation 0.818 +/- 0.036. \n", "Num of features left: 1. Removed features at the end of the round: ['f19']\n", - "Round: 13, Current number of features: 1, Current performance: Train 0.764 +/- 0.007, CV Validation 0.72 +/- 0.051. \n", + "Round: 13, Current number of features: 1, Current performance: Train 0.755 +/- 0.007, CV Validation 0.72 +/- 0.044. \n", "Num of features left: 1. Removed features at the end of the round: []\n" ] } ], "source": [ "shap_elimination = ShapRFECV(\n", - " clf=search, step=0.2, cv=10, scoring='roc_auc', n_jobs=3)\n", + " clf=search, step=0.2, cv=10, scoring='roc_auc', n_jobs=3, verbose=100)\n", "report = shap_elimination.fit_compute(X, y)" ] }, @@ -427,45 +427,45 @@ " 19\n", " [f1_categorical, f2_missing, f4, f5, f6, f7, f...\n", " [f6, f17, f4]\n", - " 0.947\n", - " 0.005\n", - " 0.905\n", - " 0.024\n", + " 0.963\n", + " 0.006\n", + " 0.913\n", + " 0.026\n", " \n", " \n", " 2\n", " 16\n", - " [f8, f16, f20, f2_missing, f1_categorical, f11...\n", - " [f2_missing, f7, f13]\n", - " 0.947\n", - " 0.005\n", - " 0.905\n", - " 0.024\n", + " [f18, f2_missing, f13, f9, f7, f15, f1_categor...\n", + " [f2_missing, f13, f7]\n", + " 0.963\n", + " 0.007\n", + " 0.914\n", + " 0.023\n", " \n", " \n", " 3\n", " 13\n", - " [f8, f16, f20, f5, f11, f1_categorical, f9, f1...\n", + " [f18, f9, f15, f1_categorical, f20, f10, f19, ...\n", " [f18, f12]\n", " 0.964\n", " 0.006\n", - " 0.913\n", - " 0.027\n", + " 0.914\n", + " 0.023\n", " \n", " \n", " 4\n", " 11\n", - " [f8, f16, f20, f9, f15, f14, f1_categorical, f...\n", - " [f10, f11]\n", - " 0.951\n", - " 0.009\n", - " 0.905\n", - " 0.037\n", + " [f20, f10, f19, f11, f16, f8, f5, f9, f14, f15...\n", + " [f11, f10]\n", + " 0.961\n", + " 0.007\n", + " 0.914\n", + " 0.027\n", " \n", " \n", " 5\n", " 9\n", - " [f8, f16, f20, f9, f15, f14, f5, f19, f1_categ...\n", + " [f20, f19, f16, f8, f5, f9, f14, f15, f1_categ...\n", " [f20]\n", " 0.959\n", " 0.008\n", @@ -479,23 +479,23 @@ "text/plain": [ " num_features features_set \\\n", "1 19 [f1_categorical, f2_missing, f4, f5, f6, f7, f... \n", - "2 16 [f8, f16, f20, f2_missing, f1_categorical, f11... \n", - "3 13 [f8, f16, f20, f5, f11, f1_categorical, f9, f1... \n", - "4 11 [f8, f16, f20, f9, f15, f14, f1_categorical, f... \n", - "5 9 [f8, f16, f20, f9, f15, f14, f5, f19, f1_categ... \n", + "2 16 [f18, f2_missing, f13, f9, f7, f15, f1_categor... \n", + "3 13 [f18, f9, f15, f1_categorical, f20, f10, f19, ... \n", + "4 11 [f20, f10, f19, f11, f16, f8, f5, f9, f14, f15... \n", + "5 9 [f20, f19, f16, f8, f5, f9, f14, f15, f1_categ... \n", "\n", " eliminated_features train_metric_mean train_metric_std \\\n", - "1 [f6, f17, f4] 0.947 0.005 \n", - "2 [f2_missing, f7, f13] 0.947 0.005 \n", + "1 [f6, f17, f4] 0.963 0.006 \n", + "2 [f2_missing, f13, f7] 0.963 0.007 \n", "3 [f18, f12] 0.964 0.006 \n", - "4 [f10, f11] 0.951 0.009 \n", + "4 [f11, f10] 0.961 0.007 \n", "5 [f20] 0.959 0.008 \n", "\n", " val_metric_mean val_metric_std \n", - "1 0.905 0.024 \n", - "2 0.905 0.024 \n", - "3 0.913 0.027 \n", - "4 0.905 0.037 \n", + "1 0.913 0.026 \n", + "2 0.914 0.023 \n", + "3 0.914 0.023 \n", + "4 0.914 0.027 \n", "5 0.914 0.031 " ] }, @@ -524,7 +524,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -554,7 +554,7 @@ { "data": { "text/plain": [ - "['f8', 'f16', 'f20', 'f9', 'f15', 'f14', 'f5', 'f19', 'f1_categorical']" + "['f20', 'f19', 'f16', 'f8', 'f5', 'f9', 'f14', 'f15', 'f1_categorical']" ] }, "execution_count": 8, diff --git a/probatus/feature_elimination/feature_elimination.py b/probatus/feature_elimination/feature_elimination.py index c3d1658a..38277da3 100644 --- a/probatus/feature_elimination/feature_elimination.py +++ b/probatus/feature_elimination/feature_elimination.py @@ -82,7 +82,8 @@ class ShapRFECV: """ - def __init__(self, clf, step=1, min_features_to_select=1, cv=None, scoring=None, n_jobs=-1, random_state=None): + def __init__(self, clf, step=1, min_features_to_select=1, cv=None, scoring=None, n_jobs=-1, verbose=0, + random_state=None): """ This method initializes the class: @@ -118,6 +119,14 @@ def __init__(self, clf, step=1, min_features_to_select=1, cv=None, scoring=None, Number of cores to run in parallel while fitting across folds. None means 1 unless in a `joblib.parallel_backend` context. -1 means using all processors. + verbose (Optional, int): + Controls verbosity of the output: + + - 0 - nether prints nor warnings are shown + - 1 - 50 - only most important warnings regarding data properties are shown (excluding SHAP warnings) + - 51 - 100 - shows most important warnings, prints of the feature removal process + - above 100 - presents all prints and all warnings (including SHAP warnings). + random_state (Optional, int): Random state set at each round of feature elimination. If it is None, the results will not be reproducible and in random search at each iteration a different hyperparameters might be tested. For @@ -148,6 +157,7 @@ def __init__(self, clf, step=1, min_features_to_select=1, cv=None, scoring=None, self.random_state = random_state self.n_jobs = n_jobs self.report_df = pd.DataFrame([]) + self.verbose = verbose self.fitted = False @@ -160,7 +170,7 @@ def _check_if_fitted(self): @staticmethod - def _preprocess_data(X): + def _preprocess_data(X, verbose=0): """ Does basic preprocessing of the data: Removal of static features, Warns which features have missing variables, and transform object dtype features to category type, such that LightGBM handles them by default. @@ -169,6 +179,14 @@ def _preprocess_data(X): X (pd.DataFrame): Provided dataset. + verbose (Optional, int): + Controls verbosity of the output: + + - 0 - neither prints nor warnings are shown + - 1 - 50 - only most important warnings regarding data properties are shown (excluding SHAP warnings) + - 51 - 100 - shows most important warnings, prints of the feature removal process + - above 100 - presents all prints and all warnings (including SHAP warnings). + Returns: (pd.DataFrame): Preprocessed dataset. @@ -179,14 +197,16 @@ def _preprocess_data(X): # Remove static features, those that have only one value for all samples static_features = [i for i in X.columns if len(X[i].unique()) == 1] if len(static_features)>0: - warnings.warn(f'Removing static features {static_features}.') + if verbose > 0: + warnings.warn(f'Removing static features {static_features}.') X = X.drop(columns=static_features) # Warn if missing columns_with_missing = [column for column in X.columns if X[column].isnull().values.any()] if len(columns_with_missing) > 0: - warnings.warn(f'The following variables contain missing values {columns_with_missing}. Make sure to impute' - f'missing or apply a model that handles them automatically.') + if verbose > 0: + warnings.warn(f'The following variables contain missing values {columns_with_missing}. Make sure to ' + f'impute missing or apply a model that handles them automatically.') # Transform Categorical variables into category dtype indices_obj_dtype_features = [column[0] for column in enumerate(X.dtypes) if column[1] == 'O'] @@ -194,9 +214,10 @@ def _preprocess_data(X): # Set categorical features type to category if len(obj_dtype_features) > 0: - warnings.warn(f'Changing dtype of {obj_dtype_features} from "object" to "category". Treating it as ' - f'categorical variable. Make sure that the model handles categorical variables, or encode ' - f'them first.') + if verbose > 0: + warnings.warn(f'Changing dtype of {obj_dtype_features} from "object" to "category". Treating it as ' + f'categorical variable. Make sure that the model handles categorical variables, or encode' + f' them first.') for obj_dtype_feature in obj_dtype_features: X[obj_dtype_feature] = X[obj_dtype_feature].astype('category') return X @@ -321,7 +342,7 @@ def _report_current_results(self, round_number, current_features_set, features_t @staticmethod - def _get_feature_shap_values_per_fold(X, y, clf, train_index, val_index, scorer): + def _get_feature_shap_values_per_fold(X, y, clf, train_index, val_index, scorer, verbose=0): """ This function calculates the shap values on validation set, and Train and Val score. @@ -345,6 +366,14 @@ def _get_feature_shap_values_per_fold(X, y, clf, train_index, val_index, scorer) A string (see sklearn [model scoring](https://scikit-learn.org/stable/modules/model_evaluation.html)) or a scorer callable object, function with the signature `scorer(estimator, X, y)`. + verbose (Optional, int): + Controls verbosity of the output: + + - 0 - neither prints nor warnings are shown + - 1 - 50 - only most important warnings regarding data properties are shown (excluding SHAP warnings) + - 51 - 100 - shows most important warnings, prints of the feature removal process + - above 100 - presents all prints and all warnings (including SHAP warnings). + Returns: (np.array, float, float): Tuple with the results: Shap Values on validation fold, train score, validation score. @@ -359,8 +388,13 @@ def _get_feature_shap_values_per_fold(X, y, clf, train_index, val_index, scorer) score_train = scorer(clf, X_train, y_train) score_val = scorer(clf, X_val, y_val) + if verbose > 100: + suppress_warnings = False + else: + suppress_warnings = True + # Compute SHAP values - shap_values = shap_calc(clf, X_val, suppress_warnings=True) + shap_values = shap_calc(clf, X_val, suppress_warnings=suppress_warnings) return shap_values, score_train, score_val @@ -383,7 +417,7 @@ def fit(self, X, y): if self.random_state is not None: np.random.seed(self.random_state) - self.X = self._preprocess_data(X) + self.X = self._preprocess_data(X, verbose=self.verbose) self.y = assure_pandas_series(y, index=self.X.index) self.cv = check_cv(self.cv, self.y, classifier=is_classifier(self.clf)) @@ -410,7 +444,8 @@ def fit(self, X, y): # Perform CV to estimate feature importance with SHAP results_per_fold = Parallel(n_jobs=self.n_jobs)(delayed(self._get_feature_shap_values_per_fold)( - X=current_X, y=self.y, clf=current_clf, train_index=train_index, val_index=val_index, scorer=self.scorer + X=current_X, y=self.y, clf=current_clf, train_index=train_index, val_index=val_index, + scorer=self.scorer, verbose=self.verbose ) for train_index, val_index in self.cv.split(current_X, self.y)) shap_values = np.vstack([current_result[0] for current_result in results_per_fold]) @@ -430,14 +465,14 @@ def fit(self, X, y): train_metric_std = np.round(np.std(scores_train), 3), val_metric_mean = np.round(np.mean(scores_val), 3), val_metric_std = np.round(np.std(scores_val), 3)) - - print(f'Round: {round_number}, Current number of features: {len(current_features_set)}, ' - f'Current performance: Train {self.report_df.loc[round_number]["train_metric_mean"]} ' - f'+/- {self.report_df.loc[round_number]["train_metric_std"]}, CV Validation ' - f'{self.report_df.loc[round_number]["val_metric_mean"]} ' - f'+/- {self.report_df.loc[round_number]["val_metric_std"]}. \n' - f'Num of features left: {len(remaining_features)}. ' - f'Removed features at the end of the round: {features_to_remove}') + if self.verbose > 50: + print(f'Round: {round_number}, Current number of features: {len(current_features_set)}, ' + f'Current performance: Train {self.report_df.loc[round_number]["train_metric_mean"]} ' + f'+/- {self.report_df.loc[round_number]["train_metric_std"]}, CV Validation ' + f'{self.report_df.loc[round_number]["val_metric_mean"]} ' + f'+/- {self.report_df.loc[round_number]["val_metric_std"]}. \n' + f'Num of features left: {len(remaining_features)}. ' + f'Removed features at the end of the round: {features_to_remove}') self.fitted = True diff --git a/probatus/interpret/model_interpret.py b/probatus/interpret/model_interpret.py index 53546598..39e529d3 100644 --- a/probatus/interpret/model_interpret.py +++ b/probatus/interpret/model_interpret.py @@ -225,7 +225,6 @@ def plot(self, plot_type, target_columns=None, samples_index=None, **plot_kwargs elif plot_type == 'dependence': ax = [] for feature_name in target_columns: - print() ax.append( self.tdp.plot(feature=feature_name, figsize=(10, 7), target_names=self.class_names)) plt.show() diff --git a/tests/feature_elimination/test_feature_elimination.py b/tests/feature_elimination/test_feature_elimination.py index 0a23bad2..01a3f9db 100644 --- a/tests/feature_elimination/test_feature_elimination.py +++ b/tests/feature_elimination/test_feature_elimination.py @@ -27,7 +27,7 @@ def test_shap_rfe_randomized_search(X, y): } search = RandomizedSearchCV(clf, param_grid, cv=2) - shap_elimination = ShapRFECV(search, step=0.8, cv=2, scoring='roc_auc', n_jobs=4) + shap_elimination = ShapRFECV(search, step=0.8, cv=2, scoring='roc_auc', n_jobs=4, verbose=150) report = shap_elimination.fit_compute(X, y) From a1c168b2164b51debb80e3fa5d36d0ba01ebcddb Mon Sep 17 00:00:00 2001 From: mgarbacz Date: Tue, 24 Nov 2020 10:03:12 +0100 Subject: [PATCH 2/2] Improve unit tests to check prints and warnings --- .../test_feature_elimination.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tests/feature_elimination/test_feature_elimination.py b/tests/feature_elimination/test_feature_elimination.py index 01a3f9db..9bffab39 100644 --- a/tests/feature_elimination/test_feature_elimination.py +++ b/tests/feature_elimination/test_feature_elimination.py @@ -18,7 +18,7 @@ def y(): return pd.Series([1, 0, 1, 0, 1, 0, 1, 0], index=[1, 2, 3, 4, 5, 6, 7, 8]) -def test_shap_rfe_randomized_search(X, y): +def test_shap_rfe_randomized_search(X, y, capsys): clf = DecisionTreeClassifier(max_depth=1) param_grid = { @@ -26,10 +26,10 @@ def test_shap_rfe_randomized_search(X, y): 'min_samples_split': [1, 2] } search = RandomizedSearchCV(clf, param_grid, cv=2) + with pytest.warns(None) as record: - shap_elimination = ShapRFECV(search, step=0.8, cv=2, scoring='roc_auc', n_jobs=4, verbose=150) - - report = shap_elimination.fit_compute(X, y) + shap_elimination = ShapRFECV(search, step=0.8, cv=2, scoring='roc_auc', n_jobs=4, verbose=150) + report = shap_elimination.fit_compute(X, y) assert shap_elimination.fitted == True shap_elimination._check_if_fitted @@ -39,14 +39,19 @@ def test_shap_rfe_randomized_search(X, y): ax1 = shap_elimination.plot(show=False) + # Ensure that number of warnings was at least 2 for the verbose (2 generated by probatus + possibly more by SHAP) + assert len(record) >= 2 -def test_shap_rfe(X, y): - - clf = DecisionTreeClassifier(max_depth=1) + # Check if there is any prints + out, _ = capsys.readouterr() + assert len(out) > 0 - shap_elimination = ShapRFECV(clf, random_state=1, step=1, cv=2, scoring='roc_auc', n_jobs=4) +def test_shap_rfe(X, y, capsys): - shap_elimination.fit(X, y) + clf = DecisionTreeClassifier(max_depth=1) + with pytest.warns(None) as record: + shap_elimination = ShapRFECV(clf, random_state=1, step=1, cv=2, scoring='roc_auc', n_jobs=4) + shap_elimination.fit(X, y) assert shap_elimination.fitted == True shap_elimination._check_if_fitted @@ -58,6 +63,11 @@ def test_shap_rfe(X, y): ax1 = shap_elimination.plot(show=False) + # Ensure that number of warnings was 0 + assert len(record) == 0 + # Check if there is any prints + out, _ = capsys.readouterr() + assert len(out) == 0 def test_calculate_number_of_features_to_remove(): assert 3 == ShapRFECV._calculate_number_of_features_to_remove(current_num_of_features=10,