set.seed(42)
n <- 100
x <- sort(runif(n, 0, 1))
y <- f_true(x) + rnorm(n, 0, 0.4)
# Fit models of increasing degrees and compute both train and test error
n_test <- 500
x_te <- seq(0, 1, length.out=n_test)
y_te <- f_true(x_te) + rnorm(n_test, 0, 0.4)
degrees <- 1:12
errors <- lapply(degrees, function(d) {
fit <- lm(y ~ poly(x, d))
xp_test <- poly(x_te, d, coefs=attr(poly(x, d), "coefs"))
data.frame(
degree = d,
train = mean(residuals(fit)^2),
test = mean((y_te - cbind(1, xp_test) %*% coef(fit))^2)
)
})
err_df <- do.call(rbind, errors)
ggplot(err_df, aes(x=degree)) +
geom_line(aes(y=train), colour="#2980b9", linewidth=1.3) +
geom_point(aes(y=train), colour="#2980b9", size=2.5) +
geom_line(aes(y=test), colour="#e74c3c", linewidth=1.3) +
geom_point(aes(y=test), colour="#e74c3c", size=2.5) +
geom_hline(yintercept=0.16, linetype="dashed", colour="#888") +
scale_x_continuous(breaks=degrees) +
labs(title="Training vs test error on a single dataset",
subtitle="Blue = training | Red = test | Dashed = σ² = 0.16",
x="Polynomial degree", y="MSE") +
theme_minimal()