Lecture 18 Maximum Likelihood in R

This is the R session for week 6. In lectures 16 and 17 we have been concentrating on some mathematical proofs, and in this week we will be considering the resulting theorems in a numerical context: we will find the maximum likelihood estimate for a model where the likelihood equation cannot be solved explicitly, we will learn how to read off the standard errors from the output of the optimiser, and we will use simulation to determine how often the interval θ^±1.96⁢se(θ^)\hat{\theta}\pm 1.96\,\mathop{\mathrm{se}}\nolimits(\hat{\theta}) from corollary 17.3 contains the truth. The written reference for this session is again appendix B.

18.1 The Log-Likelihood as an R Function

The model of the day is X1,…,XnX_{1},\dots,X_{n} i.i.d. from the gamma distribution with shape α\alpha and rate β\beta, both unknown. Example 4.9 gave the method of moments estimates α~=X¯2/V\tilde{\alpha}=\bar{X}^{2}/V and β~=X¯/V\tilde{\beta}=\bar{X}/V, with VV the plug-in variance, and section 5.3 noted that the likelihood equations have no closed-form solution. We simulate one sample of size n=40n=40 with α=2.5\alpha=2.5 and β=0.5\beta=0.5. The command dgamma with log = TRUE can be used to evaluate the logarithm of the density at every observation, and the sum of these values is ℓ⁢(α,β)\ell(\alpha,\beta). Since the R optimisers minimise, the function returns the negative log-likelihood, and since they hand over all parameters as one vector, it takes the vector par, shape first and rate second.

alpha <- 2.5
beta <- 0.5
n <- 40
set.seed(2026)
x <- rgamma(n, shape = alpha, rate = beta)
nll <- function(par, data) {
-sum(dgamma(data, shape = par[1], rate = par[2], log = TRUE))
}
nll(c(2.5, 0.5), x)
nll(c(1, 1), x)
## [1] 102.0691
## [1] 217.3799

The true parameter values lead to a much smaller negative log-likelihood than the pair (1,1)(1,1). Here we assume that we know the rate β=0.5\beta=0.5 and that we want to estimate the shape parameter α\alpha. The command optimise can be used to find the minimum of a function of one variable in a given interval, without requiring a starting value. Before you try the command, make a sketch of the negative log-likelihood as a function of α\alpha on the interval from 0.10.1 to 2020. How many dips will there be? Will the minimum lie above or below 2.52.5?

optimise(function(a) nll(c(a, 0.5), x), interval = c(0.1, 20))
## $minimum
## [1] 2.624051
##
## $objective
## [1] 101.924

The estimate α^=2.62\hat{\alpha}=2.62 is a little above the true value, and the minimum 101.9101.9 lies below the value 102.1102.1 where the truth is located. Since both parameters are now unknown, we have to use optim, which minimises a function of a vector, and which requires a starting value. From section 16.4 we know that a good way to find a starting value is to use a simple, consistent estimator. One possible choice for this is the method of moments estimate. Before you try the code, predict whether the maximum likelihood estimates will agree with the method of moments estimates to one decimal place.

V <- mean((x - mean(x))^2)
start <- c(mean(x)^2 / V, mean(x) / V)
start
fit <- optim(start, nll, data = x)
fit$par
fit$convergence
fit$counts
## [1] 2.2942540 0.4221649
## [1] 2.258769 0.415672
## [1] 0
## function gradient
## 43 NA

The extra argument data = x is passed on to our function. The result fit is a list: fit$par holds the estimates, α^=2.26\hat{\alpha}=2.26 and β^=0.42\hat{\beta}=0.42, which agree with the start to one decimal place; fit$convergence is 0 if the search finished properly, and any other value means the estimates cannot be trusted; fit$counts records that 4343 evaluations were needed. By default optim uses the Nelder–Mead method, which needs no derivatives; appendix B shows the alternative "BFGS".

18.2 Standard Errors from the Observed Information

From theorem 17.1 and the vector version of this result from lecture 17 we know that the MLE is approximately normally distributed with covariance matrix ℐ︀n⁢(θ0)−1\mathcal{I}_{n}(\theta_{0})^{-1}. As in section 17.3, we can replace ℐ︀n⁢(θ^)\mathcal{I}_{n}(\hat{\theta}) by the observed information, i.e. the matrix of second derivatives of the negative log-likelihood at the maximum. The optim function can compute this matrix for us if we ask for it explicitly, using hessian = TRUE. The matrix can be inverted using the solve command, the diag command gives the diagonal of this matrix, and the square roots of the diagonal elements are the standard errors. Before you look at the output, predict which of the two standard errors will be larger and by how much.

