Decision Trees and Ensembles
Learn a model made of yes-or-no questions by picking the best split again and again, then combine many trees into forests that overfit far less.
On this page
Before taking a drone out, an experienced pilot runs through a few questions in their head. Is it raining? If so, stay home. If not, how windy is it? Too windy, stay home. Otherwise, fly. Nobody computed a weighted sum. They followed a short chain of questions, and each answer decided which question came next.
A decision tree is a model that has exactly this shape, and it can be learned from examples. Trees are easy to read, need little data preparation, and, combined in large groups, they are some of the most accurate models for spreadsheet-style data.
A tree is a chain of questions
Each inner box of a tree asks a yes-or-no question about one feature. Each answer leads to another question or to a leaf, which holds the prediction. To predict, start at the top and follow the answers down.
Unlike the weighted sums in Regression and Classification, a tree can say "wind matters, but only on dry days". It handles that kind of interaction naturally, because the wind question only exists on one branch.
How a tree is learned
You already know the core move. In What Is Machine Learning we tried every cut-off for one feature and kept the one with the fewest mistakes. A tree does that for every feature, picks the single best question, splits the examples into two groups, and then repeats the whole search inside each group.
To score a question, let each group predict its most common answer and count how many examples that gets wrong. Here are 8 past days, sorted by wind, with whether flying went well.
for each feature f: for each value t of f: split days into f >= t and f < t cost = mistakes(group 1) + mistakes(group 2) keep the split with the lowest costrepeat inside each group- wind0518t210312415518622725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- cost
- 3
- best
- wind ≥ 8 (3)
1Question: wind ≥ 8? The 5 km/h day goes alone and is right. The other 7 days have 3 fly and 4 don't, so predicting "don't" gets 3 wrong. Cost 3.
- wind0518210t312415518622725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- cost
- 4
- best
- wind ≥ 8 (3)
2Wind ≥ 10: the low group is 1 yes and 1 no (1 wrong); the high group is 3 and 3 (3 wrong). Cost 4.
- wind0518210312t415518622725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- cost
- 3
- best
- wind ≥ 8 (3)
3Wind ≥ 12: the low group (5, 8, 10) is 2 yes and 1 no, so 1 wrong. The high group is 2 yes and 3 no, so 2 wrong. Cost 3, a tie, so the first best stays.
- wind0518210312415t518622725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- cost
- 4
- best
- wind ≥ 8 (3)
4Wind ≥ 15: 2 yes and 2 no on each side, so 2 wrong on each side. Cost 4.
- wind0518210312415518t622725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- cost
- 3
- best
- wind ≥ 8 (3)
5Wind ≥ 18: the low group is 3 yes and 2 no (2 wrong); the high group is 1 yes and 2 no (1 wrong). Cost 3, still no better.
- wind0518210312415518622t725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- cost
- 2
- best
- wind ≥ 22 (2)
6Wind ≥ 22: the two windiest days are both "don't", so that group is perfect. The other 6 have 4 yes and 2 no: 2 wrong. Cost 2, a new best.
- wind0518210312415518622725train0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- cost
- 3
- best
- wind ≥ 22 (2)
7Wind ≥ 25: the low group has 4 yes and 3 no, so 3 wrong. Cost 3. Wind ≥ 22 is still the best wind question.
- wind0518210312415518622725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- cost
- 1
- best
- rain (1)
8Now the other feature. Raining? The 3 rainy days are all "don't": perfect. The 5 dry days have 4 yes and 1 no: 1 wrong. Cost 1, the best of all.
- wind0518210312415518622725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- first question
- rain
- left to split
- 5
9The first question is "Raining?". The rainy group is pure, so it becomes a leaf: don't fly. Only the 5 dry days need more work.
- wind0518210312415518622t725rain0no1yes2no3yes4no5no6no7yesfly0yes1no2yes3no4yes5yes6no7no
- second question
- wind ≥ 22
- cost
- 0
10Repeat the search on the dry days. Wind ≥ 22 puts the one bad day alone. Cost 0: both groups are pure, so the tree is finished.
The result is the tree in the figure above. Notice that wind ≥ 22 was the best single wind question at the top, but rain was better still. Picking the best question at each step, without looking ahead, is a greedy strategy. It is fast and works well, though it does not guarantee the best possible tree.
// Mistakes if a group predicts its most common label.
function mistakes(labels) {
const yes = labels.filter((l) => l === 1).length;
return Math.min(yes, labels.length - yes);
}
// Try the question "feature >= t" for every feature and value.
function bestSplit(rows, labels) {
let best = null;
for (let f = 0; f < rows[0].length; f++) {
for (const t of new Set(rows.map((r) => r[f]))) {
const yes = [], no = [];
rows.forEach((r, i) => (r[f] >= t ? yes : no).push(labels[i]));
if (no.length === 0) continue; // everyone said yes: no split
const cost = mistakes(yes) + mistakes(no);
if (best === null || cost < best.cost) best = { feature: f, t, cost };
}
}
return best;
}
// Each row: [wind in km/h, rain (1 = yes)]. Label: 1 = good to fly.
const rows = [[5, 0], [8, 1], [10, 0], [12, 1], [15, 0], [18, 0], [22, 0], [25, 1]];
bestSplit(rows, [1, 0, 1, 0, 1, 1, 0, 0]); // { feature: 1, t: 1, cost: 1 }def mistakes(labels):
"""Mistakes if a group predicts its most common label."""
yes = sum(labels)
return min(yes, len(labels) - yes)
def best_split(rows, labels):
"""Try the question "feature >= t" for every feature and value."""
best = None
for f in range(len(rows[0])):
for t in sorted({r[f] for r in rows}):
yes = [l for r, l in zip(rows, labels) if r[f] >= t]
no = [l for r, l in zip(rows, labels) if r[f] < t]
if not no:
continue # everyone said yes: no split
cost = mistakes(yes) + mistakes(no)
if best is None or cost < best[2]:
best = (f, t, cost)
return best
# Each row: [wind in km/h, rain (1 = yes)]. Label: 1 = good to fly.
rows = [[5, 0], [8, 1], [10, 0], [12, 1], [15, 0], [18, 0], [22, 0], [25, 1]]
best_split(rows, [1, 0, 1, 0, 1, 1, 0, 0]) # (1, 1, 1)For each of f features, n candidate values, each checked against n examples. Real libraries sort each feature once and sweep, bringing one split down to O(f × n log n).
Real libraries score splits with a smoother measure than a mistake count, usually Gini impurity: for a group where a share p say yes, it is 1 − p² − (1 − p)². It is 0 for a pure group and largest for a 50/50 group. Unlike a mistake count, it rewards a split that makes a group purer even when the group's majority answer does not change.
Trees overfit, forests do not as much
Left alone, a tree keeps splitting until every leaf is pure. With enough questions it can carve out a leaf for every single training example, which is memorization, the problem from Overfitting and Evaluation. You can limit the depth, or require a minimum number of examples per leaf. But there is a better trick: grow many trees and let them vote. A group of models used together is called an ensemble.
- Random forest. Train hundreds of trees, each on a random sample of the examples, and let each split consider only a random handful of features. Every tree overfits in its own different way. When they vote, their individual quirks mostly cancel out and the shared pattern remains.
- Gradient boosting. Train small trees one after another. Each new tree is trained on the errors the earlier trees still make, so it focuses on the cases they got wrong. Their predictions are added up.
Common mistakes
Try it yourself
A tiny random forest has three trees, each trained on a different random sample of past days. Predict for both new days, tree by tree, then give the forest's vote. Then explain why tree B might have learned a different rule from the others.
Show a sample answer
- Day 1: A says fly (dry, wind below 22). B says don't fly (wind 20 ≥ 16). C says fly (dry). The vote is 2 to 1: fly.
- Day 2: A says don't fly (rain). B says fly (wind below 16). C says don't fly (rain). The vote is 2 to 1: don't fly.
Tree B probably got a random sample with few rainy days, so rain never looked useful, and the windy days in its sample happened to fall at 16 km/h and above. On its own, B would disagree with the other two on both days. In the vote, they outnumber it. A forest works because each tree is wrong in its own way, so the majority is usually right more often than any single tree.
Check yourself
A group of 6 training days has 4 "fly" and 2 "don't fly". If it becomes a leaf, how many of those days does it get wrong?
Why does a random forest usually do better on new data than one deep tree?
In gradient boosting, what does each new tree learn from?
What to practise next
Trees ask questions about the features you give them. Neural Networks learn their own intermediate features, layer by layer, which is what makes them so strong on images and text.