Why remove features at all 7 min live
Adding a column feels free. It is not, and Google's Rules of ML say why in two sentences. Rule 21: "The number of feature weights you can learn in a linear model is roughly proportional to the amount of data you have." Rule 22: "Clean up features you are no longer using." Your data budget is finite, and every weight you spend on a column that carries nothing is a weight you did not spend on one that does.
LiveThe honest number, before anyone oversells this4 min▶
Look back at the ladder you climbed in b1. Selection is the last rung, and it is the smallest one on the board:
- + Interactions: held-out AUC 0.812
- + Selection: held-out AUC 0.813
One thousandth. That is the accuracy case for feature selection on this dataset, and stating it plainly is the point - most courses quietly imply selection is a performance technique. It usually is not. What it actually buys is worth more than a decimal place:
- Simplicity. Fewer columns to explain, review, and defend when someone asks why the model did that.
- Stability. Fewer weights spread across noise means less variance between retrains, and less sensitivity to a shift in a column nobody is watching.
- Serving cost. Every feature in the model is a feature that must be computed, joined, monitored and kept alive at prediction time. A column you dropped is a pipeline you never have to fix at 2am.
Frame it that way with your team and the conversation improves immediately: you stop arguing about a thousandth of AUC and start arguing about how many things you want to keep running.
Self-studyFeature debt is real debt3 min read▶
Rule 22 is a maintenance rule, not a modelling one. An unused feature still has an owner, an upstream job, a schema, a backfill, and an on-call rotation attached to it. It still breaks. It still shows up in the incident channel. And because nobody remembers whether the model uses it, nobody dares delete it.
The cheapest moment to remove a feature is the moment you can show it earns nothing. That is what the rest of this session is about: producing evidence solid enough that the deletion happens.
The methods, and what each one costs 8 min live
Five families in sklearn, in rough order of cost. Read the last column hardest - each method has a specific way of misleading you, and none of them is "it removes the wrong feature at random".
| Method | How it decides | Cost | Watch out |
|---|---|---|---|
VarianceThreshold |
Drops features that barely vary at all | Trivial - no model, no target | Ignores the target entirely. For Bernoulli features, to remove those that are one value in more than 80% of samples, set threshold = .8 * (1 - .8) = 0.16 |
SelectKBest / SelectPercentile |
Scores each feature against the target with chi2, f_classif or mutual_info_classif |
Cheap - one pass per feature | Univariate, so it cannot see combinations. And sklearn's own warning: "Beware not to use a regression scoring function with a classification problem, you will get useless results" |
RFE / RFECV |
Fits the model, drops the weakest features, repeats. RFECV cross-validates the number to keep |
Expensive - many refits | Needs a model that exposes coef_ or feature_importances_, and inherits that model's biases |
SelectFromModel |
Keeps features whose learned coefficient or importance clears a threshold | One fit | With L1, a smaller C or a larger alpha means fewer features kept. With trees, you inherit the impurity bias in Part 3 |
SequentialFeatureSelector |
Adds (or removes) one feature at a time, keeping whatever most improves cross-validated score | The most expensive of the five | sklearn: "SFS differs from RFE and SelectFromModel in that it does not require the underlying model to expose a coef_ or feature_importances_ attribute. It may however be slower" |
LiveMutual information, and why it beats correlation5 min▶
Kaggle Learn's definition is the one to carry around: mutual information is "a measure of the extent to which knowledge of one quantity reduces uncertainty about the other." It is the first thing to reach for on a new dataset, because it answers "which columns are worth my attention this afternoon" without fitting anything.
It beats correlation for a specific reason, stated plainly in the same lesson: "correlation only detects linear relationships." A U-shaped relationship, a threshold effect, a category that matters only at the extremes - correlation reports roughly zero for all of them. Mutual information does not.
The scale: minimum 0.0, meaning the two quantities are independent, and no fixed maximum - though "values above 2.0 or so are uncommon", so treat a score above 2 as a reason to check for leakage rather than a reason to celebrate.
The first caveat is the one that bites in this course specifically. In b6 you built interactions - relationships that no single column carries. A univariate metric is structurally blind to exactly those. If you had used mutual information to prune before building interactions, you would have deleted the ingredients.
Self-studyPicking a scoring function without wasting an afternoon3 min read▶
- Classification:
chi2(non-negative features only),f_classif(linear dependency),mutual_info_classif(any dependency, slower). - Regression:
f_regression,mutual_info_regression. - The mistake sklearn bothers to warn about: crossing the two lists. "Beware not to use a regression scoring function with a classification problem, you will get useless results." Nothing raises an error - you simply get a ranking that means nothing, which is worse.
Importance that lies, and importance that does not ★ 12 min · everyone runs it
Fit a tree, print feature_importances_, sort descending, screenshot it for the deck. It is the single most common feature-analysis move in applied machine learning, and sklearn's own documentation describes it as "strongly biased". Not approximate. Not noisy. Biased, in a direction you can predict.
LiveWhat impurity importance is biased towards5 min▶
sklearn's warning, verbatim, is worth reading in full because every clause does work:
Impurity-based importances "favor high cardinality features (typically numerical features) over low cardinality features such as binary features or categorical variables with a small number of possible categories", and "can give high importance to features that may not be predictive on unseen data when the model is overfitting."
The mechanism is simple once you see it. A column with thousands of distinct values offers thousands of possible split points, so it will almost always find some split that reduces impurity on the training rows. It looks powerful because it is flexible, not because it is predictive. Meanwhile a genuinely useful binary flag gets one split point and one shot, and finishes lower down the list.
That is why a customer id, a raw timestamp, or a high-cardinality postal code so often floats to the top of an importance chart that then gets pasted into a slide.
The alternative, in sklearn's words: permutation importance "avoids this issue, since it can be computed on unseen data." Shuffle one column, re-score, and measure how much the score falls. If the model was genuinely relying on the column, breaking it hurts. If the model was memorising, breaking it changes nothing on rows it never saw.
LiveCompute it on the held-out set, and read both4 min▶
Permutation importance can be computed on training data or on held-out data, and comparing the two is a diagnostic in its own right. sklearn states the rule verbatim: "Features that are important on the training set but not on the held-out set might cause the model to overfit."
from sklearn.inspection import permutation_importance
r = permutation_importance(
pipe, X_test, y_test, # held-out rows, never the training rows
n_repeats=30, random_state=42, scoring="roc_auc")
for i in r.importances_mean.argsort()[::-1]:
if r.importances_mean[i] - 2 * r.importances_std[i] > 0:
print(f"{feature_names[i]:<24} "
f"{r.importances_mean[i]:.3f} +/- {r.importances_std[i]:.3f}")
n_repeats is what turns a single shuffle into an estimate with a spread. Without it you cannot tell a real drop from a lucky permutation, and the filter above - keep only features whose mean importance clears two standard deviations - is the cheapest way to stop yourself reading noise as a ranking.
LiveThe correlated-feature trap3 min▶
Permutation importance has one failure mode you will meet on real tabular data, and it is easy to walk into. If two features are strongly correlated, permuting one leaves the model its correlate to fall back on. The score barely moves. Do the same to the other, and the score barely moves again. Both report low importance, and both are actually load-bearing.
Drop them both on that evidence and the score collapses, which is a confusing afternoon.
The fix is mechanical: cluster the correlated features first - a hierarchical clustering on the correlation matrix does it - and keep one representative per cluster before you interpret any importance number. You are then measuring the cluster's contribution, which is the thing you actually wanted to know.
Retiring session_hour, on evidence 6 min live
Lumen's session_hour has been sitting in this case since the intro-ML course, where it was seeded as deliberate near-useless noise. It has been suspected ever since. Tonight it gets retired properly - not because someone had a hunch, but because permutation importance on held-out data puts it at zero and stands behind the number.
The suspicion. The intro-ML course exposed session_hour as noise, but exposure is not evidence anyone can act on. "We think this column does nothing" does not survive contact with the person who built the pipeline that produces it.
The evidence. Permutation importance on the held-out set: shuffling session_hour does not move the score, because the model was never relying on it for rows it had not seen. That is a measurable, reproducible, defensible zero.
The decision. Drop it. Held-out AUC goes 0.812 to 0.813 - a rounding error in accuracy terms, and a real win in every other term: one fewer column to compute, join, monitor and break.
Where the selection step lives. Inside the Pipeline, always. sklearn is explicit: "Feature selection is usually used as a pre-processing step before doing the actual learning. The recommended way to do this in scikit-learn is to use a Pipeline."
Selection outside cross-validation is the leakage you already met. In b5 you saw sklearn's own worked example: feature selection fitted on all the data before splitting scored 0.76 accuracy on completely random noise, where the honest pipeline scored 0.50 and 0.43 +/- 0.05 under cross-validation. Selection is a fit. It learns which columns to keep from the target, and if it learns that from rows that end up in your test set, your test set is no longer a test. This is the single most common way a competent team ships a model that was never as good as the notebook said.
Self-studyAn order of operations that holds up4 min read▶
- Split first, before anything else touches the data.
- Explore with mutual information on the training rows only - to decide where to spend your afternoon, not to prune.
- Build features, including the interactions from b6, before any univariate pruning. Univariate metrics cannot see combinations, so pruning first deletes the ingredients.
- Put the selector inside the Pipeline, so it re-fits within every cross-validation fold.
- Judge with permutation importance on held-out data, with
n_repeats, after clustering correlated features. - Delete the column for real - Rule 22 - and delete the pipeline that produced it too, or you have kept the cost and lost the benefit.
Watch selection earn its thousandth ★ 8 min · everyone runs it
The workbench opens with the four honest feature groups you built in b1 to b6 already on: numerics, encoding, datetime and interactions. Read the held-out score, then switch on Selection and read it again. This is the smallest move in the whole course, and the one that most needs explaining honestly.
Numerics, encoding, datetime and interactions on. Press Score on held-out: 0.812. This is the model you have earned so far.
Switch on Selection and score again: 0.813. One thousandth of AUC. If you were expecting the chart to jump, that expectation is exactly what this session exists to correct.
What the workbench's selection step actually did: it dropped session_hour, the deliberate noise column. The score barely moved because the column carried nothing - which is the confirmation, not the disappointment.
Now say why you would still ship it. One fewer column to compute at prediction time, one fewer upstream job to keep alive, one fewer thing to monitor, one fewer thing to break. Accuracy is not the case for selection. Operations is.
Try it yourself - this week ◐ 30-40 min total
- Run
permutation_importanceon a model you already have, on the held-out set, withn_repeats=30. Compare the ranking to thefeature_importances_you have been quoting. Note every column that moves more than two places. - Find the highest-cardinality feature in that model and check where it sits in each ranking. If it is high on impurity and near zero on permutation, you have just reproduced sklearn's documented bias on your own data.
- Compute mutual information on your training rows. Pick the top feature and the bottom feature, and write one sentence each on whether you believe the score - remembering that MI is univariate and cannot see the interactions you built.
- Check whether any selection step in your codebase is fitted outside the Pipeline. If so, move it inside and re-run cross-validation. Write down both numbers.
- Pick one feature you can defend deleting. Delete it, and delete the job that produces it. Rule 22 only pays if you finish it.
Official sources covered
This session works through sklearn's feature-selection chapter and its permutation-importance page in full, plus Kaggle Learn's mutual-information lesson and the two Rules of ML that make the case for removing features at all. Certificates and graded exercises stay with the official providers (Kaggle Learn, Google MLCC). This page covers:
Three questions before you go 🎯 ◐ 90 seconds
1 · Your tree's feature_importances_ puts a customer id column at the top. What does sklearn's documentation say is going on?
Thousands of distinct values means thousands of candidate split points, so the column will almost always find something that reduces impurity on the training rows. Permutation importance "avoids this issue, since it can be computed on unseen data" - and on unseen data the id is worth nothing.
2 · You rank features by mutual information and prune the bottom half before building anything. What have you risked?
MI is genuinely better than correlation, which "only detects linear relationships" - but it scores one column at a time. Two features that mean little alone and a great deal together both look droppable. Build your interactions before you prune.
3 · Why does the selection step have to sit inside the Pipeline?
Selection is a fit like any other. sklearn's recommendation is direct: "Feature selection is usually used as a pre-processing step before doing the actual learning. The recommended way to do this in scikit-learn is to use a Pipeline." Inside it, each fold selects from its own training rows.