A Neural Net From Scratch
This post is an example of regression, where we will use supervised learning (a model that returns a function after giving it an input and output). The goal is to find the temperature conversion equation. The complete code is at the end of the article.
First, we import NumPy, then defining the training set x_train and y_train. x_train is the independent variable representing temperature in Celsius, while y_train is the dependent variable representing the temperature in Fahrenheit.
x_train is divided by zero because large numbers are not stable; try to run the model without it for 100 epochs, and the loss function will return inf.
import numpy as npx_train = np.array([[-40.], [-30], [-20], [-10], [0], [10], [20], [30], [40]])/100y_train = np.array([31.28, 31.46, 31.64, 31.82, 32., 32.18, 32.36, 32.54, 32.72])
The code has five functions: forward, backward, loss, step, and model.
Function 1: forward()
The function returns the multiplication of the parameter weight with the dependent variable and adding the parameter bias.
def forward(w, x, b): return np.float(w*x+b)
Function 2: backward()
This function calculates the gradients; it guides the model in the right direction.
def backward(x, y, pred, dw, db):
dw = dw + (-2)*x*(y-pred)
db = db + (-2)*(y-pred)
return dw, db
Know how did we come up with this equation?
It’s the partial derivative of the loss function, more on calculus in this article:
Function 3: loss()
This example uses Mean Squared Error (MSE) as the loss function.
def loss(y, pred): return np.array(np.square(y-pred).mean())
Function 4: step()
The optimizer step uses the learning rate and gradients to update the parameters.
def step(w, b, dw, db, lr=.01):
w = w - lr * dw
b = b - lr * db
return w, b
Function 5: model()
The model calls all the functions above since we have a single neuron with two parameters, a weight, and a bias.
At the nested loop, we zero the gradients because every epoch is a fresh start. Then we call the forward and the backward function, followed by printing the loss and updating the parameters. Code completed!
def model(x, y, epoch=10):
m = x.shape[0]
w = 1.
b = 0 for i in range(epoch):
print(f'----------epoch: {i}-------') dw, db = 0, 0
for j in range(m):
pred = forward(w, x[j], b)
dw, db = backward(x[j], y[j], pred,dw, db) print(f'loss: {loss(y, pred)}') w, b = step(w,b,dw,db, lr=.1) return w, b
After running the code for 50 epochs, the loss function got minimized.
>>> w, b = model(x_train, y_train, 50)
----------epoch: 0-------
loss: 998.7760000000001
----------epoch: 1-------
loss: 678.2142745600001
----------epoch: 2-------
loss: 400.5283809648645
...
----------epoch: 47-------
loss: 0.7345516170916814
----------epoch: 48-------
loss: 0.7323773915041649
----------epoch: 49-------
loss: 0.7343447652825804
The result, w =~1.8 and b =~32, the constants we were after.
>>> print(w[0], b)
1.7986596334086586 31.999543280738333
Now to convert from Celsius to Fahrenheit, you can reuse the forward function as a calculator, for example:
>>> forward(w, 100, b)
211.8655066216042
Now, is the correct way to solve a linear function, no. But, It is a simple exercise for beginners. I hope that helps.