Posts

Simple back propagation in Pure Python

Back propagation in Pure Python

Back propagation is a important concept in machine learning. It is a method to train a neural network. In this article, I will show you how to implement a simple back propagation in Pure Python.

Back propagation concept

First, we need to calculate the error of the neural network. The error is the difference between the predicted value and the actual value. Then, we need to update the weights of the neural network. The weights are updated by the error and the learning rate.

For example, let's say we have a neural network with one neuron and one input. The neural network is defined as follows:

Simple neural network

This has x as input, w as weight and out as output.

Forward pass

Forward pass is the process of calculating the output of the neural network. In this case, the output is wx.

Error calculation

Error calculation is the process of calculating the difference between the predicted value and the actual value. In this example, we use Mean Squared Error as the error function. (For every input, we calculate the error and take the average.)

Error Calculation:
err=(1N)(out-y)2
Total Error:
err=iN(1N)(outi-yi)2

Gradient Descent Approach

Gradient Descent is an optimization algorithm used to minimize some function by iteratively moving in the direction of steepest descent as defined by the negative of the gradient. - ChatGPT

The new weight is calculated as follows:


(w)=w-α×(err)/(w)

Where α is the learning rate and w is the updated weight.

Implementation

Defining the neural network

X: list[float] = [1, 2, 3, 4] # Input
Y: list[float] = [2, 4, 6, 8] # Expected output

w = 0.0              # Arbitrary initial weight
learning_rate = 0.01 # Learning rate
n_iters = 10         # Number of iterations

In this example, our forward pass is w×x.

def forward(x):
    if not isinstance(x, list):
        x = [x]
    return w * x

Loss Calculation (MSE Error)


LOSS=(1N)iN(ypi-yi)2
def loss(y, y_pred):
    return sum((yp-yi)**2 for yp, yi in zip(y_pred, y)) / len(y)

Gradient Calculation


(err)/(w)=((wx-y)2N)/w=2x(wx-y)N=2x(yp-y)N
def gradient(x, y, y_pred):
    return sum(2 * (yp - yi) * xi for xi, yp, yi in zip(x, y_pred, y)) / len(y)

Training

for epoch in range(n_iters):
    y_pred = forward(X)

    # loss
    l = loss(Y, y_pred)

    # gradient
    dw = gradient(X, Y, y_pred)

    # update weight
    w -= learning_rate * dw

    print(f"epoch {epoch + 1}: w = {w:.3f}, loss = {l:.8f}")

print(f"Prediction after training: f(5) = {forward(5)[0]:.3f}")

Output

Prediction before training: f(5) = 0.0
epoch 1: w = 0.300, loss = 30.00000000
epoch 2: w = 0.555, loss = 21.67500000
epoch 3: w = 0.772, loss = 15.66018750
epoch 4: w = 0.956, loss = 11.31448547
epoch 5: w = 1.113, loss = 8.17471575
epoch 6: w = 1.246, loss = 5.90623213
epoch 7: w = 1.359, loss = 4.26725271
epoch 8: w = 1.455, loss = 3.08309009
epoch 9: w = 1.537, loss = 2.22753259
epoch 10: w = 1.606, loss = 1.60939229
Prediction after training: f(5) = 8.031