InterviewPrepKit

Home / Coding / Machine Learning Coding / Neural Net Internals / Two-Layer MLP Forward Pass

Two-Layer MLP Forward Pass

medium 00:00
Solving tips
  • An MLP is a stack of dense layers with a nonlinearity between them: relu after the hidden layer, but NOT after the output layer, whose raw scores are the logits.
  • ReLU is elementwise max(0, z); vectorize it as np.maximum(0, z), never a Python loop over entries.
  • Softmax must be numerically stable: subtract the per-row max before exponentiating, then normalize each row so its probabilities sum to 1.

Implement the forward pass of a two-layer multilayer perceptron (MLP), the smallest network that is more than a single linear layer. The hidden layer applies an affine transform followed by a ReLU nonlinearity, and the output layer applies a second affine transform to produce the logits. Interviewers use this to check that you can chain dense layers correctly, place the nonlinearity where it belongs (after the hidden layer, not after the output), and optionally turn logits into a valid probability distribution with a numerically stable softmax.

Definition

Given a batch X of shape (n, d_in), the network computes two stages:

h      = relu(X @ W1 + b1)     # (n, d_in) @ (d_in, d_hid) + (d_hid,) -> (n, d_hid)
logits = h @ W2 + b2           # (n, d_hid) @ (d_hid, d_out) + (d_out,) -> (n, d_out)

relu(z) = max(0, z) applied elementwise. The logits are the raw, unbounded output scores. If probabilities are requested, apply a row-wise softmax:

probs[i, j] = exp(logits[i, j] - max_k logits[i, k]) / sum_j exp(logits[i, j] - max_k logits[i, k])

Subtracting the per-row max before exp is a stability trick that avoids overflow without changing the result.

Task

Complete mlp_forward(X, W1, b1, W2, b2, return_probs=False). Compute the hidden activations h = relu(X @ W1 + b1), then the logits = h @ W2 + b2. Return logits when return_probs is False. When return_probs is True, also compute a numerically stable row-wise softmax of the logits and return the tuple (logits, probs). The nonlinearity is applied only after the hidden layer, never after the output layer. Everything must be vectorized with NO Python loops over samples. Use plain NumPy only (no PyTorch, TensorFlow, or Keras).

Example

X = np.array([[1.0, 2.0],
              [-1.0, 0.0]])
W1 = np.array([[1.0, -1.0, 0.0],
               [0.0,  1.0, 1.0]])
b1 = np.array([0.0, 0.0, 0.0])
W2 = np.array([[1.0, 0.0],
               [0.0, 1.0],
               [0.0, 1.0]])
b2 = np.array([0.0, 0.0])

mlp_forward(X, W1, b1, W2, b2)
# -> array([[1., 3.],
#           [0., 1.]])

logits, probs = mlp_forward(X, W1, b1, W2, b2, return_probs=True)
# logits -> array([[1., 3.],
#                  [0., 1.]])
# probs  -> array([[0.11920292, 0.88079708],
#                  [0.26894142, 0.73105858]])

For row 0, X @ W1 + b1 = [1, 1, 2]; ReLU leaves it unchanged since every entry is positive, so h = [1, 1, 2], and h @ W2 = [1, 3]. For row 1, X @ W1 + b1 = [-1, 1, 0]; ReLU clamps the -1 to 0, giving h = [0, 1, 0] and logits [0, 1].

Constraints

  • 1 <= n <= 10^4; 1 <= d_in, d_hid, d_out <= 10^3; use vectorized NumPy, no Python loop over samples.
  • Inner dimensions must agree: X.shape[1] == W1.shape[0], W1.shape[1] == b1.shape[0] == W2.shape[0], W2.shape[1] == b2.shape[0].
  • Softmax must be numerically stable (subtract the per-row max before exponentiating); each returned probability row sums to 1.
  • Values fit in float64; outputs are dense (n, d_out) float arrays.

Write your solution, then hit Run tests to check it — or get a mock grade from the AI coach.

The coach remembers this session — revise your code and ask again, and it grades your progress. It gives hints, not the answer.
Report a bug