Skip to content

Instantly share code, notes, and snippets.

@joshnuss
Last active July 25, 2019 04:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshnuss/353abbd564167e69eecdadd9d590f9e6 to your computer and use it in GitHub Desktop.
Save joshnuss/353abbd564167e69eecdadd9d590f9e6 to your computer and use it in GitHub Desktop.
Trying to predict number of pizzas that will be ordered based on reservations
# Generate .gif with `convert -delay 2 -loop 0 output/*.svg animated.gif`
defmodule Parser do
import :erlang, only: [binary_to_integer: 1]
def parse(path) do
File.stream!(path)
|> Stream.drop(1)
|> Stream.map(&String.trim/1)
|> Stream.map(&String.split(&1, " ", trim: true))
|> Stream.map(fn [x, y] ->
{
binary_to_integer(x),
binary_to_integer(y)
}
end)
|> Enum.to_list()
end
end
defmodule SVG do
def export(index, data, w, b) do
filename = index |> to_string |> String.pad_leading(5, "0")
path = "output/#{filename}.svg"
height = 50
width = 50
points =
Enum.map(data, fn {x, y} ->
~s|<circle cx="#{x}" cy="#{height - y}" r="1" fill="cadetblue"/>|
end)
data = """
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 200 200"
>
<g transform="scale(4)">
<rect fill="white" x="0" y="0" width="#{width}px" height="#{height}px"/>
<g stroke="coral" stroke-width="2" stroke-linecap="round">
<line x1="0" y1="#{height - b}" x2="#{width}" y2="#{height - (w * width + b)}" />
</g>
<text x="20" y="35" fill="neon">#{index}</text>
#{points}
</g>
</svg>
"""
File.write(path, data)
end
end
defmodule LinearRegression do
def predict(x, w, b) do
x * w + b
end
def loss(data, w, b) do
data
|> Enum.map(&predict(elem(&1, 0), w, b))
|> Enum.zip(data)
|> Enum.map(&error_rate/1)
|> average()
end
def train(data, opts \\ []) do
iterations = Keyword.get(opts, :iterations, 10_000)
learning_rate = Keyword.get(opts, :learning_rate, 0.1)
Enum.reduce_while(1..iterations, {0, 0}, &iterate(data, &1, &2, learning_rate))
end
defp iterate(data, i, {w, b}, learning_rate) do
current_loss = loss(data, w, b)
IO.puts("Iterations #{i} => Loss: #{current_loss}")
SVG.export(i, data, w, b)
cond do
loss(data, w + learning_rate, b) < current_loss ->
{:cont, {w + learning_rate, b}}
loss(data, w - learning_rate, b) < current_loss ->
{:cont, {w - learning_rate, b}}
loss(data, w, b + learning_rate) < current_loss ->
{:cont, {w, b + learning_rate}}
loss(data, w, b - learning_rate) < current_loss ->
{:cont, {w, b - learning_rate}}
true ->
{:halt, {w, b}}
end
end
defp average(list) do
Enum.sum(list) / length(list)
end
defp error_rate({prediction, {_x, y}}) do
:math.pow(prediction - y, 2)
end
end
{w, b} =
"pizza.txt"
|> Parser.parse()
|> LinearRegression.train(iterations: 100_000, learning_rate: 0.01)
IO.puts("weight=#{w}, bias=#{b}")
IO.puts("Prediction: x=#{20}, y=#{LinearRegression.predict(20, w, b)}")
Reservations Pizzas
13 33
2 16
14 32
23 51
13 27
1 16
18 34
10 17
26 29
3 15
3 15
21 32
7 22
22 37
2 13
27 44
6 16
10 21
18 37
15 30
9 26
26 34
8 23
15 39
10 27
21 37
5 17
6 18
13 25
13 23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment