CS6140 Machine Learning — Fall 2026

HW3 — Kernels, SVM, Kernel Ridge, PCA, KPCA

Make sure you check the syllabus for the due date. Please use the notations adopted in class.

Instructions. Submit code (Jupyter notebook encouraged) plus a short report with tables and plots. Libraries (numpy, sklearn, matplotlib) are allowed for data handling, plotting, and basic math; the core of any "from scratch" problem must be your own.

Requirements.

Model/hyperparameter selection. Wherever a problem asks you to choose among kernels, hyperparameters, or a dimension (Problem 1's kernel grid, Problem 2's kernel + regularization, Problem 4's T), split off a validation set and pick using validation performance only. Report the test-set number once, for the configuration you already chose — never pick a configuration by looking at test performance.

Reading. The lecture notes for this HW: PCA and Feature Representations, Kernels and Kernelized Algorithms, SVM with Kernels (optional: SVM, Advanced Topics, for the optional problems). Notation in the starter code follows these notes (e.g. the Gaussian kernel is exp(−‖x−z‖2/σ2), i.e. sklearn's gamma = 1/σ2). Dataset links in each problem are relative to the course data folder (../../data/...).

Starter code. For each problem where a library counterpart exists, the starter notebook gives you the full data/eval pipeline and asks you to fill in two things: a short library-call baseline (a quick sanity-check number, using a well-known library function) and the from-scratch algorithm steps, evaluated the same way — your from-scratch number should land on or near the library one. Answers to the (THEORY) problems may be written directly in the notebook (e.g. a markdown cell) rather than a separate document, as long as they include any equation, plot, or illustration the answer depends on — not prose alone.


PROBLEM 1 — SVM: library, then from scratch (Simplified SMO)   [100 points]

Parts A–B use an SVM library of your choice (e.g. LIBSVM, scikit-learn) on real data. Parts C–D implement your own SVM solver and check it against the same library.

Note: the "stop once a fixed number of sweeps pass with no change" stopping rule of the Simplified SMO algorithm is not guaranteed to trigger on noisy, non-separable data. Cap the total number of sweeps (e.g. 300) so your run time stays bounded regardless of whether that rule ever fires — without a cap this can run for a very long time.

Library calls: sklearn.svm.SVC (linear, polynomial and RBF kernels), sklearn.multiclass.OneVsRestClassifier.


PROBLEM 2 — Kernel Ridge Regression from scratch   [50 points]

Implement a kernel ridge regression wrapper with fit(X, y) and predict(X); the initializer selects the kernel (linear, polynomial, RBF) and its hyperparameters (regularization λ solved as α = (K + λI)-1y; Kernels note, Section 8 — center the labels first, since this model has no intercept). The kernel evaluation may use a library, but the rest must be from scratch. Check your implementation against a library baseline (e.g. sklearn.kernel_ridge.KernelRidge) at the same kernel/hyperparameters.

Library calls: sklearn.kernel_ridge.KernelRidge.


PROBLEM 3 — PCA from scratch (feature corruption & rescue)   [40 points]

Using the polluted Spambase dataset (original points, many extra junk / duplicated features):

Library calls: sklearn.decomposition.PCA, sklearn.linear_model.LogisticRegression.


PROBLEM 4 — Kernel PCA + linear classifier   [60 points]

Datasets: 1000 2-D points each — TwoSpirals (binary, chance ≈ 50%) and ThreeCircles (a balanced 3-class dataset despite the shared file format, chance ≈ 33%). Report chance level alongside accuracy so the two datasets' numbers aren't compared as if both were binary.

Library calls: sklearn.decomposition.KernelPCA, sklearn.linear_model.LogisticRegression.


PROBLEM 5 (THEORY) — Choosing the RBF Bandwidth in Kernel Ridge Regression   [25 points]

Fit your PROBLEM 2 kernel ridge regression on WAVE or CRESCENT with an RBF kernel at a very small bandwidth vs. a much larger bandwidth. With a very small bandwidth, the fitted curve passes almost exactly through every training point, with wild oscillations between them; with a larger bandwidth, it looks like a smooth curve that doesn't hit every point exactly, but tracks the overall trend.

In a short written answer, explain this pattern, and propose how to choose the bandwidth properly (i.e., not by eye and not by minimizing training error).


PROBLEM 6 (THEORY) — One-vs-Rest SVM Ambiguity in Multiclass Regions   [25 points]

Your PROBLEM 1 (Part B) one-vs-rest multiclass SVM wrapper trains one binary SVM per class and predicts using whichever classifier gives the most confident "positive" score. For most test points this works fine, but for points near the boundary between three or more classes, it is possible for all the binary classifiers to output a negative ("not this class") decision, leaving no clear winner — or, conversely, for multiple classifiers to claim the point positively.

In a short written answer, explain why one-vs-rest can produce this kind of ambiguous region, and propose whether switching to a one-vs-one (all-pairs) voting scheme would fix it.



Optional problems (no credit). Problems 7–10 have their own sections in the starter notebook: set RUN_OPTIONAL = True there to work on them. With the default False those cells are skipped, so the notebook still runs top to bottom.


PROBLEM 7 — t-SNE Dimensionality Reduction   [optional, no credit]

t-SNE isn't a kernel method, but it rhymes with this HW's PCA/KPCA problems: another way to turn high-dimensional data into something a 2-D plot (or a simple downstream classifier) can actually work with — and unlike PCA, it's built specifically to preserve local neighborhood structure, at the cost of not being a linear projection at all.

Part A (library). Run a library t-SNE (e.g. sklearn.manifold.TSNE) on MNIST and/or 20 Newsgroups, project to 2 or 3 dimensions, and scatter-plot the points colored by label. Try a few perplexity values (e.g. 5, 20, 100) and see how the picture changes. Here's what a good result looks like — each color is a different digit/newsgroup, and they should separate into fairly clean clusters:

t-SNE of 20 Newsgroups, colored by label t-SNE of MNIST, colored by digit label

Part B (from scratch, harder). Implement t-SNE yourself. We're deliberately not giving you code here — go read Laurens van der Maaten's own t-SNE page (the original author; it has the paper, reference implementations in several languages, and FAQ) and work from that. At a high level, the algorithm is:

  1. Reduce the input to ~50 dimensions with PCA first (t-SNE itself is for refining local structure, not for crunching raw high-dimensional data efficiently).
  2. Compute the pairwise squared-distance matrix in that reduced input space.
  3. For each point i, binary-search a per-point bandwidth so the resulting conditional distribution over its neighbors has a fixed "effective number of neighbors" — matching a target perplexity via that distribution's entropy.
  4. Symmetrize and normalize into one joint distribution P over all pairs, in the original high-dimensional space.
  5. Initialize a random low-dimensional embedding Y; define Q as the (normalized) Student-t similarities between points in that embedding — the heavy tail is what gives t-SNE room to spread out clusters that would otherwise collapse on top of each other.
  6. Gradient-descend (momentum/adaptive gains help a lot in practice) on KL(P ‖ Q), nudging points in Y until the low-dimensional similarities match the high-dimensional ones as well as they can.
  7. Every few iterations, re-center Y and check in (plot it, watch the KL-divergence trend down) — this is as much art as it is algorithm.

Run your implementation on MNIST (or a manageable subsample of it) and compare your scatter plot against Part A's library result.

Library calls: sklearn.manifold.TSNE (Part A only — Part B is the from-scratch counterpart).


PROBLEM 8 — Four Ways to Reduce Dimensions: PCA, ICA, Kernel PCA, t-SNE   [optional, no credit]

Each method keeps something different: PCA keeps variance in uncorrelated directions, ICA looks for statistically independent components, Kernel PCA does PCA in a kernel feature space, and t-SNE keeps local neighborhoods. This problem shows a task where each objective matters (PCA note, optional ICA section; Kernels note, Section 11; Problem 7 for t-SNE).

Library calls: sklearn.decomposition.PCA, FastICA, KernelPCA, sklearn.manifold.TSNE, sklearn.neighbors.KNeighborsClassifier.


PROBLEM 9 — Random Fourier Features: Kernel Methods Without the N×N Matrix   [optional, no credit]

Kernel ridge regression stores and solves with an N×N matrix. Random Fourier features replace the Gaussian kernel with an explicit map z(x) of R random cosine features, so that z(x)z(x')T ≈ K(x,x'); then plain (linear) ridge regression on z does the job (Kernels note, Section 14.3).

Library calls: sklearn.kernel_approximation.RBFSampler, Nystroem, sklearn.linear_model.Ridge, sklearn.kernel_ridge.KernelRidge.


PROBLEM 10 — Dr. Lawler's Patient Similarity: When a Similarity Is Not a Kernel   [optional, no credit]

Dr. Lawler, an endocrinologist, has compared patients the same way for years. On each of 6 routine lab tests, two patients "agree" if their values are within a clinical tolerance of each other, and "disagree" otherwise; a clear disagreement should count against similarity, so he scores +1 for agree and −1 for disagree, and averages over the tests:

s(a, b) = (1/6) ∑k ( +1 if |ak − bk| ≤ tk, −1 otherwise ),   with tolerances t = glucose 10 mg/dL, HbA1c 0.3 %, BMI 2, systolic BP 10 mmHg, LDL 15 mg/dL, triglycerides 30 mg/dL.

It is symmetric, between −1 and 1, and every patient is fully similar to themselves (s = 1), so it looks like a perfectly good similarity. After the kernel lectures he plugged it into Kernel PCA (to aggregate the 6 labs into a few features) and into an SVM with C = 10 (to predict who develops type-2 diabetes within 5 years), on 400 of his patients (lawler_patients.csv: patient id, the 6 labs, label diabetes_5yr; the data is synthetic). Things went wrong in strange ways. Help him.

What to expect: nearly half of the eigenvalues are negative and large, so the "variance explained" goes well above 100% and β turns into NaNs; ‖w‖2 comes out negative, so the margin is not a number; reordering the patients changes on the order of 10% of the predictions (sometimes far more), and different SMO seeds stop at different objectives. In Part D not every repair helps: one of them collapses to predicting "no diabetes" for everyone. The softened similarity is the one he can actually use, including for new patients.

Library calls: numpy.linalg.eigh, sklearn.svm.SVC(kernel='precomputed'), sklearn.model_selection.train_test_split.


PROBLEM 11 — SVM-SMO on the Digits Dataset   [optional, no credit]

Instead of a library SVM, run your own Problem 1 SMO solver on the Digits dataset. Digits has 10 classes and your SMO is a binary classifier, so you need a multiclass wrapper on top of it — choose one of:

Digits is learnable enough that you can subsample the training set (e.g. 10–20% per class) to keep runtime down — but evaluate on the full test set.


PROBLEM 12 — The Full Platt SMO Algorithm   [optional, no credit]

Problem 1 (Parts C–D) uses the CS229 Simplified SMO algorithm (random second-variable selection, no error cache). Extra credit for implementing the full Platt SMO heuristics instead, following the Platt SMO paper (summary in the SVM Advanced note, Section 6):

Reference implementations already exist in this course's materials (6_SVM_kernels/code_HW6/bingyu/SVM/SVM.py, SMO.java) if you want to check your work against them — but implement your own rather than copying them.


PROBLEM 13 — Solve the Dual with a Library QP Solver   [optional, no credit]

Same problem as Problem 1, Parts C–D, but instead of SMO, solve the SVM dual directly with a general-purpose quadratic-programming solver (e.g. cvxopt.solvers.qp in Python, or an equivalent in Matlab/Java/C); the SVM Advanced note, Section 5, writes the dual in the matrix form these solvers expect. Compare the resulting α's and accuracy against your SMO solution.


PROBLEM 14 — Six-Point Hyperplane by Inspection   [optional, no credit]

Consider the following 6 points in 2-D, for two classes:

class 0:   (1,1)   (2,2)   (2,0)
class 1:   (0,0)   (1,0)   (0,1)

a) Plot these 6 points, construct the optimal hyperplane by inspection and intuition (give W, b) and calculate the margin.
b) Which points are support vectors?
c) [Extra credit] Construct the hyperplane by solving the dual optimization problem using the Lagrangian. Compare with part (a).


