Post

Linear Regression From Scratch

Linear regression (single variable) begins with this:

  • We have a feature variable (X) and a target variable (y) that have a linear relationship.

Linear relationship between X and y [https://statistics.laerd.com/spss-tutorials/linear-regression-using-spss-statistics.php]

  • Imagine the dataset is the transaction history of a freelance developer. Your bread and butter is machine learning, not freelance web dev, so you just want to pay someone to get the job done.
  • Our goal is to “LEARN” where to put that red line, such that when we ingest a new X data point, we can predict y with minimal error. In other words, when we know how much time it will take, we can predict how much the job will cost.
  • We hope to have a hypothesis/prediction equation that looks like this: \(\hat{y} = wx + b\) \(\text{where } w = weight, b = bias\)
  • given x, number of hours, we predict y, the cost of the project

Our cost function is as follows: \(MSE = \frac{1}{N} \sum_{i=1}^n (y_i - (wx_i+b))^2 \\ J(w, b) = \frac{1}{N} \sum_{i=1}^n (y_i - (wx_i+b))^2\)

Looking at it closely:

  • $(y_i - (wx_i+b))$ is the difference between the true $y$ value at the $i^{th}$ spot and the predicted value (given the weight and bias).
  • This value is summed up across the $n$ datapoints and then divided by the number of datapoints, hence “mean square error”
  • We want to minimize that Mean Square Error so that our predicted linear regression line is as close as possible to the real values

Now we need a search algorithm that can find $w,b$ such that $J$ is minimized. We can use gradient descent. Gradient descent chooses initial values for $w$ and $b$ and then repeatedly performs an update until the cost funciton is minimized. The reason why it is called gradient descent, is because we take the gradient of $J$ and then use the corresponding derivatives in the update rule. I’ll update this with the more explanation on gradient descent, partial derivatives, and whatever else to make it more thorough.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def predict(x, weight, bias):
    return (weight*x + b)

def mse_cost(x, y, weight, bias):
    error = 0.0
    for i in range(len(x)):
        error += (y[i] - (weight*x[i]+bias))**2
    return error/len(x)

def grad_desc_update(x, y, weight, bias, alpha):
    d_weight = 0.0 # initialize at 0
    d_bias = 0.0 # initialize at 0
    
    for i in range(len(x)):
        d_weight += -2*x[i] * (y[i] - (weight*x[i] + bias))
        d_bias += -2*(x[i] - (weight*x[i] + bias))
        
    weight -= (d_weight / len(x)) * alpha
    bias -= (d_bias / len(x)) * alpha

    return weight, bias

def train(x, y, weight, bias, alpha, iterations):

    for i in range(iterations):
        weight,bias = grad_desc_update(x, y, weight, bias, alpha)

        cost = mse_cost(x, y, weight, bias)

        if i % 10 == 0:
            print("iteration: {}, cost: {}".format(i, cost))

    return weight, bias

Let’s generate some fake data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def generate_linear_data(x):
    m = 0.014
    b = 0.52
    y = [x_i*m+b+random.random() for x_i in x]
    return np.array(y)
    
    
x = list()
for i in range(100):
    x.append(random.randint(0, 100))

x = np.array(x)
 
y = generate_linear_data(x)

Then try for yourself:

1
weight, bias = train(x, y, weight=0.0, bias=0.0, alpha=0.001, iterations=100)
This post is licensed under CC BY 4.0 by the author.