CS6140 Machine Learning — Fall 2026

HW1 — Basic Classic Methods: Closed-form Regression, Decision Trees & KNN

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

Instructions. Submit your code (Jupyter notebook encouraged) together with a short report of results (tables + plots). You may use libraries (numpy, sklearn, matplotlib) for data loading, preprocessing, plotting, and basic math. Where a problem says "from scratch", the core algorithm must be your own — do not call a library implementation of it. Write your Decision Tree (PROBLEM 2) as a reusable fit() / predict() module: you will import it again in HW2.

Requirements.

Datasets. Links below are relative to the course data folder (../../data/...). Core datasets: Housing (regression; train, test, description), Spambase (binary classification; data, description), and MNIST (10-class digit classification, used for KNN) (train images, train labels, test images, test labels; or extracted HAAR features).

Starter code. For each problem, 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 — Closed-form Linear Regression + Ridge   [40 points]

Implement linear regression via the normal equations (closed form) from scratch, and its Ridge variant (L2 penalty, also closed form). Train and test on both datasets and report train/test error.

Library calls: sklearn.linear_model.LinearRegression, sklearn.linear_model.Ridge.


PROBLEM 2 — Decision / Regression Tree from scratch   [70 points]

Implement a decision tree from scratch. Features are numeric, so you need a threshold mechanism at each node (sort feature values, remove duplicates, use midpoints between successive values as candidate splits).

Write this as a reusable module — HW2 reuses it as the weak learner for boosting.

Library calls: sklearn.tree.DecisionTreeClassifier, sklearn.tree.DecisionTreeRegressor.


PROBLEM 3 — Boosted Trees for Regression   [50 points]

Boost your regression tree by fitting successive trees to the residuals (no AdaBoost, no re-weighting). For rounds i = 1..T on the Housing dataset:

Plot train and test error as a function of T (number of trees).

Library calls: sklearn.ensemble.GradientBoostingRegressor.


PROBLEM 4 — Similarity & KNN from scratch   [60 points]

Implement k-nearest-neighbor classification that works with different distance / similarity functions. There is no training phase — all the work happens at query time.

Library calls: sklearn.neighbors.KNeighborsClassifier, sklearn.neighbors.RadiusNeighborsClassifier, sklearn.metrics.pairwise.rbf_kernel, sklearn.metrics.pairwise.polynomial_kernel.


PROBLEM 5 (THEORY) — Feature Importance Stability: Single Tree vs. Boosted Ensemble   [25 points]

Consider a simple "feature importance" score for each feature: how often it is used as a split, weighted by the impurity reduction at that split — this applies directly to your PROBLEM 2 decision tree, and sums naturally over the weak learners of your PROBLEM 3 boosted ensemble. Suppose you retrain your single tree from scratch on a random 90% subsample of the same training data (Spambase) and recompute importances from the new tree: the top-5 most "important" features change substantially between the two runs. Repeating the same experiment with the boosted ensemble (summing importances over all weak learners) instead gives nearly identical top-5 features across the two subsamples.

In a short written answer, explain why the single tree's importances are so much less stable than the ensemble's, even though both models achieve similar accuracy.


PROBLEM 6 (THEORY) — Fixed-k vs. Fixed-Window KNN Under Non-Uniform Density   [25 points]

Your PROBLEM 4 KNN classifier supports both a fixed-k mode and a fixed-window (fixed-radius) mode. Suppose a dataset has regions where points are densely packed and regions where points are very sparse. Fixed-window KNN fails in the sparse regions: many query points there have zero training points within the chosen radius, so the classifier cannot predict at all. Fixed-k KNN never has this problem, but in the sparse regions the "k nearest" neighbors it finds can be very far away — arguably not meaningfully similar to the query point at all.

In a short written answer, propose which of the two you would use as the default for such a dataset, and describe one concrete modification to the other approach that would address its specific failure.



