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.
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 ), which rotates an internal gear (linear combination ), which opens a pressure valve (sigmoid activation ), which affects the final reading on a gauge (loss ). 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:
Breaking Down Each Link Step by Step:
- Link 1 (How loss changes with respect to prediction): For an individual squared loss , its derivative is:
- Link 2 (How sigmoid activation changes with respect to ): One of the most elegant mathematical properties of the sigmoid function is that its derivative can be expressed directly in terms of its output:
- Link 3 (How the affine combination changes with respect to weight or bias ): Since :
Multiplying the first two links yields the local sample error term :
Consequently, the gradients for each parameter on a single sample are:
What is a Training Epoch?
An epoch is one complete pass through the entire training dataset:
- For every sample in the batch, compute the forward pass, evaluate loss, and accumulate gradients.
- After iterating through all samples, compute the mean gradient by dividing by total samples .
- 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 rateweights: current weights listbias: current biassamples: list of training samples formatted as{"x": [x_1, ..., x_d], "y": y_target}
Step-by-Step Algorithm
- Initialize
total_loss = 0.0,grad_w = [0.0] * d,grad_b = 0.0. - For each sample
sinsamples:- Compute .
- Compute .
- Add sample loss to
total_loss: . - Compute delta: .
- For each feature : accumulate into
grad_w[j]. - Accumulate into
grad_b.
- Compute batch averages dividing by :
mean_loss = total_loss / Ngrad_w_mean[j] = grad_w[j] / Ngrad_b_mean = grad_b / N
- Update parameters:
Practical Implementation and Code Advice
- Python Pattern:
n = len(samples) d = len(weights) total_loss = 0.0 grad_w = [0.0] * d grad_b = 0.0 for s in samples: x, y = s["x"], s["y"] z = sum(xi * wi for xi, wi in zip(x, weights)) + bias y_hat = 1.0 / (1.0 + math.exp(-z)) total_loss += 0.5 * ((y_hat - y) ** 2) delta = (y_hat - y) * y_hat * (1.0 - y_hat) for j in range(d): grad_w[j] += delta * x[j] grad_b += delta new_w = [round_value(weights[j] - lr * (grad_w[j] / n)) for j in range(d)] new_b = round_value(bias - lr * (grad_b / n)) mean_loss = round_value(total_loss / n) return { "bias": new_b, "loss": mean_loss, "weights": new_w } - TypeScript Pattern:
const n = samples.length; const d = weights.length; let totalLoss = 0; const gradW = new Array(d).fill(0); let gradB = 0; for (const s of samples) { let z = bias; for (let j = 0; j < d; j++) z += s.x[j] * weights[j]; const yHat = 1 / (1 + Math.exp(-z)); totalLoss += 0.5 * (yHat - s.y) ** 2; const delta = (yHat - s.y) * yHat * (1 - yHat); for (let j = 0; j < d; j++) gradW[j] += delta * s.x[j]; gradB += delta; } const newW = weights.map((w, j) => roundValue(w - lr * (gradW[j] / n))); const newB = roundValue(bias - lr * (gradB / n)); const meanLoss = roundValue(totalLoss / n); return { bias: newB, loss: meanLoss, weights: newW, };
Validation and Output Rules
- If
samplesis empty or any sample vector dimension differs fromweights, writeerrortostderrand exit with code1. - Round all floats to 4 decimal places with
round_value/roundValue(serialize integers or zero without decimal points, e.g.0). - Output compact JSON sorted alphabetically by key (
"bias","loss","weights"):{ "bias": 0, "loss": 0.125, "weights": [0.0063, -0.0063] }