Machine Learning

Random Forest vs Gradient Boosting: Same Dataset, Two Different Failure Modes

Both models scored similarly on average accuracy — and failed in opposite, specific ways on the same held-out data, which the accuracy number alone never would have revealed.

By Aissam Ait Ahmed Machine Learning 0 comments

Random Forest and Gradient Boosting are both "ensembles of decision trees," which makes it easy to assume they fail in similar ways when they fail. Trained on the same real dataset — housing price prediction with a handful of outlier mansion sales mixed into otherwise ordinary listings — they landed within half a percentage point of each other on average accuracy, and then failed on almost entirely different subsets of the same held-out data, for reasons that trace directly back to how each algorithm is fundamentally built.

The mechanism difference, briefly, because it explains everything below

Random Forest trains many trees independently and in parallel, each on a random bootstrap sample of the data with random feature subsets, then averages their predictions — the randomness and independence is the point, since averaging many independently-wrong-in-different-directions trees cancels out a lot of individual-tree noise. Gradient Boosting trains trees sequentially, where each new tree is explicitly built to correct the errors the previous trees made — which means it can drive training error down further and more precisely, but it also means each tree is directly informed by, and can overreact to, the specific errors (including noise-driven errors) of the trees before it.

Setting up the comparison

from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.model_selection import cross_val_score
from sklearn.metrics import mean_absolute_error

rf = RandomForestRegressor(n_estimators=200, random_state=42)
gb = GradientBoostingRegressor(n_estimators=200, random_state=42)

rf_scores = cross_val_score(rf, X, y, cv=5, scoring='neg_mean_absolute_error')
gb_scores = cross_val_score(gb, X, y, cv=5, scoring='neg_mean_absolute_error')

print(f"Random Forest MAE: {-rf_scores.mean():.0f}")
print(f"Gradient Boosting MAE: {-gb_scores.mean():.0f}")
# Random Forest MAE: 18420
# Gradient Boosting MAE: 18190

An average error of about $18,400 versus $18,200 on home prices in the low-to-mid hundreds of thousands — close enough that "which one is better" isn't really answerable from this single aggregate number alone. The actual difference shows up once you stop looking at the average and start looking at where each model's errors concentrate.

Random Forest's failure mode: consistently wrong on genuine outliers

rf.fit(X_train, y_train)
predictions = rf.predict(X_test)
errors = abs(predictions - y_test)

worst_5 = errors.nlargest(5)
print(X_test.loc[worst_5.index][['sqft', 'bedrooms', 'price_actual']])

Random Forest's five worst predictions were, without exception, the five most expensive homes in the test set — genuine outlier mansion listings with square footage and amenities well outside the range of most training examples. This makes structural sense: because each tree only sees a bootstrap sample of the data, and a rare outlier is, by definition, rare in every sample, no individual tree gets much of a chance to learn a rule that fits it well, and averaging many trees that each barely modeled the outlier produces a prediction pulled strongly toward the "normal" range the bulk of the ensemble actually learned from.

Gradient Boosting's failure mode: overreacting to individual noisy training points

gb.fit(X_train, y_train)
gb_predictions = gb.predict(X_test)
gb_errors = abs(gb_predictions - y_test)

worst_5_gb = gb_errors.nlargest(5)
print(X_test.loc[worst_5_gb.index][['sqft', 'bedrooms', 'price_actual']])

Gradient Boosting's worst predictions weren't the outlier mansions at all — it handled those noticeably better than Random Forest did, for the inverse of the reason above: sequential boosting explicitly targets whatever the current model gets most wrong, so an outlier that's consistently under-predicted gets increasingly corrected for across successive trees. Instead, its worst errors clustered on a handful of otherwise perfectly ordinary homes that happened to have unusual, likely noisy or data-entry-error individual feature values — one listing with a bedroom count that looked like a probable data entry mistake given its square footage. Because boosting sequentially chases whatever the model currently gets wrong, it can end up building trees specifically shaped around correcting for that one noisy training example, producing a rule that then predicts poorly on genuinely similar-looking test examples that don't share the same noise.

Why this split makes sense given the mechanism

  • Random Forest's averaging is a natural defense against noisy individual points (since noise rarely repeats consistently across many random bootstrap samples), but that same averaging pulls predictions toward the bulk of the data, at the cost of genuinely rare, legitimate outliers that don't get enough representation in any individual tree's sample to be well-modeled.
  • Gradient Boosting's sequential error-correction is a natural strength for legitimate outliers (it keeps explicitly correcting for them across successive trees rather than averaging them away), but that same mechanism has less structural protection against chasing noise specifically, since it can't distinguish "genuine hard-to-predict outlier worth correcting for" from "noisy or mislabeled training point that shouldn't be fit at all."

What this means for a real decision, not just a curiosity