PROBLEM 7 — Self Study: Second-order (Newton) Boosting, from the XGBoost paper   [optional, no credit, substantial]

This one is a self-study mini-project, not a normal problem — budget real time, and expect several rounds of back-and-forth with an LLM before it actually works.

  1. Read. Chen & Guestrin, "XGBoost: A Scalable Tree Boosting System" (KDD 2016), §2.1–2.3 — the regularized learning objective, the additive (boosting) model, and the exact greedy split-finding algorithm (their Eq. 5–7, Algorithm 1). PROBLEM 3 above fits trees to raw residuals (first-order); this is the second-order generalization that made XGBoost XGBoost.
  2. Implement (LLM-assisted). Extend your PROBLEM 3 boosting to classification on Spambase, using log-loss:
  3. Validate. Plot train/test log-loss vs. boosting round, and compare against xgboost.XGBClassifier (or sklearn's HistGradientBoostingClassifier) on the same Spambase split. They should track closely if your math is right — if they don't, that's your signal to go back to the paper (or the LLM) and find the mismatch.
  4. Bonus: more than two classes (library only). Your from-scratch structure-score tree and boosting loop above assume binary log-loss; generalizing them to multiclass (softmax loss, a gradient/Hessian per class, one tree per class per round) is a substantially bigger build and is not required. Instead, just fit sklearn's (multiclass) GradientBoostingClassifier on MNIST, plot its train/test log-loss vs. round the same way, and note how accuracy/log-loss compare to the binary Spambase case — this is purely a "how does the library behave with more classes" check, no from-scratch code needed for MNIST.
  5. Deliverables.

Library calls: sklearn.ensemble.GradientBoostingClassifier, xgboost.XGBClassifier (or sklearn.ensemble.HistGradientBoostingClassifier).


PROBLEM 8 — Does Boosting Confidence Track KNN Neighborhood Consistency?   [optional, no credit]

This one connects PROBLEM 3/PROBLEM 7 (boosted trees) with PROBLEM 4 (KNN). Almost no new training is needed — reuse what you already built.

Hypothesis. Shallow trees split the same feature space k-NN measures proximity in. A test point sitting in a locally pure/consistent neighborhood should tend to land on the "easy" side of most trees in a boosted ensemble, so their contributions reinforce rather than cancel, giving a confident prediction. In a mixed/sparse neighborhood, different trees disagree and partially cancel, so the ensemble's output stays close to "unsure."

Run this on all three datasets: Spambase, Housing, and MNIST.

Report:


PROBLEM 9 — 1-D Normal Equations   [optional, no credit]

Derive explicit formulas for the normal equations solution presented in class for the case of one input dimension.

(Essentially assume the data is (xi,yi), i=1,2,...,m, and you are looking for h(x) = ax+b that realizes the minimum mean square error. The problem asks you to write down explicit formulas for a and b.)

HINT: Do not simply copy the formulas from here (but do read the article): either take the general formula derived in class and make the calculations (inverse, multiplications, transpose) for one dimension, or derive the formulas for a and b from scratch; in either case show the derivations. You can compare your end formulas with the ones linked above.


PROBLEM 10 — Convex Hulls and Linear Separability   [optional, no credit]

DHS chapter 5. The convex hull of a set of vectors xi, i = 1,...,n is the set of all vectors of the form ∑ αi xi, where the coefficients αi are nonnegative and sum to one. Given two sets of vectors, show that either they are linearly separable or their convex hulls intersect.

Hint on easy part: that the two conditions cannot happen simultaneously. Suppose that both statements are true, and consider the classification of a point in the intersection of the convex hulls.

[Difficult] Hard part: that at least one of the two conditions must hold. Suppose that the convex hulls don't intersect; then show the points are linearly separable.


PROBLEM 11 — Entropy Decrease is Bounded   [optional, no credit]

DHS chapter 8. Consider a binary decision tree using entropy splits (splits have 2 branches, labels have K classes).

A) Prove that the decrease in entropy by a split on a binary yes/no feature can never be greater than 1 bit. HINT: use the mutual information formula I(X,Y) = H(Y) − H(Y|X), and use its symmetric property.

