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.

15 minLesson 5 of 7
On this page
  1. A tree is a chain of questions
  2. How a tree is learned
  3. Trees overfit, forests do not as much
  4. Common mistakes
  5. Try it yourself
  6. Check yourself
  7. What to practise next

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.

yes no no yes Raining? Don't fly 3 of 3 days Wind ≥ 22 km/h? Fly 4 of 4 days Don't fly 1 of 1 day
The tree learned from 8 past days. The numbers show how many training days ended in each leaf.

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.

Choosing the first questionUse the arrows or ← → keys
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
  1. wind
    05
    18t
    210
    312
    415
    518
    622
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

  2. wind
    05
    18
    210t
    312
    415
    518
    622
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

  3. wind
    05
    18
    210
    312t
    415
    518
    622
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

  4. wind
    05
    18
    210
    312
    415t
    518
    622
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    cost
    4
    best
    wind ≥ 8 (3)

    4Wind ≥ 15: 2 yes and 2 no on each side, so 2 wrong on each side. Cost 4.

  5. wind
    05
    18
    210
    312
    415
    518t
    622
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

  6. wind
    05
    18
    210
    312
    415
    518
    622t
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

  7. wind
    05
    18
    210
    312
    415
    518
    622
    725t
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

  8. wind
    05
    18
    210
    312
    415
    518
    622
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

  9. wind
    05
    18
    210
    312
    415
    518
    622
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

  10. wind
    05
    18
    210
    312
    415
    518
    622t
    725
    rain
    0no
    1yes
    2no
    3yes
    4no
    5no
    6no
    7yes
    fly
    0yes
    1no
    2yes
    3no
    4yes
    5yes
    6no
    7no
    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.

1 / 10

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)
TimeO(f × n²)
SpaceO(n)

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

Try it: Let the forest vote

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

Quick check

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.