PROBLEM 15 — The Dual Constraint 0 ≤ α ≤ C/m   [optional, no credit]

Explain why 0 ≤ α ≤ C/m is a constraint in the dual optimization with slack variables (hint: read the Burges SVM tutorial first). Distinguish three cases, and explain each in terms of the classification and the constraints: a) α = 0; b) 0 < α < C/m; c) α = C/m. This has been discussed in class and in the SVM notes (SVM note, Section 3; SVM Advanced note, Sections 2–3); a detailed, rigorous explanation is expected, not just a restatement of the three cases.


PROBLEM 16 — VC Dimension   [optional, no credit]

What is the VC dimension of an SVM with a linear kernel? (Mostly of historical interest; see the last section of the SVM Advanced note.)


PROBLEM 17 — LDA Instead of PCA   [optional, no credit]

Problem 3 rescues polluted Spambase with PCA. Run LDA instead of PCA before the downstream logistic regression classifier — you can use a library LDA implementation (e.g. sklearn.discriminant_analysis.LinearDiscriminantAnalysis; Fisher's LDA is in the optional part of the PCA note). Compare accuracy against Problem 3's PCA result.


PROBLEM 18 — DHS Chapter 5   [optional, no credit]

Pick a problem from DHS (Duda, Hart & Stork, Pattern Classification) chapter 5 (linear discriminant functions / SVMs) that isn't already covered above, and solve it. If you're not sure which one to pick, ask.


PROBLEM 19 — Linear SVM by Subgradient Descent (Pegasos)   [optional, no credit]

The soft-margin SVM is also an unconstrained problem: a regularizer plus the average hinge loss, (λ/2)‖w‖2 + (1/N)∑i max(0, 1 − yi(xiw + b)), with λ = 1/(NC) (SVM Advanced note, Section 7). That means it can be trained like HW2's logistic regression, one point at a time, with no dual and no kernel matrix.

Library calls: sklearn.svm.LinearSVC, sklearn.svm.SVC (sklearn.linear_model.SGDClassifier(loss='hinge') is the library version of the same idea).


PROBLEM 20 — Support Vector Regression vs. Kernel Ridge Regression   [optional, no credit]

SVR replaces the hinge with the ε-insensitive loss: residuals smaller than ε cost nothing, so all points inside a tube of half-width ε around the fit get zero weight (SVM Advanced note, Section 8). Kernel ridge regression (Problem 2) gives every training point a weight.

Library calls: sklearn.svm.SVR, sklearn.kernel_ridge.KernelRidge.