B) [Optional, no credit] Generalize this result to the case of arbitrary branching B>1.


PROBLEM 12 — Read a Paper   [optional, no credit]

Pick one of the two (or find your own, on a similar theme):



PROBLEM 13 — Self Study: Implement LambdaMART for Learning-to-Rank   [optional, no credit, substantial]

Like PROBLEM 7, this is a self-study mini-project, not a normal problem — budget real time, and expect several rounds of back-and-forth with an LLM before it actually works. It revisits PROBLEM 3's residual-boosting loop a third time, now for ranking instead of regression or classification.

  1. Read. Burges, "From RankNet to LambdaRank to LambdaMART: An Overview" (Microsoft Research technical report MSR-TR-2010-82) — the pairwise RankNet cost, the LambdaRank gradient λ_ij that weights each pair by |ΔNDCG_ij|, and the LambdaMART algorithm that fits MART-style trees to these λ's instead of raw residuals. The lecture notes' own LambdaMART section derives the same algorithm end-to-end, including a fully worked ΔNDCG/λ_ij numeric example — read that first if the paper's notation is heavy going.
  2. Dataset. Microsoft's LETOR 4.0 MQ2007 dataset (or its smaller sibling MQ2008, if you want faster iteration) — pre-extracted 46-dimensional query–document feature vectors with graded relevance labels {0,1,2} and standard train/vali/test folds.
  3. Implement (LLM-assisted).
  4. Validate. Report NDCG@1 and NDCG@10 on the test fold. As a baseline, also fit a plain regression-tree ensemble directly to the raw relevance labels (i.e. PROBLEM 3's ordinary residual boosting, ignoring ranking structure entirely) and show LambdaMART's NDCG beats it — this isolates what the λ-gradient actually buys you over "just boost the labels." If you want a sanity-check magnitude: the lecture notes cite MQ2007 NDCG@1 numbers around 0.41–0.42 for RankBoost and (bagged) LambdaMART in the literature; you're not expected to match those exactly (different feature sets, folds, and hyperparameters), but you should be in the same ballpark, not wildly off.
  5. Deliverables.

PROBLEM 14 — Ridge Regression via Data Augmentation   [optional, no credit]

The lecture notes (see REGRESSION_ADVANCED_claude_0910.pdf, “An Alternate Derivation of Ridge, via Data Augmentation”) show that fitting ridge regression on your N real training points is identical to fitting ordinary, unregularized least squares on those same N points plus m synthetic “pseudo-rows,” one per feature, each pulling that feature's coefficient toward 0.

Using your PROBLEM 1 centered design matrix Z and label vector y, build the augmented system Zλ = [Z; √λ·Im], yλ = [y; 0m], and solve for w via your PROBLEM 1 (unregularized) normal-equations code on (Zλ, yλ) — no penalty term anywhere in this solve. Confirm numerically that this w matches your PROBLEM 1 ridge solution to numerical precision, on both Housing and Spambase, for at least two different λ values.

Library calls: none beyond what PROBLEM 1 already uses — this problem is entirely about reusing your own normal-equations solver on a different (augmented) dataset.


PROBLEM 15 — Ridge Is a Biased Estimator, Verified Empirically   [optional, no credit]

The lecture notes (see REGRESSION_ADVANCED_claude_0910.pdf, “Ridge Is a Biased Estimator”) prove that E[wridge] ≠ w* for λ ≠ 0, unlike ordinary least squares, which is unbiased. This problem asks you to observe that bias directly, by simulation, rather than just take the proof's word for it.

Library calls: none required beyond PROBLEM 1's own ridge solver and numpy.random for the simulation.