Neuroevolution
In Elixir
Jeff Smith
@jeffksmithjr
jeffsmith.tech
init/1
How can we build
technology
That learns from
biology
That serves humans
Deep Learning
Evolution
Neuroevolution in Elixir
Evolution
def evolve(nets, generations) do
evolve(nets, generations, &decrement/1)
end
def evolve(nets, generations, count_function) when generations > 0 do
Task.Supervisor.async(GN.TaskSupervisor, fn ->
IO.puts("Generations remaining: #{generations}")
learn_generation(nets)
|> select()
|> evolve(count_function.(generations), count_function)
end)
end
def evolve(nets, _generations, _count_function) do
nets
end
Evolution
def learn_generation(nets) do
clean_nets = strip_empties(nets)
tasks =
Task.Supervisor.async_stream_nolink(
GN.TaskSupervisor,
clean_nets,
&start_and_spawn(&1),
timeout: GN.Parameters.get(__MODULE__, :timeout)
)
generation = for {status, net} <- tasks, status == :ok, do: net
IO.puts(inspect(generation))
generation
end
Evolution
def spawn_offspring(seed_layers, mutation_rate  @mutation_rate) do
duplicate(seed_layers, mutation_rate)
|> remove(mutation_rate)
|> Enum.map(&mutate(&1, mutation_rate))
end
Mutation
def mutate_params(params, mutation_rate) do
for param <- params do
cond do
should_mutate(mutation_rate) ->
cond do
is_atom(param) ->
Enum.take_random(activation_functions(), 1) |> hd()
is_integer(param) ->
Statistics.Distributions.Normal.rand(param, @std_dev)
|> Statistics.Math.to_int()
is_float(param) ->
:rand.uniform()
end
...
Diversity in Ecosystems
Selection
def select(pid  __MODULE__, nets) do
cutoffs = cutoffs(nets)
for net <- nets do
complexity = length(net.layers)
level = Enum.min(
[Enum.find_index(cutoffs, &(&1 >= complexity)) + 1,
complexity_levels()])
net_acc = net.test_acc
elite_acc = Map.get(get(level), :test_acc)
if is_nil(elite_acc) or net_acc > elite_acc do
put(pid, level, net)
end
end
Evolution
iex(1)> GN.Selection.get_all()
%{
1 => %GN.Network{
id: "0c2020ad-8944-4f2c-80bd-1d92c9d26535",
layers: [
dense: [64, :softrelu],
batch_norm: [],
activation: [:relu],
dropout: [0.5],
dense: [63, :relu]
],
test_acc: 0.8553
},
2 => %GN.Network{
id: "58229333-a05d-4371-8f23-e8e55c37a2ec",
layers: [
dense: [64, :relu],
Continual Learning
Continual Learning
iex(2)> GN.Example.infinite_example()
%Task{
owner: #PID<0.171.0>,
pid: #PID<0.213.0>,
ref: #Reference<0.1968944036.911736833.180535>
}
Generations remaining: infinity
Continual Learning
def evolve_continual(nets) do
evolve(nets, :infinity, & &1)
end
def evolve(nets, generations, count_function) when generations > 0 do
Task.Supervisor.async(GN.TaskSupervisor, fn ->
IO.puts("Generations remaining: #{generations}")
learn_generation(nets)
|> select()
|> evolve(count_function.(generations), count_function)
end)
end
Interactive Evolution
Continual Learning
iex(2)> GN.Example.infinite_example()
%Task{
owner: #PID<0.171.0>,
pid: #PID<0.213.0>,
ref: #Reference<0.1968944036.911736833.180535>
}
Generations remaining: infinity
Interactive Evolution
iex(3)> GN.Parameters.put(GN.Selection, %{complexity_levels: 4})
:ok
Model Library
defmodule GN.Library do
use Agent
def start_link(opts  []) do
opts = Keyword.put_new(opts, :name, __MODULE__)
Agent.start_link(fn -> %{} end, opts)
end
def put(pid  __MODULE__, net) do
Agent.update(pid, &Map.put(&1, net.id, net))
end
def get_all(pid  __MODULE__) do
Agent.get(pid, & &1)
end
end
Interactive Evolution
iex(4)> GN.Selection.get_all() |> Map.get(2) |> GN.Library.put()
iex(5)> GN.Library.get("02b2a947-f888-4abf-b2a5-5df25668b0ee") |> GN.Selection.put_unevaluated()
Genotypes
Genotypes: V1
%GN.Network{
id: "0c2020ad-8944-4f2c-80bd-1d92c9d26535",
layers: [
dense: [64, :softrelu],
batch_norm: [],
activation: [:relu],
dropout: [0.5],
dense: [63, :relu]
],
test_acc: 0.8553
}
Layers in Elixir
def dense(py, n, activation) do
act_type = Atom.to_string(activation)
py |> call(dense(n, act_type))
end
def activation(py, activation) do
act_type = Atom.to_string(activation)
py |> call(activation(act_type))
end
def dropout(py, rate) do
py |> call(dropout(rate))
end
def batch_norm(py) do
py |> call(batch_norm())
end
Layers in Python
def dense(n, act_type):
act_type_str = act_type.decode("UTF-8")
if act_type_str == "none":
act_type_str = None
return gluon.nn.Dense(n, activation=act_type_str)
def activation(act_type):
return gluon.nn.Activation(act_type.decode("UTF-8"))
def dropout(rate):
return gluon.nn.Dropout(rate)
def batch_norm():
return gluon.nn.BatchNorm()
Galápagos Nǎo Tech Stack
● Elixir
● Apache MXNet/Gluon
● Python
● Export/ErlPort
● Docker
● Microsoft Cognitive Toolkit*
● ONNX*
● Caffe2*
* WIP
Models
ONNX Format
message AttributeProto {
enum AttributeType {
UNDEFINED = 0;
FLOAT = 1;
INT = 2;
STRING = 3;
TENSOR = 4;
GRAPH = 5;
FLOATS = 6;
INTS = 7;
STRINGS = 8;
TENSORS = 9;
GRAPHS = 10;
}
Loading ONNX Models
iex(1)> {:ok, mnist_data} = File.read "./test/examples/mnist.onnx"
{:ok,
<<8, 3, 18, 4, 67, 78, 84, 75, 26, 3, 50, 46, 52, 40, 1, 58, 227, 206, 1, 10,
199, 80, 18, 12, 80, 97, 114, 97, 109, 101, 116, 101, 114, 49, 57, 51, 26,
12, 80, 97, 114, 97, 109, 101, 116, 101, 114, 49, ...>>}
ONNXS: Genotypes V2
iex(2)> mnist_struct = Onnx.ModelProto.decode(mnist_data)
%Onnx.ModelProto{
doc_string: nil,
domain: nil,
graph: %Onnx.GraphProto{
doc_string: nil,
initializer: [],
input: [
%Onnx.ValueInfoProto{
doc_string: nil,
name: "Input3",
type: %Onnx.TypeProto{
value: {:tensor_type,
%Onnx.TypeProto.Tensor{
elem_type: 1,
shape: %Onnx.TensorShapeProto
...
ONNXS
iex(3)> mnist_updated = %{mnist_struct | model_version: 2}
def mutate(
%Onnx.NodeProto{
attribute: [%Onnx.AttributeProto{t: %Onnx.TensorProto{float_data: float_data}}]
} = layer,
mutation_rate
) do
mutated_data = mutate_params(float_data, mutation_rate)
update_in(
layer,
[Access.key!(:attribute), Access.all(), Access.key!(:t), Access.key!(:float_data)],
fn d -> mutated_data end
)
end
Mutation V2
Mutation V2
def mutate_params(params, mutation_rate) do
for param <- params do
cond do
should_mutate(mutation_rate) ->
cond do
is_float(param) ->
Statistics.Distributions.Normal.rand(param, @std_dev)
end
true ->
param
end
end
end
Opportunities
Tools for
ONNX
Elixir MXNet
bindings
Parameter discovery for
Python deep learning
tools
Phoenix-based
model server
EVM numerical
computing libraries
Embodied AI
terminate/2
Use the code
ctwempex18
for 40% off
all
Manning books.
References
Neuroevolution
In Elixir
Jeff Smith
@jeffksmithjr
jeffsmith.tech

Neuroevolution in Elixir