set.seed(123)
library('tidyverse')
library('knitr')
library('neuralnet')
update_geom_defaults("line", list(linewidth = 1))
Leave-one-out cross-validation in R
Strategy
- Fit “straight” models: linear model, neural net with 2 layers with 3 perceptrons each
- Perform “Leave-one-out-cross-validation” (LOOC)
- Calculate mean absolute prediction error and root mean squared error of the different methods
- In cross-validation case for the left-out data point
Implementation
<- Vectorize(
leave_one_out function(i, type) {
<- cars[-i, ]
training <- cars[i, ]
test if(type == 'lm') {
<- lm(speed ~ dist, data = training)
fm <- predict(fm, newdata = test)
pp else {
} <- neuralnet(speed ~ dist,
fm linear.output = TRUE, data = training,
hidden = c(3, 3),
stepmax = 1e+7, threshold = .5)
<- predict(fm, newdata = test)[, 1]
pp
}return(pp)
vectorize.args = 'i') },
<- lm(speed ~ dist, data = cars)
fm_lm
<- neuralnet(speed ~ dist,
fm_nn linear.output = TRUE, data = cars,
hidden = c(3, 3),
stepmax = 1e+7, threshold = .5)
<- tibble(
data
cars,'Linear model straight' = predict(fm_lm),
'Neural net straight'= predict(fm_nn, newdata = cars)[, 1],
'Linear model with LOOC' = leave_one_out(1:nrow(cars), type = 'lm'),
'Neural net with LOOC' = leave_one_out(1:nrow(cars), type = 'nn')
)
Predictions for straight models for all integers from 0 to 125
<- tibble(dist = 0:125)
pred_df <- pred_df |>
pred_df mutate('Linear model' = predict(fm_lm, pred_df),
'Neural net' = predict(fm_nn, pred_df)[, 1])
<- tibble(dist = 80, value = 7, text = 'Original data as black dots')
text_to_add |> pivot_longer(cols = -dist) |>
pred_df ggplot(aes(dist, value, colour = name)) + geom_line() +
geom_point(mapping = aes(dist, speed), data = cars, inherit.aes = FALSE) +
geom_label(aes(dist, value, label = text), data = text_to_add, inherit.aes = FALSE) +
labs(colour = 'Model type', y = 'speed')
Predictions for all dist values that are in the original data set as well
|>
data select(-speed) |>
pivot_longer(cols = -dist) |>
ggplot(aes(dist, value, colour = name)) +
geom_line(linewidth = 1) +
labs(y = 'speed', colour = 'Model type') +
geom_point(aes(dist, speed), cars, inherit.aes = FALSE) +
geom_label(aes(dist, value, label = text), data = text_to_add, inherit.aes = FALSE)
<- data |>
data_long pivot_longer(cols = -c(dist, speed), names_to = 'Method')
|>
data_long mutate(error = value - speed,
squared_error = error^2) |>
group_by(Method) |>
summarize('Mean absolute error' = mean(abs(error)),
'Root mean squared error' = sqrt(mean(squared_error))) |>
kable(align = 'c', digits = 2)
Method | Mean absolute error | Root mean squared error |
---|---|---|
Linear model straight | 2.52 | 3.09 |
Linear model with LOOC | 2.63 | 3.24 |
Neural net straight | 1.95 | 2.43 |
Neural net with LOOC | 2.89 | 3.46 |
Conclusion
- The neural net seems to overfit!