Build your own neural network
Stage 5 of 6v1 · e3f9bfc4

The Training Epoch and Backpropagation

Combine sigmoid forward pass, MSE cost function, and the chain rule to accumulate gradients and update parameters across a batch of samples.

Build it in your language

The tester treats your program as a black box: any language works. These are just the most convenient ones for this project.

First Principles: The Chain Rule and Demystifying Backpropagation

BackpropagationAn algorithm for calculating the gradient of the loss function with respect to weights using the chain ruleView official documentation is often portrayed as esoteric, but at its heart lies a straightforward intuition: tracing the root cause of an error backwards.

Imagine a gear train: you turn an input dial (weight ww), which rotates an internal gear (linear combination zz), which opens a pressure valve (sigmoid activation y^\hat{y}), which affects the final reading on a gauge (loss LL). If the final gauge reading is off, how much should you turn the input dial?

The chain rule of calculus states that we simply multiply the rates of change along each link in the pipeline:

∂L∂wj=∂L∂y^⋅∂y^∂z⋅∂z∂wj \frac{\partial L}{\partial w_j} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} \cdot \frac{\partial z}{\partial w_j}

Breaking Down Each Link Step by Step:

  1. Link 1 (How loss changes with respect to prediction): For an individual squared loss L=12(y^−y)2L = \frac{1}{2}(\hat{y} - y)^2, its derivative is: ∂L∂y^=y^−y\frac{\partial L}{\partial \hat{y}} = \hat{y} - y
  2. Link 2 (How sigmoid activation changes with respect to zz): One of the most elegant mathematical properties of the sigmoid function is that its derivative can be expressed directly in terms of its output: ∂y^∂z=y^⋅(1−y^)\frac{\partial \hat{y}}{\partial z} = \hat{y} \cdot (1 - \hat{y})
  3. Link 3 (How the affine combination changes with respect to weight wjw_j or bias bb): Since z=w1x1+⋯+wjxj+bz = w_1 x_1 + \dots + w_j x_j + b: ∂z∂wj=xj,∂z∂b=1\frac{\partial z}{\partial w_j} = x_j, \quad \frac{\partial z}{\partial b} = 1

Multiplying the first two links yields the local sample error term δ\delta:

δ=(y^−y)⋅y^⋅(1−y^) \delta = (\hat{y} - y) \cdot \hat{y} \cdot (1 - \hat{y})

Consequently, the gradients for each parameter on a single sample are:

Gradient of wj=δ⋅xj\text{Gradient of } w_j = \delta \cdot x_j Gradient of b=δ⋅1=δ\text{Gradient of } b = \delta \cdot 1 = \delta

What is a Training Epoch?

An epoch is one complete pass through the entire training dataset:

  1. For every sample in the batch, compute the forward pass, evaluate loss, and accumulate gradients.
  2. After iterating through all samples, compute the mean gradient by dividing by total samples NN.
  3. Update weights and bias using the gradient descent rule implemented in the previous stage.

Your Objective in this Stage

When standard input contains "op": "train-epoch", process:

  • learning_rate: learning rate α\alpha
  • weights: current weights list [w1,…,wd][w_1, \dots, w_d]
  • bias: current bias bb
  • samples: list of training samples formatted as {"x": [x_1, ..., x_d], "y": y_target}

Step-by-Step Algorithm

  1. Initialize total_loss = 0.0, grad_w = [0.0] * d, grad_b = 0.0.
  2. For each sample s in samples:
    • Compute z=∑j=1d(wj⋅s.xj)+bz = \sum_{j=1}^{d} (w_j \cdot s.x_j) + b.
    • Compute y^=11+e−z\hat{y} = \frac{1}{1 + e^{-z}}.
    • Add sample loss to total_loss: 0.5×(y^−s.y)20.5 \times (\hat{y} - s.y)^2.
    • Compute delta: δ=(y^−s.y)×y^×(1.0−y^)\delta = (\hat{y} - s.y) \times \hat{y} \times (1.0 - \hat{y}).
    • For each feature jj: accumulate δ×s.xj\delta \times s.x_j into grad_w[j].
    • Accumulate δ\delta into grad_b.
  3. Compute batch averages dividing by N=len(samples)N = \text{len}(samples):
    • mean_loss = total_loss / N
    • grad_w_mean[j] = grad_w[j] / N
    • grad_b_mean = grad_b / N
  4. Update parameters:
    • wj′=wj−α⋅grad_w_mean[j]w_j' = w_j - \alpha \cdot \text{grad\_w\_mean}[j]
    • b′=b−α⋅grad_b_meanb' = b - \alpha \cdot \text{grad\_b\_mean}

Implementation direction

Separate gradient accumulation from parameter updates. Check intermediate values against the numerical example. Keep training and inference distinct; write the loops yourself and reuse the earlier functions.

Validation and Output Rules

  1. If samples is empty or any sample vector dimension differs from weights, write error to stderr and exit with code 1.
  2. Round all floats to 4 decimal places with round_value / roundValue (serialize integers or zero without decimal points, e.g. 0).
  3. Output compact JSON sorted alphabetically by key ("bias", "loss", "weights"):
    json
    { "bias": 0, "loss": 0.125, "weights": [0.0063, -0.0063] }
    

Training uses half the previous stage’s MSE: 0.5 * mean((prediction - target)^2). The factor 0.5 cancels the derivative’s factor 2. Keep full precision during computation and round only the emitted results. Do not reuse rounded parameters when training multiple epochs.

Output and errors

Multiline examples are formatted for readability. Emit compact JSON without optional spaces, with properties in the order shown by the output examples, and a final newline. Keep logs off stdout. Tests compare exact text. Invalid inputs use stderr containing error, empty stdout, and exit 1. Valid inputs exit 0. Serialize integer results as JSON integers; neural-network uses the stage’s four-decimal rounding rule.