Week 9 · Decision trees and ensembles
Read the words of the video
- Imagine playing twenty questions to guess an object. A decision tree works similarly, asking simple yes or no questions one by one. For example, 'Is the petal shorter than two point four five centimetres?' or 'Is it wider than one point seven five centimetres?' It keeps asking until it knows the answer.
- A single decision tree is easy to understand, but it can be a bit 'jumpy'. Small changes in our data might lead to a completely different tree. It also tends to 'memorise' the training information too well. To fix this, we create a 'committee of trees', combining many trees through voting or averaging their predictions.
- A clear glass cube holds ten shimmering spheres, five red and five blue. A glowing divider slides in, splitting the cube into two sections. The spheres instantly reconfigure: one section has four red and one blue, the other has one red and four blue. An overhead camera with subtle zoom reveals the much cleaner separation. Bright lab lighting.
- For your exams, remember that increasing 'minimum samples per leaf' reduces overfitting. Ensembles like voting or random forests are powerful, especially 'soft voting' with probabilities. Also, 'feature importances' tell us which factors truly matter. You've learned to build and tame trees, and combine them into smart committees. Great job!
The one-minute story
A decision tree is the game of twenty questions turned into code: "Is the petal shorter than 2.45 cm? ... Is the petal wider than 1.75 cm?..." scikit-learn builds the questions automatically, always choosing the question that leaves the cleanest groups.
One tree is easy to read but jumpy: change a few training flowers and you may get a different tree, and left alone it memorises the training set. The cure is a committee of trees:
- ask many different classifiers and let them vote (voting),
- or train many trees on different random samples and average (bagging and the famous random forest).
This week: grow and tame a tree, then build the committees.
By the end of this week you can:
- train and read a
DecisionTreeClassifier, and control overfitting with its hyperparameters; - explain the CART cost and Gini vs entropy;
- use
VotingClassifier(hard and soft),BaggingClassifier(with out-of-bag score), andRandomForestClassifier; - read
feature_importances_.
Words you need
- Node / leaf - a question box / a final answer box.
- Gini impurity - ; 0 for a pure node.
- CART - the greedy algorithm scikit-learn uses to build binary trees.
- Ensemble - several models combined.
- Hard / soft voting - majority of the votes / average of the predicted probabilities.
- Bagging / pasting - training on samples drawn with / without replacement.
- Out-of-bag (OOB) - the training examples a given tree did not see.
- Feature importance - how much each feature reduced impurity, summed over the tree.
Idea 1: A decision tree in scikit-learn
We use the iris flowers, keeping petal length and width only, with a depth limit of 2:
from sklearn.tree import DecisionTreeClassifier, export_text
tree = DecisionTreeClassifier(max_depth=2, random_state=42).fit(X_petal, y)
print(export_text(tree, feature_names=["petal_len", "petal_wid"]))
|--- petal_len <= 2.45
| |--- class: 0
|--- petal_len > 2.45
| |--- petal_wid <= 1.75
| | |--- class: 1
| |--- petal_wid > 1.75
| | |--- class: 2
Three leaves, depth 2. The first question already separates class 0 (setosa) perfectly. The root's Gini is , the largest possible for 3 equal classes.
predict(X)gives the leaf's majority class.predict_proba(X)gives the class fractions in the leaf: for a flower with petal the answer was[0, 0.907, 0.093], because the leaf it lands in holds 49 class-1 and 5 class-2 training flowers ().feature_importances_(non-negative, sum to 1): how much each feature contributed to reducing impurity.get_depth(),get_n_leaves().
Trees need no feature scaling (each question compares one feature to a threshold) and handle numeric and (encoded) categorical features.
Idea 2: How the tree is grown (CART)
At each node, CART tries every feature and every threshold and picks the pair that minimises the cost
(the size-weighted impurity of the two children). It splits, then recurses on each child, greedily: it never looks ahead or goes back, so the result is good, not provably optimal.
criterion="gini"is the default;"entropy"is the alternative. They give almost identical trees; Gini is slightly faster, entropy slightly more balanced.- Growth stops when a node is pure, or when a stopping rule (a hyperparameter) applies.
Question. A node holds 10 examples (5 class A, 5 class B). A split gives left and right . Compute the Gini cost. Compare with the unsplit node.
Solution. and . Cost . The parent's impurity is , so the split lowers it by . CART picks the split with the smallest cost.
Idea 3: Taming a tree
An unrestricted tree keeps splitting until every leaf is pure, so it fits the training set perfectly (on the iris petal data it grew to depth 5 with 8 leaves, 99.3% training accuracy). That memorising is overfitting: on the "two moons" data (500 noisy points, 70/30 split):
| Tree | Train acc. | Test acc. |
|---|---|---|
| default (unrestricted) | 1.000 | 0.853 |
min_samples_leaf=5 | 0.949 | 0.873 |
Perfect on training, worse on test. The regularising hyperparameters: each one limits the tree, so increasing a min_* value or decreasing a max_* value regularises:
| Hyperparameter | Default | Effect |
|---|---|---|
max_depth | None | stop at this depth ( = simpler) |
min_samples_split | 2 | a node needs at least this many examples to be split ( = simpler) |
min_samples_leaf | 1 | each leaf must hold at least this many ( = simpler) |
max_leaf_nodes | None | cap on the number of leaves |
max_features | None | features considered at each split |
criterion | 'gini' | 'gini' or 'entropy' |
DecisionTreeRegressor works the same way but chooses splits that minimise the mean squared error, and each leaf predicts the mean of its training targets (so predictions are a staircase).
Weaknesses. Trees make axis-aligned splits (a diagonal boundary needs many steps), and they are unstable: tiny changes in the data, or rotating the data, can produce a very different tree. That instability is exactly what ensembles average away.
Idea 4: Voting classifiers
Train several different classifiers on the same data and combine them:
from sklearn.ensemble import VotingClassifier
vote = VotingClassifier([("lr", LogisticRegression()),
("rf", RandomForestClassifier(random_state=42)),
("svc", SVC(random_state=42))], voting="hard")
- Hard voting: each model casts one vote for a class; the majority wins.
- Soft voting: average the predicted probabilities and pick the highest. Needs every model to have
predict_proba(soSVC(probability=True)). It usually does better, because very confident models speak louder.
On the moons data: LogisticRegression 0.860, random forest 0.907, SVC 0.907, hard voting 0.913, soft voting 0.933: the committee beats each member. It works best when members are diverse and make different mistakes.
Idea 5: Bagging, pasting and out-of-bag evaluation
Use the same algorithm many times, each on a different random subset of the training data:
from sklearn.ensemble import BaggingClassifier
bag = BaggingClassifier(DecisionTreeClassifier(), n_estimators=300,
max_samples=100, bootstrap=True,
oob_score=True, random_state=42)
bag.fit(X_train, y_train)
bag.oob_score_
bootstrap=True(the default) samples with replacement: that is bagging.bootstrap=Falsesamples without replacement: pasting.n_estimatorsdefault is 10;max_samplesis how many examples each model sees.- Any classifier with
fit/predictcan be the base estimator (trees, k-NN, logistic regression, SVC...); if none is given a decision tree is used. - Each ensemble prediction is a majority vote (or average of probabilities if available).
- Out-of-bag evaluation: each model never saw about 37% of the training examples;
oob_score=Truetests every example only with the models that did not train on it, giving a free validation estimate inoob_score_. On moons: OOB 0.926, close to the real test accuracy (0.92): a trustworthy estimate with no hold-out set. - Compared with a single tree, bagging has about the same bias but lower variance. (Pasting: test accuracy 0.913 here.)
- Trees can be trained in parallel:
n_jobs=-1.
Idea 6: Random forests
A random forest is bagged decision trees plus random feature selection at every split, so the trees differ more:
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_estimators=300, random_state=42, oob_score=True).fit(X_train, y_train)
n_estimators=100by default;max_features="sqrt"by default (the square root of the number of features are considered at each split);bootstrap=True; plus all the tree hyperparameters. Moons: test accuracy 0.907, OOB 0.894.- Extra-Trees (
ExtraTreesClassifier) go further: random thresholds too, andbootstrap=Falseby default; faster, more bias, less variance. - Feature importances.
rf.feature_importances_averages each feature's impurity reduction over all trees and sums to 1. On the breast-cancer data the top three wereworst perimeter(0.143),worst area(0.128) andworst concave points(0.119). A great tool for finding which features matter.
Solved examples
Question. For a decision tree, which changes reduce overfitting? (a) larger max_depth (b) larger min_samples_leaf (c) smaller max_leaf_nodes (d) smaller min_samples_split.
Solution. Restrictions regularise. (b) and (c) reduce overfitting. (a) lets the tree grow deeper and (d) lets nodes split more easily: both increase overfitting.
Question. Three classifiers give class-1 probabilities for one example. What do hard and soft voting predict?
Solution. Hard: votes for class 1 are (0.9 is above 0.5; 0.4 and 0.45 are below), so class 0 wins 2 to 1. Soft: mean probability , so class 1. The confident first model carries the soft vote.
oob_score_Question. A bagging classifier has oob_score_=0.91 and test accuracy 0.90. What does it tell you?
Solution. The OOB estimate and the test accuracy agree, so the OOB score is a reliable stand-in for a validation set, and the ensemble should generalise at about 90 to 91%.
Your turn
A node has 30 examples: 20 class A, 10 class B. Compute its Gini impurity.
Need a hint?
.
Show the full solution
, . .
A leaf holds 12 training examples: 9 of class 0 and 3 of class 1. What does predict_proba give for a test point landing there?
Need a hint?
Class fractions.
Show the full solution
.
Which is the correct way to train pasting with 50 trees, each on 80 examples, from BaggingClassifier?
Need a hint?
Pasting means sampling without replacement.
Show the full solution
BaggingClassifier(DecisionTreeClassifier(), n_estimators=50, max_samples=80, bootstrap=False).
A random forest has n_estimators=200. How many DecisionTreeClassifiers does it contain, and does each see all the training rows?
Need a hint?
Think of bootstrap sampling.
Show the full solution
200 trees. Each is trained on a bootstrap sample (the same size, drawn with replacement, so about 63% distinct rows), not on all rows exactly once.
(a) Trees need scaled features. (b) feature_importances_ sums to 1. (c) Soft voting requires predict_proba. (d) A deeper tree always generalises better.
Need a hint?
Check each lesson point.
Show the full solution
(a) False. (b) True. (c) True. (d) False: deeper trees overfit.
Exam radar
MLP Quiz 2 and the End Term: "Suppose you want to use BaggingClassifier; which estimators can be the base estimator?" (any classifier), "advantages of ensemble methods", "which options give the largest regularisation (lowest overfitting)" for trees and forests, "predict the class for this test point from the code given", oob_score, hard vs soft voting, RandomForest hyperparameter grid counts ("how many unique combinations of hyperparameters are in this grid?"), and feature_importances_.
From MLT (the theory partner): decision trees in 24 End Term papers, entropy in 19, random forests in 12, bagging in 14.
- Soft voting needs
predict_proba;SVCneedsprobability=True. BaggingClassifierhasbootstrap=True(bagging);bootstrap=Falseis pasting.- Regularise a tree by raising
min_samples_*or loweringmax_depth/max_leaf_nodes. - Random forest bagged trees only: it also randomises the features per split.
- Trees do not need scaling; k-NN, SVM and linear models do.
- The default
BaggingClassifierhas 10 estimators; the default forest has 100.
Quick quiz
Which change makes a decision tree LESS likely to overfit?
- increase max_depth
- increase min_samples_leaf
- decrease min_samples_leaf
- grow it until every leaf is pure
What does soft voting average?
- the predicted classes
- the predicted probabilities
- the feature importances
- the training errors
In BaggingClassifier, bootstrap=False means...
- no randomness at all
- the samples are drawn without replacement (pasting)
- the trees share the same data exactly
- out-of-bag scoring is faster
Select all TRUE statements about random forests. (select all that apply)
- Each split considers only a random subset of features.
- feature_importances_ sums to 1.
- They require feature scaling.
- They always have exactly 10 trees.
Recap
- CART: greedily minimise (Gini default, entropy option).
predict_proba= leaf class fractions. Trees: no scaling, axis-aligned, unstable. - Unrestricted tree overfits. Regularise with
max_depth,min_samples_leaf,min_samples_split,max_leaf_nodes. - Voting: hard (majority) or soft (mean probabilities, usually better). Bagging (
bootstrap=True) vs pasting (False); OOB score validation. - Random forest = bagged trees + random features per split (
max_features="sqrt");feature_importances_sums to 1.
- Gini ; entropy ; cost
- Defaults: tree
criterion="gini",max_depth=None,min_samples_leaf=1;BaggingClassifier(n_estimators=10, bootstrap=True);RandomForestClassifier(n_estimators=100, max_features="sqrt") - OOB fraction per tree;
oob_score=Trueoob_score_
- MLT week 7 - k-NN and decision trees: Decision trees: the theory, then the code.
Practise with real exam questions
These come straight from past papers (with the real figures and the official answer key). Open 53 real questions on this week All End Term papers