fit <- optim(start, nll, data = x, hessian = TRUE)
fit$hessian
covmat <- solve(fit$hessian)
se <- sqrt(diag(covmat))
result <- cbind(estimate = fit$par, se = se,
lower = fit$par - 1.96 * se, upper = fit$par + 1.96 * se)
rownames(result) <- c("shape", "rate")
round(result, 3)
cov2cor(covmat)[1, 2]
## [,1] [,2]
## [1,] 22.18712 -96.2299
## [2,] -96.22990 522.9200
## estimate se lower upper
## shape 2.259 0.473 1.333 3.185
## rate 0.416 0.097 0.225 0.606
## [1] 0.8933916

The table prints the results of a maximum likelihood estimate in an easy to read format. Each row corresponds to one parameter, and shows the parameter estimate, the standard error and the interval θ^±1.96⁢se(θ^)\hat{\theta}\pm 1.96\,\mathop{\mathrm{se}}\nolimits(\hat{\theta}) from corollary 17.3. Both intervals capture the true parameter values. The standard error for the shape parameter is approximately five times larger than the standard error for the rate parameter, but, since the estimates differ by a similar factor, the two standard errors are comparable relative to the estimates. The final row of the table, computed using the cov2cor function, shows that the two estimates have correlation 0.890.89. It is easy to show that, if we increase the shape and the rate simultaneously, while keeping the mean α/β\alpha/\beta fixed, the log-likelihood changes only slightly. Thus the data constrains the ratio α/β\alpha/\beta well, but poorly constrains each parameter individually. This is the problem of a nuisance parameter as discussed in lecture 14.

18.3 Starting Values

The gamma log-likelihood function has only one peak, so the method is forgiving. Before you try the code, make a prediction: what happens if we start with very different values, e.g. with shape 3030 and rate 3030? Will optim still find the maximum? Will it take more than 4343 evaluations to find the maximum, when we used the “good” start?

fit_far <- optim(c(30, 30), nll, data = x)
fit_far$par
fit_far$counts
## [1] 2.2571696 0.4154238
## function gradient
## 97 NA

The search reaches the same maximum, up to two decimal places, but it takes more than twice as long and R complains about several warnings and the presence of NaNs (not a number) in the output: the search tried to use negative parameter values where dgamma is not defined. We can prevent this by adding a line at the top of the function which returns Inf if any(par <= 0). This will prevent the optimiser from trying these values. For a “good” start this is not required. The real problem of a poor start is when the likelihood has several local maxima, as in section 5.3. We illustrate this effect using a toy function which is not a model: the logarithm of a two-component normal mixture density, evaluated at θ\theta, has a local maximum at θ=2\theta=2 and a lower local maximum at θ=−2\theta=-2 (see figure 18.1). Which of these two peaks will optimise report for the interval from −6-6 to 44, which contains both maxima? Which optim result will you get if you start at −3-3 and at 33, respectively? To find the maximum, optim needs to use a different method than the default method, and we can use the BFGS method. Also, we need to tell optimise that we want to find a maximum by using the argument maximum = TRUE.

toy <- function(theta) {
log(0.7 * dnorm(theta, mean = 2) + 0.3 * dnorm(theta, mean = -2, sd = 0.7))
}
curve(toy(x), from = -5, to = 5)
optimise(toy, interval = c(-6, 4), maximum = TRUE)$maximum
optim(-3, function(theta) -toy(theta), method = "BFGS")$par
optim(3, function(theta) -toy(theta), method = "BFGS")$par
## [1] -1.99892
## [1] -1.998923
## [1] 2
A smooth curve of theta ranging from -5 to 5,
featuring two peaks: a smaller one around theta = -2 and a larger,
broader one around theta = 2, with a dip around theta =
-0.4.
Figure 18.1: The toy function with the two peaks. The peak found by optim depends on the starting point. If optimise is used, it returns the lower peak, despite the interval containing both peaks.

Both optimisers find a local maximum, but neither of them can determine whether this is the global maximum: optim starts at and ends at the first peak it encounters, and optimise assumes that there is only one peak. There is no command which allows us to fix this problem. The only way to avoid this problem is to use some theory and to follow certain habits: always start with a consistent estimator (as we did for the gamma distribution earlier in this lecture), always plot the log-likelihood function (if the number of parameters is one), and always start the optim procedure again to see whether the solution is different.

18.4 Coverage of the Wald Interval

While corollary 17.3 shows that the interval θ^±1.96⁢se(θ^)\hat{\theta}\pm 1.96\,\mathop{\mathrm{se}}\nolimits(\hat{\theta}) covers the truth with probability approaching 0.950.95, in section 17.5 we have seen that sometimes, for moderate nn, this is not the case. To verify the claim for the gamma shape and rate, we use simulation in this section, following the approach to coverage introduced in lecture 12. The complete script for our simulation is given in the following box, with the lines shuffled (except for the bodies of the function and of the loop). Before you read the rest of this section, try to do the following: (1) write down the lines in a way which forms a working script, and (2) predict the coverages for n=20n=20: will they be above or below 0.950.95? Which of the two, shape or rate, will be worse?

