Powered by AppSignal & Oban Pro

Ch 3: ML Math

Ch3 - MLmath.livemd

Ch 3: ML Math

Mix.install([
  {:nx, "~> 0.5"},
  {:exla, "~> 0.5"},
  {:kino, "~> 0.8"},
  {:stb_image, "~> 0.6"},
  {:vega_lite, "~> 0.1"},
  {:kino_vega_lite, "~> 0.1"}
])

Nx.default_backend(EXLA.Backend)

Linear Algebra

# Vector representation
# is just single dimension tensor

a = Nx.tensor([1, 2, 3])
b = Nx.tensor([4.0, 5.0, 6.0])
c = Nx.tensor([1, 0, 1], type: {:u, 8})
IO.inspect(a, label: :a)
IO.inspect(b, label: :b)
IO.inspect(c, label: :c)
# Scalar
# Use tensor or just leave as number. Number easier to read.

i_am_a_scalar = Nx.tensor(5)
|> IO.inspect
i_am_also_a_scalar = 5
|> IO.inspect
goog_current_price = 2677.32
goog_pe = 23.86
goog_mkt_cap = 1760

meta_current_price = 133.93
meta_pe = 11.10
meta_mkt_cap = 360

stocks_matrix =
  Nx.tensor([
    [goog_current_price, goog_pe, goog_mkt_cap],
    [meta_current_price, meta_pe, meta_mkt_cap]
  ])
  |> IO.inspect()
# Nx.add -- element wise sum

sales_day_1 = Nx.tensor([32, 10, 14])
sales_day_2 = Nx.tensor([10, 24, 21])
total_sales = Nx.add(sales_day_1, sales_day_2)
# Nx.multiply -- Scalar multiplication

sales_day_1 = Nx.tensor([32, 10, 14])
sales_day_2 = Nx.tensor([10, 24, 21])
total_sales = Nx.add(sales_day_1, sales_day_2)

keep_rate = 0.9
unreturned_sales = Nx.multiply(keep_rate, total_sales)
# Nx.transpose -- Transpose matrix

sales_matrix =
  Nx.tensor([
    [32, 10, 14],
    [10, 24, 21]
  ])

Nx.transpose(sales_matrix)

# NB on vector this is identity function because 
# Nx has no differentiation between row and col vector
vector = Nx.tensor([1, 2, 3])
Nx.transpose(vector)

# If you need to have a dimension you can explicitly create one
Nx.tensor([[1, 2, 3]])
Nx.tensor([[1], [2], [3]])
# Matrix multiplication -- Nx.dot

vector = Nx.dot(Nx.tensor([1, 2, 3]), Nx.tensor([1, 2, 3]))
vector_matrix = Nx.dot(Nx.tensor([1, 2]), Nx.tensor([[1], [2]]))
matrix_matrix = Nx.dot(Nx.tensor([[1, 2]]), Nx.tensor([[3], [4]]))

vector |> IO.inspect(label: :vector)
vector_matrix |> IO.inspect(label: :vector_matrix)
matrix_matrix |> IO.inspect(label: :matrix_matrix)
a = Nx.tensor([[1, 2], [3,4]])
|> IO.inspect()
b = Nx.tensor([[1, 3], [2,4]])
|> IO.inspect()
# matrix multiplication
dot = Nx.dot(a, b)
|> IO.inspect()
# element wise
mult = Nx.multiply(a, b)
|> IO.inspect()

Probability

simulation = fn key ->
  {value, key} = Nx.Random.uniform(key)
  if Nx.to_number(value) < 0.5, do: {0, key}, else: {1, key}
end
key = Nx.Random.key(42)

for n <- [10, 100, 1000, 10000] do
  Enum.map_reduce(1..n, key, fn
    _, key -> simulation.(key)
  end)
  |> elem(0)
  |> Enum.sum()
  |> IO.inspect()
end

Derivatives and Vega plotting

defmodule BerryFarm do
  import Nx.Defn

  # defn profits(trees) do
  #   trees
  #   |> Nx.subtract(1)
  #   |> Nx.pow(4)
  #   |> Nx.negate()
  #   |> Nx.add(Nx.pow(trees, 3))
  #   |> Nx.add(Nx.pow(trees, 2))
  # end

  defn profits(trees) do
    -((trees - 1) ** 4) + trees ** 3 + trees ** 2
  end

  defn profits_derivative(trees) do
    grad(trees, &amp;profits/1)
  end
end
trees = Nx.linspace(0, 4, n: 100)
profits = BerryFarm.profits(trees)

alias VegaLite, as: Vl

Vl.new(title: "Berry Profits", width: 600, height: 600)
|> Vl.data_from_values(%{
  trees: Nx.to_flat_list(trees),
  profits: Nx.to_flat_list(profits)
})
|> Vl.mark(:line, interpolate: :basis)
|> Vl.encode_field(:x, "trees", type: :quantitative)
|> Vl.encode_field(:y, "profits", type: :quantitative)

trees = Nx.linspace(0, 3, n: 100)
profits = BerryFarm.profits(trees)
profits_derivative = BerryFarm.profits_derivative(trees)

title = "Berry Profits and Profits Rate of Change"

Vl.new(title: title, width: 600, height: 600)
|> Vl.data_from_values(%{
  trees: Nx.to_flat_list(trees),
  profits: Nx.to_flat_list(profits),
  profits_derivative: Nx.to_flat_list(profits_derivative)
})
|> Vl.layers([
  Vl.new()
  |> Vl.mark(:line, interpolate: :basis)
  |> Vl.encode_field(:x, "trees", type: :quantitative)
  |> Vl.encode_field(:y, "profits", type: :quantitative),
  Vl.new()
  |> Vl.mark(:line, interpolate: :basis)
  |> Vl.encode_field(:x, "trees", type: :quantitative)
  |> Vl.encode_field(:y, "profits_derivative", type: :quantitative)
  |> Vl.encode(:color, value: "#ff0000")
])
defmodule GradFun do
  import Nx.Defn

  defn my_function(x) do
    x |> Nx.cos() |> Nx.exp() |> Nx.sum() |> print_expr()
  end

  defn grad_my_function(x) do
    grad(x, &amp;my_function/1) |> print_expr()
  end
end
GradFun.grad_my_function(Nx.tensor([1.0, 2.0, 3.0]))