Skip to content

Commit 8d6f715

Browse files
committed
add gradient descent calculation
1 parent 4d632c9 commit 8d6f715

5 files changed

Lines changed: 127 additions & 62 deletions

File tree

lib/learn_kit/math.ex

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,38 @@ defmodule LearnKit.Math do
130130
[firsts | swap_rows_cols(others)]
131131
end
132132

133+
@doc """
134+
Scalar multiplication
135+
136+
## Examples
137+
138+
iex> LearnKit.Math.scalar_multiply(10, [5, 6])
139+
[50, 60]
140+
141+
"""
142+
@spec scalar_multiply(integer, list) :: list
143+
144+
def scalar_multiply(multiplicator, list) when is_list(list) do
145+
list
146+
|> Enum.map(fn x -> x * multiplicator end)
147+
end
148+
149+
@doc """
150+
Vector subtraction
151+
152+
## Examples
153+
154+
iex> LearnKit.Math.vector_subtraction([40, 50, 60], [35, 5, 40])
155+
[5, 45, 20]
156+
157+
"""
158+
@spec vector_subtraction(list, list) :: list
159+
160+
def vector_subtraction(x, y) when is_list(x) and is_list(y) and length(x) == length(y) do
161+
Enum.zip(x, y)
162+
|> Enum.map(fn {xi, yi} -> xi - yi end)
163+
end
164+
133165
@doc """
134166
Division for 2 elements
135167

lib/learn_kit/regression/linear.ex

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ defmodule LearnKit.Regression.Linear do
77

88
alias LearnKit.Regression.Linear
99

10-
use Linear.Fit
11-
use Linear.Predict
10+
use Linear.Calculations
1211

1312
@type factors :: [number]
1413
@type results :: [number]
@@ -72,7 +71,7 @@ defmodule LearnKit.Regression.Linear do
7271
7372
iex> predictor = predictor |> LearnKit.Regression.Linear.fit([method: "gradient descent"])
7473
%LearnKit.Regression.Linear{
75-
coefficients: [-1.5, 4.0],
74+
coefficients: [-1.4975720508482548, 3.9992148848913356],
7675
factors: [1, 2, 3, 4],
7776
results: [3, 6, 10, 15]
7877
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
defmodule LearnKit.Regression.Linear.Calculations do
2+
@moduledoc """
3+
Module for fit functions
4+
"""
5+
6+
alias LearnKit.Math
7+
8+
defmacro __using__(_opts) do
9+
quote do
10+
defp fit_data(method, factors, results) when method == "gradient descent" do
11+
gradient_descent_iteration([:rand.uniform, :rand.uniform], 0.0001, nil, 1000000, Enum.zip(factors, results), 0)
12+
end
13+
14+
defp fit_data(_, factors, results) do
15+
beta = Math.correlation(factors, results) * Math.standard_deviation(results) / Math.standard_deviation(factors)
16+
alpha = Math.mean(results) - beta * Math.mean(factors)
17+
[alpha, beta]
18+
end
19+
20+
defp predict_sample(sample, [alpha, beta]) do
21+
sample * beta + alpha
22+
end
23+
24+
defp calculate_score([], _, _), do: raise("There was no fit for model")
25+
26+
defp calculate_score(coefficients, factors, results) do
27+
1.0 - sum_of_squared_errors(coefficients, factors, results) / total_sum_of_squares(results)
28+
end
29+
30+
defp total_sum_of_squares(list) do
31+
mean_list = Math.mean(list)
32+
list
33+
|> Enum.map(fn x -> :math.pow(x - mean_list, 2) end)
34+
|> Enum.sum
35+
end
36+
37+
defp sum_of_squared_errors(coefficients, factors, results) do
38+
Enum.zip(factors, results)
39+
|> Enum.map(fn {xi, yi} -> squared_prediction_error(coefficients, xi, yi) end)
40+
|> Enum.sum
41+
end
42+
43+
defp squared_prediction_error(coefficients, x, y) do
44+
coefficients
45+
|> prediction_error(x, y)
46+
|> :math.pow(2)
47+
end
48+
49+
defp squared_error_gradient(coefficients, x, y) do
50+
error_variable = coefficients |> prediction_error(x, y)
51+
[
52+
-2 * error_variable,
53+
-2 * error_variable * x
54+
]
55+
end
56+
57+
defp prediction_error(coefficients, x, y) do
58+
y - predict_sample(x, coefficients)
59+
end
60+
61+
defp gradient_descent_iteration(_, _, min_theta, _, _, iterations_with_no_improvement) when iterations_with_no_improvement >= 100, do: min_theta
62+
63+
defp gradient_descent_iteration(theta, alpha, min_theta, min_value, data, iterations_with_no_improvement) do
64+
[
65+
min_theta,
66+
min_value,
67+
iterations_with_no_improvement,
68+
alpha
69+
] = data |> check_value(min_value, theta, min_theta, iterations_with_no_improvement, alpha)
70+
71+
theta = data
72+
|> Enum.shuffle
73+
|> Enum.reduce(theta, fn {xi, yi}, acc ->
74+
gradient_i = squared_error_gradient(acc, xi, yi)
75+
acc |> Math.vector_subtraction(alpha |> Math.scalar_multiply(gradient_i))
76+
end)
77+
gradient_descent_iteration(theta, alpha, min_theta, min_value, data, iterations_with_no_improvement)
78+
end
79+
80+
defp check_value(data, min_value, theta, min_theta, iterations_with_no_improvement, alpha) do
81+
value = data
82+
|> Enum.map(fn {xi, yi} -> squared_prediction_error(theta, xi, yi) end)
83+
|> Enum.sum
84+
cond do
85+
value < min_value ->
86+
[theta, value, 0, 0.0001]
87+
true ->
88+
[min_theta, min_value, iterations_with_no_improvement + 1, alpha * 0.9]
89+
end
90+
end
91+
end
92+
end
93+
end

lib/learn_kit/regression/linear/fit.ex

Lines changed: 0 additions & 21 deletions
This file was deleted.

lib/learn_kit/regression/linear/predict.ex

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)