quizforge / gallery / es335-quiz-1

Quiz 1 — Foundations, Trees & Nearest Neighbours

ES 335: Machine Learning 12 questions · 30 marks 4 randomized sets source ↗

Native kNN and loss-curve figures, confusion matrix and entropy tables, math blanks. Every set below contains exactly the same questions and total marks — only the question order and the MCQ option order differ, derived deterministically from the exam id and the set id.

The 4 versions

Set A, page 1
Set A · paper · key
Set B, page 1
Set B · paper · key
Set C, page 1
Set C · paper · key
Set D, page 1
Set D · paper · key

Answer key (set A)

Key page 1
✓ options, filled blanks, model answers · full key PDF

The source that generated all of this

One file produces every version above, its keys, and the grading CSV. The feature tour explains each marker with minimal code.

View exams/quiz3.typ (207 lines)
// Real-course demo: an ES 335-style Machine Learning quiz with figures and
// tables, written entirely in plain markup. Compiled as-is → answer key.
//
//   python3 scripts/build.py exams/quiz3.typ        # all sets + keys + CSV

#import "/quizforge/lib.typ": *

// ---- figures (plain Typst, no packages — grayscale for print) -------------

// kNN scatter: ● class A, □ class B, ⨯ the query point.
#let knn-fig = {
  let w = 5.2cm
  let h = 4.2cm
  let dot(x, y) = place(dx: x * w - 2.2pt, dy: (1 - y) * h - 2.2pt, circle(radius: 2.2pt, fill: black))
  let sq(x, y) = place(dx: x * w - 2.6pt, dy: (1 - y) * h - 2.6pt, rect(width: 5.2pt, height: 5.2pt, stroke: 0.9pt))
  let query(x, y) = place(
    dx: x * w - 4pt, dy: (1 - y) * h - 7.5pt,
    text(size: 13pt, weight: "bold", sym.times),
  )
  align(center, box(width: w + 1.4cm, height: h + 1.1cm, inset: (left: 0.9cm, bottom: 0.7cm, top: 0.4cm, right: 0.5cm), {
    place(bottom + left, line(length: w, stroke: 0.6pt))
    place(bottom + left, line(angle: -90deg, length: h, stroke: 0.6pt))
    place(bottom + center, dy: 0.55cm, text(size: 9pt, [$x_1$]))
    place(left + horizon, dx: -0.75cm, text(size: 9pt, [$x_2$]))
    box(width: w, height: h, {
      // class A cluster (top-left) with two members near the query
      dot(0.18, 0.72); dot(0.30, 0.84); dot(0.52, 0.86); dot(0.32, 0.62); dot(0.36, 0.44)
      // class B cluster (bottom-right) with one stray member nearest the query
      sq(0.62, 0.30); sq(0.70, 0.42); sq(0.80, 0.24); sq(0.66, 0.14); sq(0.86, 0.44)
      sq(0.50, 0.63)
      query(0.44, 0.55)
    })
  }))
}

// Loss curves: training loss falls; validation loss falls then rises.
#let loss-fig = {
  let w = 6.4cm
  let h = 3.6cm
  align(center, box(width: w + 3.4cm, height: h + 1.2cm, inset: (left: 1cm, bottom: 0.8cm, top: 0.4cm, right: 2.4cm), {
    place(bottom + left, line(length: w, stroke: 0.6pt))
    place(bottom + left, line(angle: -90deg, length: h, stroke: 0.6pt))
    place(bottom + center, dy: 0.55cm, text(size: 9pt, [epochs]))
    place(left + horizon, dx: -0.9cm, text(size: 9pt, [loss]))
    // training loss (solid): monotone decrease
    place(bottom + left, curve(
      stroke: 1pt,
      curve.move((0cm, -h + 0.3cm)),
      curve.cubic((w * 0.25, -0.5cm), (w * 0.5, -0.35cm), (w, -0.25cm)),
    ))
    // validation loss (dashed): dips at ~40% then rises
    place(bottom + left, curve(
      stroke: (thickness: 1pt, dash: "dashed"),
      curve.move((0cm, -h + 0.2cm)),
      curve.cubic((w * 0.3, -0.9cm), (w * 0.5, -1.1cm), (w, -h + 0.6cm)),
    ))
    place(top + right, dx: 2.2cm, dy: 0.15cm, text(size: 8.5pt, [--- validation \ — training]))
  }))
}

// ---- the paper -------------------------------------------------------------

#show: quiz.with(
  id: "es335-quiz-1",
  course: "ES 335: Machine Learning",
  title: "Quiz 1 — Foundations, Trees & Nearest Neighbours",
  date: "2026-08-28",
  duration: "50 minutes",
  sets: ("A", "B", "C", "D"),
  answer-grid: true,
  instructions: (
    [All logarithms are base 2 unless stated otherwise.],
    [Calculators are permitted; devices with network access are not.],
  ),
)

= Multiple Choice

Choose the single best answer unless a question says otherwise.

+ #m(2) A binary classifier produces the confusion matrix below.
  #align(center, table(
    columns: 3,
    align: center,
    stroke: 0.6pt,
    inset: 5pt,
    [], [*Predicted +*], [*Predicted −*],
    [*Actual +*], [40], [10],
    [*Actual −*], [20], [30],
  ))
  What is its precision?
  - ✓ $40 \/ 60$
  - $40 \/ 50$
  - $40 \/ 100$
  - $70 \/ 100$
  #explain[Precision $= "TP" \/ ("TP" + "FP") = 40 \/ (40 + 20)$. $40\/50$ is the
    recall and $70\/100$ the accuracy.]