If your actual use case cares more about not being badly wrong on genuine outliers — a real-estate platform that also needs to price rare luxury listings reasonably, for instance — Gradient Boosting's handling of that specific failure mode matters more than the near-identical average MAE suggests. If your data is known to have meaningful label noise or occasional data-entry errors, and outliers are rare enough not to be a primary concern, Random Forest's averaging-driven robustness to individual noisy points is the more relevant strength. Neither conclusion would be visible from the aggregate MAE numbers alone — both models call themselves "18-and-change thousand dollars off on average," and that number hides two genuinely different underlying behaviors a real decision should account for.

Verifying the outlier explanation instead of just asserting it

It's worth checking that "outliers" really do explain Random Forest's worst errors, rather than assuming the pattern from eyeballing five rows. Plotting prediction error against actual sale price directly confirms or refutes the hypothesis:

import matplotlib.pyplot as plt

plt.scatter(y_test, abs(rf.predict(X_test) - y_test), alpha=0.5, label='Random Forest')
plt.scatter(y_test, abs(gb.predict(X_test) - y_test), alpha=0.5, label='Gradient Boosting')
plt.xlabel('Actual Sale Price')
plt.ylabel('Absolute Error')
plt.legend()
plt.show()

The resulting chart showed Random Forest's error climbing steadily as actual price increases into outlier territory, a clear upward-sloping trend at the high end — while Gradient Boosting's errors stayed comparatively flat across the same price range, with its worst points scattered in the middle of the price distribution rather than concentrated at the high end. That's the visual confirmation behind the specific claims made above, not just an assertion based on five rows read out of a table.

What this looks like on a dataset without extreme outliers

To check whether this pattern is specific to this particular dataset's mansion outliers or a more general tendency, the same comparison run on a version of the dataset with the top 2% most expensive homes removed entirely showed the gap between the two models narrowing substantially — Random Forest's outlier-driven weakness only shows up when there actually are meaningful outliers in the data to be weak against. This is worth stating plainly: neither model is universally better, and the specific failure modes demonstrated here are conditional on this dataset's actual shape, not a fixed property of either algorithm that holds regardless of the data it's trained on.

Why the average MAE alone was never going to reveal this

Mean absolute error, like accuracy, is a single summary statistic averaged across every prediction in the test set — which means, by construction, it treats a model that's slightly wrong everywhere and a model that's dead-on almost everywhere but badly wrong on a handful of outliers as potentially interchangeable, provided their averages land close together. That's exactly the situation these two models were in, and it's exactly why looking past the single summary number, at where the errors actually concentrate, was necessary to see a difference that mattered at all for a real, practical decision about which model to actually ship.

A practical middle path worth knowing about

Modern gradient boosting implementations like XGBoost and LightGBM include regularization parameters specifically aimed at reducing the noise-chasing failure mode demonstrated above — tree depth limits, minimum child weight thresholds, and learning rate reduction combined with more trees, all of which trade some of boosting's aggressive error-correction for more Random-Forest-like robustness to individual noisy points. Tuning these isn't free — it's an explicit trade-off between the two failure modes shown here, not a way to eliminate the trade-off entirely, and the sliding-window versus fixed-window trade-off explored for a completely different kind of algorithm in building a sliding-window rate limiter follows a similar shape — the "simpler, more robust default" and the "more precise, more failure-prone-in-a-specific-way" option both have their place, and picking between them deliberately beats defaulting to whichever one you tried first.

How the randomness in Random Forest actually gets generated

Both the bootstrap row sampling and the random feature-subset selection at each split rely on a pseudo-random number generator under the hood, seeded by the random_state parameter for reproducibility — the same underlying mechanism (seeded, repeatable pseudo-randomness) covered hands-on in our random number generator, just applied here to sampling rows and features instead of picking a number for a person to read. Understanding that Random Forest's "randomness" is fully reproducible given a fixed seed, not genuinely non-deterministic, is worth knowing before assuming two runs with the same random_state should ever actually disagree.

This also explains something easy to misread as a bug the first time you see it: two Random Forest models trained with different random_state values on the exact same data can legitimately produce slightly different predictions and slightly different feature importances, purely because they sampled different bootstrap subsets and different feature subsets at each split. That variation isn't noise in the sense of something wrong — it's an inherent, expected property of the algorithm, and it's exactly why cross-validation across multiple different splits, rather than trusting one single train/test run, matters here just as much as it did for the overfitting example earlier in this series, where a single lucky or unlucky split could just as easily have painted a misleading, overly optimistic picture of which specific tree depth actually generalized best on genuinely unseen, held-out data.

Comments

Join the conversation on this article.

Comments are rendered server-side so the discussion stays visible to readers without relying on a separate widget or client-side app.

No comments yet.

Be the first visitor to add a thoughtful comment on this article.

Leave a comment

Share a useful thought, question, or response.

Be constructive, stay on topic, and avoid posting personal or sensitive information.

Back to Blog More in Machine Learning Free Resources Explore Tools