Vectorize One-Nearest-Neighbor and Express It as a Neural Forward Pass
OpenAIMachine Learning Engineer · Technical ScreenLast updated
Tests vectorized distance computation in array libraries and the mathematical equivalence between nearest-prototype classification and an affine neural network layer.
Vectorize One-Nearest-Neighbor and Express It as a Neural Forward Pass
Nearest-neighbor classification is one of the foundational non-parametric learning algorithms. In deep learning architectures, metric learning and prototype-based classifiers (e.g., Learning Vector Quantization, ProtoNets) bridge nearest-neighbor principles with neural network operations.
This problem has two parts:
Part 1: Vectorized 1-NN Inference
Implement a function predict_1nn(X_query, X_ref, y_ref) in NumPy/PyTorch-style pseudocode that computes predictions for a batch of query vectors against a reference set using squared Euclidean distance.
X_query: array of shape(M, D)representingMquery vectors of dimensionD.X_ref: array of shape(N, D)representingNreference vectors of dimensionD.y_ref: 1D array of shape(N,)containing the label for each reference vector.
Constraint: Do not use explicit Python loops over samples (M or N). The implementation must be fully vectorized.
Part 2: Nearest Prototype as a Linear Layer
Assume we have K class prototypes C of shape (K, D), where each row c_k is the representative vector for class k in {0, 1, ..., K-1}. An unnormalized query x of shape (D,) is assigned to class k* = argmin_k ||x - c_k||^2.
Show that this decision rule can be expressed as a standard linear feed-forward layer followed by an argmax:
logits = x @ W.T + b
pred = argmax(logits)
- Derive the mathematical expressions for weight matrix
W(shape(K, D)) and bias vectorb(shape(K,)) in terms of the prototype matrixC. - Explain why the data-dependent norm
||x||^2can be omitted from the logits.
Example
# Reference dataset (N=3, D=2)
X_ref = np.array([
[0.0, 0.0],
[1.0, 1.0],
[2.0, 2.0]
])
y_ref = np.array([0, 1, 1])
# Query points (M=2, D=2)
X_query = np.array([
[0.1, 0.2],
[1.8, 1.9]
])
# Nearest ref for X_query[0] is X_ref[0] -> label 0
# Nearest ref for X_query[1] is X_ref[2] -> label 1
# Output: array([0, 1])
Hints
Hint 1
To compute pairwise distances between (M, D) and (N, D) efficiently, expand ||a - b||^2 into ||a||^2 + ||b||^2 - 2 * (a @ b.T).
Hint 2
When mapping argmin of ||x - c_k||^2 to an argmax of logits, negate or invert the optimization direction.
Hint 3
Notice that the term ||x||^2 is constant with respect to the class index k, so it does not affect which class maximizes the expression.
Follow-up questions
- How does the numerical stability and memory footprint of the ||a - b||^2 expansion compare to broadcasting (X_query[:, None, :] - X_ref[None, :, :])**2?
- How can you make this prototype classifier differentiable so that the prototype vectors can be learned via backpropagation?
- If N is on the order of tens of millions, what algorithmic approximations or indexing structures (e.g., IVF-PQ, HNSW) would replace dense matrix multiplication?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.