Backpropagation through a two-layer MLP

ImplementHard40 min13 checks

backpropagationgradientsmlpchain-rule

Problem

Backpropagation computes the gradient of the loss with respect to every weight of a network. You run the forward pass once, keep the values that the backward pass needs, and then apply the chain rule from the loss back to the input. In this problem, you write both passes for a network with two sigmoid layers and a mean squared error loss.

Task

Write three functions.

  1. mlp_forward(x, params) runs the network on a batch and returns (output, cache).
  2. mse_loss(predicted, target) returns the mean squared error.
  3. mlp_backward(params, cache, target) returns the gradient of the loss with respect to each parameter.

The forward pass for a batch x of shape (N, n_in) is:

z_1 = x W_1 + b_1, \quad a_1 = \sigma(z_1), \quad z_2 = a_1 W_2 + b_2, \quad a_2 = \sigma(z_2)

Here \sigma(z) = 1 / (1 + e^{-z}) is the sigmoid. The output is a_2. The bias vectors add to every row.

The loss is the mean over all N \cdot n_{out} entries:

L = \frac{1}{N \, n_{out}} \sum_{i,j} (a_{2,ij} - y_{ij})^2

The cache is your own choice. It can be any Python object. The checks pass the cache from your mlp_forward to your mlp_backward and do not read it.

Input and output

NameTypeShapeMeaning
xfloat array(N, n_in)one row for each example
paramsdict of float arrayssee belowthe parameters of the network
params["W1"]float array(n_in, n_hidden)the weights of the hidden layer
params["b1"]float array(n_hidden,)the bias of the hidden layer
params["W2"]float array(n_hidden, n_out)the weights of the output layer
params["b2"]float array(n_out,)the bias of the output layer
predictedfloat array(N, n_out)the output of mlp_forward
targetfloat array(N, n_out)the target values, from 0 to 1
cacheanythe second value that your mlp_forward returns
  • mlp_forward returns a tuple (output, cache). output is a float array of shape (N, n_out).
  • mse_loss returns a Python float.
  • mlp_backward returns a dict with the keys "W1", "b1", "W2" and "b2". Each value is a float array with the shape of that parameter. It is the partial derivative of L with respect to that parameter.

Constraints

  • 1 <= N <= 64, 1 <= n_in, n_hidden, n_out <= 16.
  • Weights and inputs can be large. A pre-activation can be 1000 or more. No output or gradient can be NaN or infinite.
  • Do not change x, target or the arrays in params.
  • Use numpy only.

Examples

pythonx = np.array([[0.0, 1.0], [1.0, 0.0]])
params = {
    "W1": np.array([[0.5, -0.5, 1.0], [0.25, 0.75, -1.0]]),
    "b1": np.array([0.0, 0.1, -0.1]),
    "W2": np.array([[1.0], [-1.0], [0.5]]),
    "b2": np.array([0.2]),
}
target = np.array([[1.0], [0.0]])
output, cache = mlp_forward(x, params)
loss = mse_loss(output, target)
grads = mlp_backward(params, cache, target)
textoutput
[[0.5464852 ]
 [0.68495157]]
loss
0.33741716
grads["W2"]
[[ 0.02881637]
 [-0.01942579]
 [ 0.07701337]]
grads["b2"]
[0.03540898]
grads["W1"]
[[ 0.03473536 -0.03551239  0.01518726]
 [-0.02766515  0.0235782  -0.01053007]]
grads["b1"]
[ 0.0070702  -0.01193419  0.0046572 ]

Hints

5 hints

    Solution and notes

    Locked

    Pass every check with Submit to open the reference solution and the notes.

    Run it on your computer

    Clone the repository, install numpy and run the same checks in a terminal:

    python3 scripts/practice/run.py mlp-backprop my_answer.py
    python3 scripts/practice/run.py mlp-backprop my_answer.py --examples

    Start my_answer.py from starter.py.

    Your code

    Python loads on the first run

    Your code is saved in this browser as you type. On a phone you can read and edit the code, but the checks are easier to use on a desktop.

    Keyboard shortcuts
    Ctrl Enter
    Run the examples
    Ctrl Shift Enter
    Submit
    Tab / Shift Tab
    Indent or outdent the lines
    Esc then Tab
    Move the focus out of the editor
    Alt H
    Show the next hint

    On a Mac, use Cmd in place of Ctrl.

    Select Run to check the examples, or Submit to run every check.