Batches of vectors
Networks process many examples at once. A batch is a matrix X of shape (n, d): one row per example. To apply a layer to every row, multiply by the transpose: X @ W.T + b. The result has shape (n, m), and b of shape (m,) is broadcast, added to every row.
import numpy as np
X = np.array([[1.0, 0.0],
[0.0, 1.0],
[1.0, 1.0]])
W = np.array([[2.0, 0.0],
[0.0, 3.0],
[1.0, -1.0]])
b = np.array([0.0, 1.0, 0.0])
print(X @ W.T + b) # shape (3, 3)Broadcasting pairs
Adding a size-1 axis lines arrays up for every pair: A[:, None, :] - B[None, :, :] has shape (n, m, d), all differences between rows of A and rows of B. Summing the squares over the last axis gives every squared distance with no Python loop. Attention, nearest neighbours and clustering all start this way.
Your task
Write batch_layer(X, W, b) applying the layer W @ x + b to every row of X, returning shape (n, m). Write pairwise_sq_distances(A, B) returning the (n, m) matrix whose entry [i, j] is the squared L2 distance between row i of A and row j of B, without Python loops.
The explanation opens here once every check passes.