Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Pizzera Web App

notebooks/pizzeria_app.livemd

Pizzera Web App

# Don't forget to start pizzeria
# iex --sname pizzeria --cookie pizzeriacookie -S mix phx.server

alias VegaLite, as: Vl
require Explorer.DataFrame, as: DF
require Explorer.Series, as: S
:ok

Load The Data

Let’s load our logarithmic dataset (you can also load a linear() version).

alias Pizzeria.Migrations.Seeds
# Seeds.linear()
Seeds.logarithmic()

Training The Data

We have, for example 31 days of data (Jan 1 - 31). We will split our data into a training set into two, the most often used splits are 80/20, 90/10, and 67/33. We will pick 80/20.

alias Pizzeria.Ml.Engine
traindata = Engine.dataset({~D[2023-01-01], ~D[2023-09-30]})
testdata = Engine.dataset({~D[2023-10-01], ~D[2023-12-31]})

Visualize the Data

{x, y} = traindata
df = DF.new(x: x, y: y)

{testx, testy} = testdata
traindf = DF.new(x: testx, y: testy)

Vl.new(
  title: [text: "Pizza Dough Approximation"],
  width: 630,
  height: 630
)
|> Vl.data_from_values(df)
|> Vl.mark(:circle, color: :blue, size: 100)
|> Vl.encode_field(:x, "x", type: :quantitative, axis: [grid: false])
|> Vl.encode_field(:y, "y", type: :quantitative, axis: [grid: false])

Train The Data

# Let's try linear regression
alias Scholar.Linear.LinearRegression, as: LR
{features, labels} = traindata
model = LR.fit(features, labels)

Visualize The Model

{x, y} = traindata
df = DF.new(x: x, y: y)

{testx, testy} = testdata
traindf = DF.new(x: testx, y: testy)

[intercept] = Nx.to_flat_list(model.intercept)
[coefficients] = Nx.to_flat_list(model.coefficients)
x_1 = 0
x_2 = 60

line = %{
  x: [x_1, x_2],
  y: [x_1 * coefficients + intercept, x_2 * coefficients + intercept]
}

Vl.new(
  title: [text: "Pizza Dough Approximation"],
  width: 630,
  height: 630
)
|> Vl.layers([
  Vl.new()
  |> Vl.data_from_values(df)
  |> Vl.mark(:circle, color: :blue)
  |> Vl.encode_field(:x, "x", type: :quantitative, axis: [grid: false])
  |> Vl.encode_field(:y, "y", type: :quantitative, axis: [grid: false]),
  Vl.new()
  |> Vl.data_from_values(traindf)
  |> Vl.mark(:square, color: :red)
  |> Vl.encode_field(:x, "x", type: :quantitative, axis: [grid: false])
  |> Vl.encode_field(:y, "y", type: :quantitative, axis: [grid: false]),
  Vl.new()
  |> Vl.data_from_values(line)
  |> Vl.mark(:line, color: :green, weight: 2)
  |> Vl.encode_field(:x, "x", type: :quantitative, axis: [grid: false])
  |> Vl.encode_field(:y, "y", type: :quantitative, axis: [grid: false])
])

Evaluate The Model

Using Mean Average Error, we can see how well the model fits.

alias Scholar.Linear.LinearRegression, as: LR
{features, labels} = testdata

model
|> LR.predict(features)
|> Nx.subtract(labels)
|> Nx.abs()
|> Nx.mean()

Tweak the model

alias Scholar.Linear.PolynomialRegression, as: PR
{features, labels} = traindata
model = PR.fit(features, labels)

Let’s see if a polynominal regression fits nicer.

{x, y} = traindata
df = DF.new(x: x, y: y)

{testx, testy} = testdata
traindf = DF.new(x: testx, y: testy)

polyx = Enum.to_list(1..60)

polyy =
  polyx
  |> Enum.map(&Nx.tensor([[&1]]))
  |> Enum.map(&(PR.predict(model, &1) |> Nx.to_flat_list() |> List.first()))

Vl.new(
  title: [text: "Pizza Dough Approximation"],
  width: 630,
  height: 630
)
|> Vl.layers([
  Vl.new()
  |> Vl.data_from_values(df)
  |> Vl.mark(:circle, color: :blue)
  |> Vl.encode_field(:x, "x", type: :quantitative, axis: [grid: false])
  |> Vl.encode_field(:y, "y", type: :quantitative, axis: [grid: false]),
  Vl.new()
  |> Vl.data_from_values(traindf)
  |> Vl.mark(:square, color: :red)
  |> Vl.encode_field(:x, "x", type: :quantitative, axis: [grid: false])
  |> Vl.encode_field(:y, "y", type: :quantitative, axis: [grid: false]),
  Vl.new()
  |> Vl.data_from_values(line)
  |> Vl.mark(:line, color: :lightgrey, weight: 1)
  |> Vl.encode_field(:x, "x", type: :quantitative, axis: [grid: false])
  |> Vl.encode_field(:y, "y", type: :quantitative, axis: [grid: false]),
  Vl.new()
  |> Vl.data_from_values(%{x: polyx, y: polyy})
  |> Vl.mark(:line, color: :green, weight: 2)
  |> Vl.encode_field(:x, "x", type: :quantitative, axis: [grid: false])
  |> Vl.encode_field(:y, "y", type: :quantitative, axis: [grid: false])
])

Re-evaluate The Model

{features, labels} = testdata

model
|> PR.predict(features)
|> Nx.subtract(labels)
|> Nx.abs()
|> Nx.mean()