data.frame(n = ns, shape = coverage[, 1], rate = coverage[, 2])
fit_gamma <- function(n, alpha, beta) {
x <- rgamma(n, shape = alpha, rate = beta)
V <- mean((x - mean(x))^2)
start <- c(mean(x)^2 / V, mean(x) / V)
fit <- optim(start, nll, data = x, hessian = TRUE)
c(fit$par, sqrt(diag(solve(fit$hessian))))
}
set.seed(2026)
for (i in seq_along(ns)) {
res <- replicate(5000, fit_gamma(ns[i], alpha, beta))
lower <- res[1:2, ] - 1.96 * res[3:4, ]
upper <- res[1:2, ] + 1.96 * res[3:4, ]
coverage[i, 1] <- mean(lower[1, ] <= alpha & alpha <= upper[1, ])
coverage[i, 2] <- mean(lower[2, ] <= beta & beta <= upper[2, ])
}
alpha <- 2.5
coverage <- matrix(0, nrow = length(ns), ncol = 2)
ns <- c(20, 100)
beta <- 0.5

Every name must exist before it is used: the function and the four assignments come first, with coverage after ns; then the seed, then the loop, and the table last. The one new detail is the return value, the two estimates followed by the two standard errors, which replicate stacks as the columns of a matrix; thus res[1:2, ] holds the estimates and res[3:4, ] the standard errors. The script in working order is R/S18-coverage.R; it fits the model 50005000 times for each sample size, takes a few seconds, and prints the following table.

## n shape rate
## 1 20 0.9624 0.9604
## 2 100 0.9508 0.9526

At n=100n=100 both coverages are within simulation error of the nominal 0.950.95, as expected from corollary 17.3. At n=20n=20 both coverages are above the nominal level, around 0.960.96: the interval is slightly too wide rather than too narrow. The total hides an imbalance again, and the script also counts for the shape at n=20n=20 how many misses are below the truth and how many are above.

## below above
## 0.0296 0.0116

Almost three quarters of the misses are below the true shape. Since the standard error increases with the estimate, an underestimated shape will result in a short interval which does not reach the truth, whereas an overestimated shape will result in a long interval which usually still covers the truth. The same mechanism was at work for the exponential rate in lecture 12.

18.5 A Task to Take Home

The following task is in the style of a practical report. The published scripts of this lecture can be used as a starting point. Use your student ID to seed the random number generator, and choose your own shape and rate. Simulate one gamma sample of size n=30n=30. Use optim to fit the model by maximum likelihood, starting with estimates from the method of moments. For each parameter, report the estimate, the standard error, and the interval θ^±1.96⁢se(θ^)\hat{\theta}\pm 1.96\,\mathop{\mathrm{se}}\nolimits(\hat{\theta}), in three separate sentences. For each interval, state whether the interval contains the value you chose. Estimate the coverage of both intervals when n=30n=30, using 20002000 replicates. Write a short paragraph about whether you would trust the intervals for this sample size. Finally, fit the normal model with unknown mean and variance, using optim, to a normal sample of size 3030. Check that the estimates match the closed forms from example 5.4, and that the standard error of the mean matches σ^/n\hat{\sigma}/\sqrt{n}.

Before the next session, you should type the commands used in this session, and should check that you get the same results as the text.

Summary.
  • •
    ​

    The written documentation for optim, optimise, solve and the Hessian matrix can be found in appendix B.

  • •
    ​

    The negative log-likelihood can be represented as an R function for one parameter vector, using the density with log = TRUE. optimise can then be used to minimise this function over an interval for one parameter, and optim can be used to find the minimum for several parameters, starting from a given point. It is important to check fit$convergence to see whether the optimisation was successful.

  • •
    ​

    If hessian = TRUE, the optimiser returns the observed information matrix. The inverse of this matrix can be used to approximately determine the covariance matrix of the MLE, and the square roots of the diagonal elements can be used to determine the standard errors reported in section 17.3. The results of a fit can be printed in a table which shows the estimates, standard errors and intervals for each parameter.

  • •
    ​

    A good starting point for the optimisation procedure is to use a consistent estimator, e.g. based on the method of moments. Also, since an optimiser finds a local maximum, it is important to plot the log-likelihood and to re-run the optimisation procedure with a different starting point.

  • •
    ​

    For the gamma distribution, with shape and rate parameters, the coverage of the Wald interval is slightly above 95%95\% for n=20n=20 and close to 95%95\% for n=100n=100, with most of the misses being below the true value. This is caused by small estimates having small standard errors.