Linear Regression Example
Let's try using Values to fit a line to data, using gradient descent.
First, we'll make up some fake data of two things with an assumed-linear relationship: height and basketball-skills. We'll define the true slope and intercept of the relationship as $m=2$ and $b=10$.
using Random
Random.seed!(1234)
# seeding the random number generator for reproducibility
heights = Float64[]
for count in 1:79
push!(heights, rand(40:90))
end
# TRUE PARAMS y = m*x + b
m = 2
b = 10
skills = Float64[]
for height in heights
skill = m * height + b + randn() * 7.0
push!(skills, skill)
end
Just for fun, I'll add myself to this dataset. I'm 72 inches tall, and extremely bad at basketball lol
push!(heights, 72)
push!(skills, 75)
Now let's plot the data just to take a look at it:
using Plots
scatter(heights, skills, legend=false, markersize=3, color=:black, xlabel="Height (inches)", ylabel="Basketball Skills",dpi=300)
Ok, now let's see if we can use Values to fit a line to this data. First, we'll cast all of the height and skill measurements we have to Values
heights = [Value(item) for item in heights]
skills = [Value(item) for item in skills]
Next we'll set two hyper-parameters. We'll set the learning rate lr = 0.000002
and the number of iterations of the fitting to runs = 100000
.
lr = 0.000002
runs = 100000
Now we'll define our initial guesses:
m_guess = Value(0)
b_guess = Value(0)
Now we can actually fit the parameters to the data. We'll do this with runs = 100000
iterations, updating the parameters each time. For each iteration, we start by reseting the m_guess
and b_guess
gradients to 0. Then, we set global loss = Value(0)
. We then iterate through our heights
array, and calculate the predicted skills using our current parameters as: skill_pred = heights[i] * m_guess + b_guess
. We then compare the predicted skill level to the actual skill level for that height and compute the squared error between them: loss_to_add = (skill_pred - skills[i])^2
. Then, we add that amount to the total loss: global loss += loss_to_add
. All of these calculations are valid operations for our Values, so at this point we can call backward(loss)
to calculate the gradients of the parameters. We then update the parameters in the opposite direction of the gradients, scaled by the learning rate. Here's the entire block of code:
for run in 1:runs
# reset the grads to 0 for each iteration
m_guess.grad = 0
b_guess.grad = 0
# set loss to 0
global loss = Value(0)
for i in 1:length(heights)
# predicted skill level, using our parameters
skill_pred = heights[i] * m_guess + b_guess
# squared error compared to actual skill level
loss_to_add = (skill_pred - skills[i])^2
global loss += loss_to_add
end
# backward pass to calculate the gradients
backward(loss)
# updating parameters
m_guess.data -= m_guess.grad * lr
b_guess.data -= b_guess.grad * lr
end
Let's see where our guesses for m and b are at now.
println(m_guess)
# output: Value(1.9906384976156302)
println(b_guess)
# output: Value(10.133894222774007)
Pretty close to the real values that we originally used to make the data! We can also plot the fit line with these m_guess
and b_guess
parameters:
heights_data = [item.data for item in heights] # remember heights is full of Values, so need to do this to get the numbers
x_line = minimum(heights_data):maximum(heights_data)
y_line = m_guess.data * x_line .+ b_guess.data
plot!(x_line, y_line, linewidth=2, color=:blue)