Plug-in Prediction and Held-Out Evaluation

Context: FIT2086_MOC Β· Studio 3 Β· what you do with after fitting β€” predict, then score the prediction on data the fit never saw Β· the first appearance of train/test thinking that becomes cross-validation later in the unit Β· extends R Toolkit (Cheatsheet) Β· data: train.csv ( heights), test.csv () Problem it solves: fit a distribution to a small sample, turn it into probability statements about the population, and check whether those probabilities were any good.

Quick Revision

  • 🎯 Trigger: β€œfit a model and predict ” βž” estimate on train β†’ pnorm with β†’ compare against empirical proportions on test β†’ score with the out-of-sample negative log-likelihood.
  • ⚠️ Key Constraint: every estimate comes from train only; test is touched only for scoring. Re-estimating on test destroys the whole point of the exercise.

πŸ”§ Minimal Working Example

# 1. Fit: all three estimates in one list-returning function
my_estimates <- function(X) {
  n = length(X)
  retval = list()
  retval$mu_ml = sum(X)/n                    # sample mean
  e2 = (X - retval$mu_ml)^2                  # squared deviations, reused twice
  retval$var_ml = sum(e2)/n                  # divisor n   βž” biased
  retval$var_u  = sum(e2)/(n-1)              # divisor n-1 βž” unbiased
  return(retval)
}
train <- read.csv("train.csv"); test <- read.csv("test.csv")
est <- my_estimates(train$heights)
 
# 2. Predict: plug the estimates into the family β€” pnorm takes the SD, not the variance
1 - pnorm(1.7, est$mu_ml, sqrt(est$var_ml))                                  # P(X > 1.7)
pnorm(1.5, est$mu_ml, sqrt(est$var_u))                                       # P(X < 1.5)
pnorm(1.75, est$mu_ml, sqrt(est$var_u)) - pnorm(1.6, est$mu_ml, sqrt(est$var_u))
 
# 3. Ground truth: the empirical proportion in the held-out data
mean(test$heights > 1.7)          # logical vector βž” TRUE=1, FALSE=0 βž” mean = proportion
 
# 4. Score: negative log-likelihood of the HELD-OUT data under the fitted model
norm_negloglike <- function(y, mu, sigma) {
  n = length(y)
  return( (n/2)*log(2*pi*sigma^2) + 1/2/sigma^2*sum((y-mu)^2) )
}
norm_negloglike(test$heights, est$mu_ml, sqrt(est$var_ml))
norm_negloglike(test$heights, est$mu_ml, sqrt(est$var_u))

Expected output: , , β€” the unbiased estimate is always the larger, by the factor .

πŸ“Š Predicted vs Empirical (fit on , scored on )

EventML Unbiased Empirical (test)Verdict
ML closer β€” both overestimate
unbiased closer β€” both underestimate
unbiased closer
Predictive NLL on testβ€”unbiased wins (lower is better, )

Final extracted output: the model fits the future data better overall, because a wider density spreads probability onto the tail values a sample never showed β€” but that same widening is what makes it overshoot . With large the two estimates converge and the distinction evaporates.

πŸ”€ Variations

  • Two densities on one plot βž” scatter the sample on the -axis, then overlay both fitted pdfs to see the width difference:
plot(x=train$heights, y=rep(0,10), ylim=c(0,6), xlim=c(1.4,2), ylab="p(heights)", xlab="Heights")
xv = seq(from=1.4, to=2, length.out=100)          # smooth grid for the curves
lines(xv, dnorm(xv, est$mu_ml, sqrt(est$var_ml)), lwd=2.5, col="red")
lines(xv, dnorm(xv, est$mu_ml, sqrt(est$var_u)),  lwd=2.5, col="blue")
legend(x=1.75, y=6, c("Samples","ML Estimate","Unbiased Estimate"),
       lty=c(0,1,1), pch=c("o","",""), col=c("black","red","blue"), lwd=c(1,2.5,2.5))
  • Why the NLL, not just the three probabilities βž” the NLL scores the model at every observed value at once rather than at three hand-picked thresholds; low probability assigned to values that actually occur is punished by of a small number being large.

✍️ Practice

⚠️ Common Mistakes

  • πŸ’‘ Passing the variance where R wants the SD βž” pnorm(1.7, mu, est$var_ml) silently models ; every call needs sqrt(...).
  • πŸ’‘ Reading a negative NLL as an error βž” for continuous data the density can exceed (here ), so goes negative; only differences between models carry meaning.
  • πŸ’‘ Estimating on the test set βž” test exists only to stand in for the unseen population; fitting on it makes the score self-congratulatory and, in reality, you never have it.
  • πŸ’‘ Trusting a cat() label over its argument βž” the released studio3.solns.R prints the var_ml figure under the label β€œ(unbiased)” and vice versa; read which estimate is actually passed to the function.
  • πŸ’‘ Concluding β€œunbiased is better” in general βž” it won this comparison at ; the ranking is a property of this dataset and sample size, and both estimates converge as grows.