Ten problems on maximum likelihood and Bayesian inference
A dataset-driven problem set that mirrors the two Day 4 lectures. The morning problems are pure maximum likelihood — grid search, numerical optimization, likelihood-ratio tests, and profile confidence intervals. The afternoon problems are your first Bayesian models, fit with Nimble. Five problems use the three real course datasets — vonbert.csv (Problem 2), whale.csv (Problems 3 and 10), and Logistic.csv (Problems 4 and 7); the rest are short simulate-and-fit or conceptual questions.
Watch out
Assessed problem set · worth 20 points. Answers are not shown here. Write your work in the R Markdown templates: morning_lab_template.Rmd for the morning (MLE) problems, afternoon_lab_template.Rmd for the afternoon (Bayesian) problems. Knit each to HTML and upload to the matching Canvas assignment.
For worked examples with visible answers, see the in-class practice lab.
Key idea
Topic scope — read this before you start. The two sessions use two different paradigms, and you must keep them separate.
- Morning (Problems 1–4) — maximum likelihood only. Use likelihood functions, grid search,
optim()/stats4::mle(), likelihood-ratio tests, and profile-likelihood confidence intervals. Do not use priors, posteriors, or MCMC here — Bayesian methods have not been taught yet in the morning. - Afternoon (Problems 5–10) — Bayesian only. Use priors, posteriors, conjugacy, and MCMC. All fitted Bayesian models must be written in Nimble (
nimbleCode+nimbleMCMC), not JAGS.
Aim for roughly 3 hours total (about 90 minutes per session). Try each problem before checking anything. All datasets live in this Day's data/ folder and are loaded exactly as in the lab.
Datasets for this assignment
Click a file to download it, then put it in a data/ subfolder beside your .Rmd so the read.csv() calls resolve.
vonbert.csvLength-at-age data for von Bertalanffy growth (columns: sex, age, length).whale.csvWhale blow counts, one row per whale (columns: sex, blows) — two-component Poisson mixture of males and females.Logistic.csvPopulation size and per-capita growth rate (r/K logistic model).Morning — Maximum likelihood
Post-release survival from a tagging study
You released 80 fish after barotrauma treatment and tracked them with acoustic tags; 47 were detected alive after 7 days. Treat each fish's outcome as an independent Bernoulli($\phi$), where $\phi$ is the probability of post-release survival.
- Write the joint likelihood $L(\phi)$ for the 80 fish. Take the log, differentiate, and solve algebraically for the MLE $\hat\phi$.
- Two confidence intervals — compute both and compare. Neither the term "Fisher information" nor the "Wald interval" was covered in lecture, so here is what they mean.
- The observed Fisher information is the curvature of the log-likelihood at the MLE: the negative second derivative $I(\hat\phi) = -\ell''(\hat\phi)$ (for a model with several parameters it is the negative Hessian matrix). A sharper peak means more information and a smaller standard error, $\text{SE} = 1/\sqrt{I(\hat\phi)}$. The Wald 95% CI is then the symmetric interval $\hat\phi \pm 1.96\,\text{SE}$. Compute $I(\hat\phi)$ and this Wald interval. (For a fitted
mle()/optim()object, R hands you the same SE viasqrt(diag(vcov(fit))), which inverts the Hessian for you.) - The profile-likelihood CI — the kind this course teaches — is the set of $\phi$ whose log-likelihood is within 1.92 units of the maximum, where $1.92 = \tfrac12\chi^2_{1,\,0.95} =$
0.5 * qchisq(0.95, 1). Obtain it withstats4::mle()andconfint().
- The observed Fisher information is the curvature of the log-likelihood at the MLE: the negative second derivative $I(\hat\phi) = -\ell''(\hat\phi)$ (for a model with several parameters it is the negative Hessian matrix). A sharper peak means more information and a smaller standard error, $\text{SE} = 1/\sqrt{I(\hat\phi)}$. The Wald 95% CI is then the symmetric interval $\hat\phi \pm 1.96\,\text{SE}$. Compute $I(\hat\phi)$ and this Wald interval. (For a fitted
- The manager asks: "What is the probability that true survival is at least 0.5?" Explain why maximum likelihood alone cannot answer that question directly, and name the framework (taught this afternoon) that can.
Biological intuition
Post-release survival is the single most contested number in catch-and-release fishery management. The same Bernoulli/binomial likelihood structure underlies mark-recapture survival, nest success, and germination trials — you will meet it again all week.Von Bertalanffy growth from length-at-age data (vonbert.csv)
The file data/vonbert.csv holds length-at-age records with three columns — sex (1 or 2), age (years), and length. The Von Bertalanffy growth function (VBGF) is
where $L_\infty$ is asymptotic length and $\kappa$ is the growth coefficient. Load the data exactly as in the lab:
TheData <- read.csv("data/vonbert.csv")
Sex <- TheData$sex; Age <- TheData$age; Len <- TheData$length
- Define your own R function — name it
nll_vbgf(Linf, kappa, sigma)— that takes the three parameters as arguments and returns the negative log-likelihood of the pooled data (both sexes). (nll_vbgfis not built in; you write it, exactly as in the lab walk-through.) Holding $L_\infty = 100$ and $\sigma = 10$ fixed, run a grid search for $\kappa$ overseq(0.05, 0.50, by = 0.005). Plot the NLL against $\kappa$ and report the grid-optimal $\hat\kappa$. - Now free all three parameters. Fit the full model with
mle()(method"L-BFGS-B"with sensible lower bounds). Report $\hat L_\infty$, $\hat\kappa$, $\hat\sigma$, and the profile-likelihood 95% CIs fromconfint(). - Test whether the sexes share a growth curve with a likelihood-ratio test. Fit a null model (one $L_\infty$, one $\kappa$, shared $\sigma$) and an alternative with sex-specific $L_\infty$ and $\kappa$ (shared $\sigma$). Compute $\Lambda = 2(\ell_{\text{alt}} - \ell_{\text{null}})$, compare to $\chi^2_2$, and report the p-value. Cross-check with
AIC(). - Explain why grid search does not scale to the full three-parameter model, and why it remains a useful sanity check on the optimizer.
Two-component Poisson mixture for whale blow counts (whale.csv)
The file data/whale.csv holds blow counts for individual whales (column sex codes each whale 1 or 2; column blows is the count). The population is a mix of males and females that blow at different average rates. Treat each whale's sex as unobserved and model the counts as a two-component mixture: males blow at rate $\lambda_1$, females at rate $\lambda_2$, and a whale is male with probability $\pi$ (the proportion of males) and female with probability $1-\pi$. Your goal is to estimate the male rate $\lambda_1$, the female rate $\lambda_2$, and the proportions $\pi$ and $1-\pi$:
TheData <- read.csv("data/whale.csv")
blows <- TheData$blows # the mixture ignores the sex column; fit is unsupervised
- Explain, in one or two sentences, why the mixture MLE is not just the pair of single-component Poisson MLEs. Identify precisely which step in the usual derivation ($\log\prod = \sum\log$) breaks down.
- Write the joint negative log-likelihood and fit it with
mle(), starting from $\lambda_1 < \lambda_2$ (e.g.lam1 = 10, lam2 = 30, p = 0.4). Report the estimated male rate $\hat\lambda_1$, female rate $\hat\lambda_2$, proportion of males $\hat\pi$, and their standard errors. - Refit from at least 8 different starting values. How many distinct local optima appear? Report the global best (lowest NLL) and the fraction of starts that reached it.
- The two-component mixture is invariant under relabeling: $(\lambda_1,\lambda_2,\pi)$ and $(\lambda_2,\lambda_1,1-\pi)$ are the same model. Explain how this shows up if you don't constrain $\lambda_1 < \lambda_2$, and why it would corrupt your standard errors.
- Validate against the labels. Your mixture never used the
sexcolumn — it estimated $\hat\pi$, the proportion of whales in the low-rate component, from the counts alone. In this dataset sex is coded1 = male,2 = female, and males are the lower-rate group. Compute the actual proportion of males,mean(TheData$sex == 1), and compare it to $\hat\pi$. Also compare the observed mean blow count within each sex to $\hat\lambda_1$ and $\hat\lambda_2$. Does the unsupervised mixture recover the real sex structure?
Logistic (r/K) growth by maximum likelihood (Logistic.csv)
The file data/Logistic.csv has two columns: PopulationSize ($N$) and GrowthRate (the per-capita rate $\tfrac{1}{N}\tfrac{dN}{dt}$). The per-capita form of logistic growth is linear in $N$:
Logistic <- read.csv("data/Logistic.csv", header = TRUE)
Logistic <- Logistic[order(Logistic$PopulationSize), ]
- The model is linear: intercept $= r$, slope $= -r/K$. Fit it with
lm(GrowthRate ~ PopulationSize). Because a Normal-error linear model fit by least squares is its maximum-likelihood fit, the fitted intercept and slope are already the MLEs of $r$ and $-r/K$. Now use the invariance property of the MLE: if $\hat\theta$ is the MLE of $\theta$, then $g(\hat\theta)$ is the MLE of any transformation $g(\theta)$. That is exactly why you may push the fitted coefficients through a formula to get MLEs of the biological parameters — e.g. $\hat K = -\hat r / \widehat{\text{slope}}$ is the MLE of $K$. Recover $\hat r$ and $\hat K$ this way. (You will invoke the same invariance property again in part (c) to turn $\hat r,\hat K$ into the MLE of MSY.) - Now fit the same model directly by maximum likelihood with
mle(), parameterized in $(r, K, \sigma)$ so that $\mu_i = r - (r/K)N_i$. Confirm you recover the same $\hat r$ and $\hat K$ as in (a), and report profile-likelihood 95% CIs. - Using the invariance of the MLE, compute the plug-in MLE of the maximum sustainable yield $\text{MSY} = \hat r\hat K/4$ and of the population size at maximum growth $K/2$.
- Maximum likelihood gives you point estimates and CIs for $r$ and $K$, but propagating that uncertainty into a derived quantity like MSY is awkward (delta method, bootstrap). Note in one sentence why — you will revisit this exact model in Problem 7, where the Bayesian posterior propagates the uncertainty for free.
Biological intuition
This is the simplest population-dynamics model in all of quantitative ecology. Everything downstream — surplus-production stock assessment, harvest theory — is built on the $r$/$K$ skeleton you fit here.Try it yourself
Your answers to Problems 1–4 go inmorning_lab_template.Rmd. Show your likelihood functions, code, output, and a one- or two-sentence biological interpretation for each part. Knit to HTML and upload to Canvas.Afternoon — Bayesian inference (Nimble)
Key idea
From here on, work in the Bayesian paradigm. Every fitted model below is written in Nimble with nimbleCode() and run with nimbleMCMC() (3 chains, a burn-in, and samplesAsCodaMCMC = TRUE so MCMCvis works directly). Report posterior summaries, and check convergence ($\hat R \approx 1$, healthy effective sample size) before interpreting.
Bayesian survival with an informative prior
You marked 50 adult animals in spring; 38 were re-encountered alive the following year. Literature suggests adult survival is around 0.55 with moderate uncertainty, which you encode as a Beta(11, 9) prior on $\phi$ (annual survival).
- Justify the Beta(11, 9) prior: what are its prior mean and its effective (pseudo) sample size, and what do they represent?
- Write the model in Nimble — $y \sim \text{Binomial}(n = 50, \phi)$ with the Beta(11, 9) prior — and fit it with
nimbleMCMC()using 3 chains. - Report the posterior mean, median, 95% BCI, and 95% HPDI for $\phi$, plus $\hat R$ and effective sample size.
- Compute $\Pr(\phi > 0.7 \mid \text{data})$ from the MCMC draws. Then, because Beta–Binomial is conjugate, compare to the closed-form Beta$(11+38,\, 9+12)$ = Beta(49, 21) posterior to verify Nimble is doing what you think.
Bayesian detection rate for a species of concern
You ran 25 ten-minute point counts and recorded these per-count detection numbers:
y = c(0,1,0,2,0,1,0,0,3,1,0,0,1,2,0,1,0,1,0,0,2,0,1,1,0)
Model the counts as iid Poisson($\lambda$), where $\lambda$ is the mean detections per count.
- Place a weakly informative Gamma($\alpha = 1$, $\beta = 1$) prior on $\lambda$. Write the model in Nimble and fit it.
- Report the posterior mean and 95% BCI for $\lambda$.
- Compute $\Pr(\lambda < 1 \mid y)$ — a direct statement about how rare detections are — from the posterior draws.
- Gamma–Poisson is conjugate: verify your Nimble fit against the closed-form Gamma$(1 + \sum y_i,\; 1 + n)$ posterior (mean, 95% interval).
Bayesian logistic (r/K) growth with derived quantities (Logistic.csv)
Return to data/Logistic.csv from Problem 4, but now fit the per-capita logistic model in the Bayesian framework so you get full posterior distributions for $r$, $K$, and any derived quantity. The template Logistic_BayesianGrowth_Nimble.R in this folder is your scaffold.
Logistic <- read.csv("data/Logistic.csv", header = TRUE)
Logistic <- Logistic[order(Logistic$PopulationSize), ]
- Write the model in Nimble with $y_i \sim \mathcal{N}\!\big(r - (r/K)x_i,\; \sigma^2\big)$ and priors $K \sim \text{Uniform}(0, 4000)$, $r \sim \text{Uniform}(0, 2)$, $\sigma \sim \text{Uniform}(0, 2)$ (use BUGS-style precision
tau <- 1/sigma^2). Fit with 3 chains. - Report posterior means and 95% BCIs for $r$, $K$, and $\sigma$. Confirm the posterior of $r$ is consistent with the morning MLE from Problem 4.
- Add the derived quantities $\text{MSY} = rK/4$ and $N_{\max} = K/2$ inside the model block. Report their posterior medians and 95% HPDIs. This is the "for free" uncertainty propagation promised in Problem 4(d).
- Compute $\Pr(r > 0.22 \mid y)$ from the posterior draws, and plot the posterior of $\sigma$ with both its 95% BCI and 95% HPDI overlaid. Comment on why the two intervals differ for $\sigma$.
Prior sensitivity in a data-poor problem
You observed 3 detections in 20 surveys. Compare four priors on the detection probability $p$: Beta(1, 1) (flat), Beta(1, 9) (skeptical, prior mean 0.10), Beta(20, 30) (informative around 0.40), and Beta(100, 100) (very informative around 0.50).
- For each prior, write down the posterior in closed form (Beta–Binomial conjugacy) and its posterior mean.
- Plot all four posteriors on the same axes. Which posterior is closest to the data MLE $\hat p = 3/20 = 0.15$, and which is most shifted away from it?
- A published paper fits this same dataset and reports a posterior mean of 0.49. Which prior most likely produced that result, and what specific concern would you raise as a reviewer?
BCI vs HPDI for a skewed posterior
You fit a Poisson model to clutch sizes at $n = 12$ nests. Write $y_i$ for the clutch size (number of eggs) at nest $i$; the data enter the Gamma–Poisson posterior only through their total, $\sum_{i=1}^{12} y_i$ — the total number of eggs summed across all 12 nests, here $\sum y_i = 38$. With a Gamma(2, 1) prior, the posterior on the mean clutch size $\lambda$ is Gamma$\bigl(2 + \textstyle\sum y_i,\; 1 + n\bigr)$ = Gamma$(2 + 38,\; 1 + 12)$ = Gamma(40, 13).
- Compute the posterior mean, mode, median, 95% equal-tailed BCI, and 95% HPDI for $\lambda$ (use
HDInterval::hdi()). - Plot the posterior density and overlay both intervals.
- Explain why the BCI and HPDI are nearly identical here, but would separate substantially for a strongly skewed posterior — e.g. a Gamma(1, 2) obtained from a Gamma(1, 1) prior and a single observation $y = 0$. What geometric feature of the density anchors the HPDI in that case?
Identifiability and label switching in a Bayesian mixture
You refit the two-component whale-blow mixture from Problem 3 (males vs females) in Nimble instead of with mle(). With 3 chains, no constraint on the ordering of $\lambda_1$ and $\lambda_2$, and chains started from different labelings, each chain locks onto its own labeling and stays there: one chain reports "$\lambda_1$" $\approx 10$ while another reports "$\lambda_1$" $\approx 30$ for the whole run. Every individual trace plot looks perfectly well mixed, but the chains disagree about what "$\lambda_1$" means, and $\hat R$ is enormous.
The culprit is identifiability. A parameter — or a combination of parameters — is non-identifiable when two different parameter values produce exactly the same likelihood, so no amount of data can separate them. Here the mixture is invariant under relabeling: $(\lambda_1,\lambda_2,\pi)$ and $(\lambda_2,\lambda_1,\,1-\pi)$ give an identical likelihood — swapping the "male" and "female" labels changes nothing the data can see. That exchange symmetry is what breaks the sampler.
- Explain geometrically what is happening. Why is $\hat R$ huge even though each chain has, in a sense, "converged" and the model fit is fine?
- Reproduce the pathology. First fit the mixture with no ordering constraint, deliberately starting chain 2 from the mirrored labeling (rates swapped, labels flipped) so the chains disagree from the outset. Run this and record $\hat R$ for
lam[1]— this is your "before" number:R · unconstrained mixture — the "before" run
Also plot the three traces forlibrary(nimble); library(MCMCvis); library(coda) set.seed(10) # so everyone gets the same Rhat unc_code <- nimbleCode({ lam[1] ~ dunif(0, 50) lam[2] ~ dunif(0, 50) # nothing forces lam[1] < lam[2] pi ~ dbeta(1, 1) pz[1] <- pi pz[2] <- 1 - pi for (i in 1:n) { z[i] ~ dcat(pz[1:2]) y[i] ~ dpois(lam[z[i]]) } }) zinit <- ifelse(blows < 20, 1, 2) # rough starting labels unc <- nimbleMCMC(unc_code, constants = list(n = length(blows)), data = list(y = blows), inits = list(list(lam = c(8, 28), pi = 0.4, z = zinit), list(lam = c(28, 8), pi = 0.6, z = 3 - zinit), # mirrored start list(lam = c(10, 32), pi = 0.35, z = zinit)), monitors = c("lam", "pi"), nchains = 3, nburnin = 2000, niter = 8000, samplesAsCodaMCMC = TRUE) MCMCsummary(unc) # Rhat for lam[1] is enormouslam[1]and note that each one is individually flat and well mixed — the disagreement is between chains, not within any one of them. - Fix it with an ordering constraint, then confirm the fix. The clean cure is to make the two rates un-swappable by construction — force $\lambda_1 < \lambda_2$. One simple way: give the lower (male) rate its own prior, build the higher (female) rate on top of it with a positive gap $\delta$, and give each whale a latent sex label. Complete and run this skeleton (3 chains, the same
blowsvector as Problem 3):R · Nimble mixture with λ₁ < λ₂ enforced
What to look for: after the constraint every chain agrees on which rate is which — $\lambda_1$ sits near 10 and $\lambda_2 = $library(nimble); library(MCMCvis); library(coda) set.seed(11) # so everyone gets the same Rhat mix_code <- nimbleCode({ lam1 ~ dunif(0, 50) # lower (male) rate delta ~ dunif(0, 50) # positive gap, so lam[2] > lam[1] lam[1] <- lam1 lam[2] <- lam1 + delta # higher (female) rate pi ~ dbeta(1, 1) # P(a whale is component 1 = male) pz[1] <- pi pz[2] <- 1 - pi for (i in 1:n) { z[i] ~ dcat(pz[1:2]) # latent sex label (1 or 2) y[i] ~ dpois(lam[z[i]]) } }) zinit <- ifelse(blows < 20, 1, 2) # rough starting labels out <- nimbleMCMC(mix_code, constants = list(n = length(blows)), data = list(y = blows), inits = list(list(lam1 = 8, delta = 20, pi = 0.4, z = zinit), list(lam1 = 12, delta = 18, pi = 0.5, z = zinit), list(lam1 = 10, delta = 22, pi = 0.35, z = zinit)), monitors = c("lam1", "delta", "pi"), nchains = 3, nburnin = 2000, niter = 8000, samplesAsCodaMCMC = TRUE) MCMCsummary(out) # check Rhat ~ 1lam1 + deltanear 30 in all three chains, rather than one chain having them the other way round. $\hat R$ drops to $\approx 1$, and the posterior means land on the Problem 3 MLEs ($\lambda_1\approx 10$, $\lambda_2\approx 30$, $\pi\approx 0.42$). Report $\hat R$ for $\lambda_1$ before and after adding the constraint — the two numbers should be dramatically different. - If you relabel the posterior draws post hoc so that $\lambda_1 < \lambda_2$ always, what bias might you introduce, and when is such post hoc relabeling justified?
Biological intuition
The same non-identifiability logic appears throughout fisheries and wildlife modeling — for instance the catchability–biomass product $qB$ in surplus-production stock assessment, where only the product is identified from a single index. Recognizing an unidentified parameter combination is a core modeling skill.Try it yourself
Your answers to Problems 5–10 go inafternoon_lab_template.Rmd. Show your Nimble model code, convergence diagnostics, posterior summaries, and a one- or two-sentence interpretation for each part. Knit to HTML and upload to Canvas.