+ #m(2) The figure shows training points from two classes and a query point $times$.
  #knn-fig
  Using Euclidean distance, how does the query point get classified by 1-NN and
  by 3-NN respectively?
  - ✓ 1-NN: class B (#sym.square), #h(2pt) 3-NN: class A (#sym.circle.filled)
  - 1-NN: class A (#sym.circle.filled), #h(2pt) 3-NN: class A (#sym.circle.filled)
  - 1-NN: class B (#sym.square), #h(2pt) 3-NN: class B (#sym.square)
  - 1-NN: class A (#sym.circle.filled), #h(2pt) 3-NN: tie
  #explain[The single nearest neighbour is the stray #sym.square just above the
    query; the 3-neighbourhood contains two #sym.circle.filled and one #sym.square.]

+ #m(2) Increasing $k$ in $k$-NN typically has which effect?
  - ✓ Increases bias, decreases variance
  - Decreases bias, increases variance
  - Increases both bias and variance
  - None of the above
  #explain[Larger neighbourhoods average over more points: smoother (higher-bias),
    more stable (lower-variance) decision boundaries.]

+ #m(2) Which of the following objectives are convex in their parameters?
  - ✓ Linear regression with squared loss
  - ✓ Logistic regression negative log-likelihood
  - The $k$-means clustering objective (jointly in assignments and centroids)
  - A two-layer neural network with squared loss
  #explain[Both linear-regression MSE and logistic NLL are convex; $k$-means and
    neural-network losses are not.]

+ #m(2) #qid("es335-loss-curves") The curves below were logged during training.
  #loss-fig
  What is the most appropriate response?
  - ✓ Stop earlier or regularize — the model is overfitting
  - Train longer — the model is underfitting
  - Increase model capacity — both losses are too high
  - Lower the learning rate — training has diverged
  #explain[Training loss keeps falling while validation loss rises after its
    minimum: the classic overfitting signature.]

+ #m(2) #opts(columns: 2) For a split that sends all positives left and all
  negatives right, the weighted entropy of the children is:
  - ✓ $0$
  - $0.5$
  - $1$
  - $log_2 3$

= Fill in the Blanks

+ #m(1) Computing inner products in a high-dimensional feature space without
  ever constructing the features is known as the kernel #blank[trick].

+ #m(1) Compared with L2, L1 regularization tends to produce weight vectors
  that are #blank(width: 3cm)[sparse (many exact zeros)].

+ #m(2) For linear regression in matrix form, the least-squares solution is
  $hat(theta) = (X^top X)^(#blank(width: 1.5cm)[$-1$]) X^top #blank(width: 1.5cm)[$y$]$.

= Long Answers #section(shuffle: false)

Answer in the space provided; show your working.

+ #m(5) The table records eight days of data for predicting whether a match is
  #emph[Played].
  #align(center, table(
    columns: 4,
    align: center,
    stroke: 0.6pt,
    inset: 4.5pt,
    [*Day*], [*Outlook*], [*Windy*], [*Played*],
    [1], [Sunny], [No], [Yes],
    [2], [Sunny], [Yes], [No],
    [3], [Overcast], [No], [Yes],
    [4], [Overcast], [Yes], [Yes],
    [5], [Rainy], [No], [Yes],
    [6], [Rainy], [Yes], [No],
    [7], [Sunny], [No], [No],
    [8], [Rainy], [No], [Yes],
  ))
  Compute (i) the entropy of #emph[Played], and (ii) the information gain of
  splitting on #emph[Windy]. Which single-feature split would a decision tree
  prefer if the gain for #emph[Outlook] is $0.311$?
  #answer(9cm, rubric: [+1 $H("Played")$; +2 conditional entropies; +1 gain;
    +1 correct comparison and conclusion.])[
    $H("Played") = H(5\/8) = -5/8 log 5/8 - 3/8 log 3/8 approx 0.954$.
    Windy = Yes: (1 Yes, 2 No), $H approx 0.918$; Windy = No: (4 Yes, 1 No),
    $H approx 0.722$. Weighted: $3/8 dot 0.918 + 5/8 dot 0.722 approx 0.795$.
    $"IG"("Windy") approx 0.954 - 0.795 = 0.159 < 0.311$, so the tree splits on
    #emph[Outlook].]

+ #m(5) Starting from $L(theta) = norm(y - X theta)_2^2$, derive the normal
  equation. State the condition under which the solution is unique, and give
  one practical remedy when it is not.
  #answer(8cm, rubric: [+2 gradient $-2 X^top (y - X theta)$; +1 setting to zero
    → $X^top X theta = X^top y$; +1 uniqueness ⇔ $X^top X$ invertible (full
    column rank); +1 remedy: ridge / drop collinear features.])[
    $nabla_theta L = -2 X^top (y - X theta) = 0 arrow.r.double X^top X theta =
    X^top y$. Unique iff $X$ has full column rank. Remedy: add ridge penalty
    $lambda I$ (or remove collinear features).]

+ #m(4) Your colleague reports 99.2% accuracy on a fraud-detection dataset in
  which 0.8% of transactions are fraudulent. Explain why this number is not
  impressive, and name two evaluation metrics (or tools) better suited to this
  setting, justifying each in one line.
  #answer(6cm, rubric: [+2 base-rate argument (predicting "not fraud" gives
    99.2%); +1 per metric with justification (max 2): precision/recall, F1,
    PR-AUC, recall at fixed FPR, cost-sensitive evaluation.])[
    A constant "not fraud" classifier already achieves 99.2% — accuracy is
    dominated by the majority class. Better: recall (catch rate of actual
    fraud) with precision or F1 / PR-AUC, which focus on the rare positive
    class rather than overall agreement.]