Spaces:
Sleeping
Sleeping
Commit
·
c3d240e
1
Parent(s):
ec48038
Add benchmark file
Browse files- README.md +26 -8
- benchmark.jl +14 -0
- benchmark.sh +1 -0
- eureqa.jl +12 -9
- paralleleureqa.jl +11 -11
README.md
CHANGED
|
@@ -1,14 +1,36 @@
|
|
| 1 |
# Running:
|
| 2 |
|
| 3 |
-
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
|
|
|
|
| 7 |
|
| 8 |
## Modification
|
| 9 |
|
| 10 |
You can change the binary and unary operators in `eureqa.jl` here:
|
| 11 |
-
```
|
| 12 |
const binops = [plus, mult]
|
| 13 |
const unaops = [sin, cos, exp];
|
| 14 |
```
|
|
@@ -28,10 +50,6 @@ by either loading in a dataset, or modifying the definition of `y`.
|
|
| 28 |
|
| 29 |
### Hyperparameters
|
| 30 |
|
| 31 |
-
Turn on annealing by setting the following in `paralleleureqa.jl`:
|
| 32 |
-
|
| 33 |
-
`const annealing = true`
|
| 34 |
-
|
| 35 |
Annealing allows each evolutionary cycle to turn down the exploration
|
| 36 |
rate over time: at the end (temperature 0), it will only select solutions
|
| 37 |
better than existing solutions.
|
|
|
|
| 1 |
# Running:
|
| 2 |
|
| 3 |
+
You can run the performance benchmark with `./benchmark.sh`.
|
| 4 |
+
|
| 5 |
+
Modify the search code in `paralleleureqa.jl` and `eureqa.jl` to your liking
|
| 6 |
+
(see below for options). Then, in a new Julia file called
|
| 7 |
+
`myfile.jl`, you can write:
|
| 8 |
+
|
| 9 |
+
```julia
|
| 10 |
+
include("paralleleureqa.jl")
|
| 11 |
+
fullRun(10,
|
| 12 |
+
npop=100,
|
| 13 |
+
annealing=true,
|
| 14 |
+
ncyclesperiteration=1000,
|
| 15 |
+
fractionReplaced=0.1f0,
|
| 16 |
+
verbosity=100)
|
| 17 |
+
```
|
| 18 |
+
The first arg is the number of migration periods to run,
|
| 19 |
+
with `ncyclesperiteration` determining how many generations
|
| 20 |
+
per migration period. `npop` is the number of population members.
|
| 21 |
+
`annealing` determines whether to stay in exploration mode,
|
| 22 |
+
or tune it down with each cycle. `fractionReplaced` is
|
| 23 |
+
how much of the population is replaced by migrated equations each
|
| 24 |
+
step.
|
| 25 |
+
|
| 26 |
|
| 27 |
+
Run it with threading turned on using:
|
| 28 |
+
`julia --threads auto -O3 myfile.jl`
|
| 29 |
|
| 30 |
## Modification
|
| 31 |
|
| 32 |
You can change the binary and unary operators in `eureqa.jl` here:
|
| 33 |
+
```julia
|
| 34 |
const binops = [plus, mult]
|
| 35 |
const unaops = [sin, cos, exp];
|
| 36 |
```
|
|
|
|
| 50 |
|
| 51 |
### Hyperparameters
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
Annealing allows each evolutionary cycle to turn down the exploration
|
| 54 |
rate over time: at the end (temperature 0), it will only select solutions
|
| 55 |
better than existing solutions.
|
benchmark.jl
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
include("paralleleureqa.jl")
|
| 2 |
+
fullRun(1,
|
| 3 |
+
npop=100,
|
| 4 |
+
annealing=true,
|
| 5 |
+
ncyclesperiteration=1000,
|
| 6 |
+
fractionReplaced=0.1f0,
|
| 7 |
+
verbosity=0)
|
| 8 |
+
@time fullRun(3,
|
| 9 |
+
npop=100,
|
| 10 |
+
annealing=true,
|
| 11 |
+
ncyclesperiteration=1000,
|
| 12 |
+
fractionReplaced=0.1f0,
|
| 13 |
+
verbosity=0
|
| 14 |
+
)
|
benchmark.sh
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
julia --threads 8 -O3 benchmark.jl
|
eureqa.jl
CHANGED
|
@@ -35,6 +35,10 @@ const nbin = size(binops)[1]
|
|
| 35 |
const nops = nuna + nbin
|
| 36 |
const nvar = size(X)[2];
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
# Define a serialization format for the symbolic equations:
|
| 39 |
mutable struct Node
|
| 40 |
#Holds operators, variables, constants in a tree
|
|
@@ -241,7 +245,7 @@ end
|
|
| 241 |
function scoreFunc(
|
| 242 |
tree::Node,
|
| 243 |
X::Array{Float32, 2},
|
| 244 |
-
y::Array{Float32, 1}
|
| 245 |
parsimony::Float32=0.1f0)::Float32
|
| 246 |
try
|
| 247 |
return MSE(evalTreeArray(tree, X), y) + countNodes(tree)*parsimony
|
|
@@ -341,8 +345,8 @@ function iterate(
|
|
| 341 |
end
|
| 342 |
|
| 343 |
try
|
| 344 |
-
beforeLoss = scoreFunc(prev, X, y, mult)
|
| 345 |
-
afterLoss = scoreFunc(tree, X, y, mult)
|
| 346 |
delta = afterLoss - beforeLoss
|
| 347 |
probChange = exp(-delta/(T*alpha))
|
| 348 |
|
|
@@ -378,7 +382,7 @@ mutable struct PopMember
|
|
| 378 |
score::Float32
|
| 379 |
birth::Int32
|
| 380 |
|
| 381 |
-
PopMember(t) = new(t, scoreFunc(t, X, y, parsimony), round(Int32, 1e3*(time()-1.6e9))
|
| 382 |
)
|
| 383 |
end
|
| 384 |
|
|
@@ -418,7 +422,7 @@ function iterateSample(pop::Population, T::Float32)::PopMember
|
|
| 418 |
allstar = bestOfSample(pop)
|
| 419 |
new = iterate(allstar.tree, T, X, y, alpha, parsimony)
|
| 420 |
allstar.tree = new
|
| 421 |
-
allstar.score = scoreFunc(new, X, y, parsimony)
|
| 422 |
allstar.birth = round(Int32, 1e3*(time()-1.6e9))
|
| 423 |
return allstar
|
| 424 |
end
|
|
@@ -441,7 +445,7 @@ function run(
|
|
| 441 |
pop::Population,
|
| 442 |
ncycles::Integer,
|
| 443 |
annealing::Bool=false;
|
| 444 |
-
|
| 445 |
)::Population
|
| 446 |
pop = deepcopy(pop)
|
| 447 |
|
|
@@ -452,12 +456,11 @@ function run(
|
|
| 452 |
else
|
| 453 |
pop = regEvolCycle(pop, 1.0f0)
|
| 454 |
end
|
| 455 |
-
if
|
| 456 |
-
# Get best 10 models from each evolution. Copy because we re-assign later.
|
| 457 |
bestPops = bestSubPop(pop)
|
| 458 |
bestCurScoreIdx = argmin([bestPops.members[member].score for member=1:bestPops.n])
|
| 459 |
bestCurScore = bestPops.members[bestCurScoreIdx].score
|
| 460 |
-
|
| 461 |
end
|
| 462 |
end
|
| 463 |
return pop
|
|
|
|
| 35 |
const nops = nuna + nbin
|
| 36 |
const nvar = size(X)[2];
|
| 37 |
|
| 38 |
+
function debug(verbosity, string...)
|
| 39 |
+
verbosity > 0 ? println(string...) : nothing
|
| 40 |
+
end
|
| 41 |
+
|
| 42 |
# Define a serialization format for the symbolic equations:
|
| 43 |
mutable struct Node
|
| 44 |
#Holds operators, variables, constants in a tree
|
|
|
|
| 245 |
function scoreFunc(
|
| 246 |
tree::Node,
|
| 247 |
X::Array{Float32, 2},
|
| 248 |
+
y::Array{Float32, 1};
|
| 249 |
parsimony::Float32=0.1f0)::Float32
|
| 250 |
try
|
| 251 |
return MSE(evalTreeArray(tree, X), y) + countNodes(tree)*parsimony
|
|
|
|
| 345 |
end
|
| 346 |
|
| 347 |
try
|
| 348 |
+
beforeLoss = scoreFunc(prev, X, y, parsimony=mult)
|
| 349 |
+
afterLoss = scoreFunc(tree, X, y, parsimony=mult)
|
| 350 |
delta = afterLoss - beforeLoss
|
| 351 |
probChange = exp(-delta/(T*alpha))
|
| 352 |
|
|
|
|
| 382 |
score::Float32
|
| 383 |
birth::Int32
|
| 384 |
|
| 385 |
+
PopMember(t) = new(t, scoreFunc(t, X, y, parsimony=parsimony), round(Int32, 1e3*(time()-1.6e9))
|
| 386 |
)
|
| 387 |
end
|
| 388 |
|
|
|
|
| 422 |
allstar = bestOfSample(pop)
|
| 423 |
new = iterate(allstar.tree, T, X, y, alpha, parsimony)
|
| 424 |
allstar.tree = new
|
| 425 |
+
allstar.score = scoreFunc(new, X, y, parsimony=parsimony)
|
| 426 |
allstar.birth = round(Int32, 1e3*(time()-1.6e9))
|
| 427 |
return allstar
|
| 428 |
end
|
|
|
|
| 445 |
pop::Population,
|
| 446 |
ncycles::Integer,
|
| 447 |
annealing::Bool=false;
|
| 448 |
+
verbosity::Integer=0
|
| 449 |
)::Population
|
| 450 |
pop = deepcopy(pop)
|
| 451 |
|
|
|
|
| 456 |
else
|
| 457 |
pop = regEvolCycle(pop, 1.0f0)
|
| 458 |
end
|
| 459 |
+
if verbosity > 0 && (iT % verbosity == 0)
|
|
|
|
| 460 |
bestPops = bestSubPop(pop)
|
| 461 |
bestCurScoreIdx = argmin([bestPops.members[member].score for member=1:bestPops.n])
|
| 462 |
bestCurScore = bestPops.members[bestCurScoreIdx].score
|
| 463 |
+
debug(verbosity, bestCurScore, " is the score for ", stringTree(bestPops.members[bestCurScoreIdx].tree))
|
| 464 |
end
|
| 465 |
end
|
| 466 |
return pop
|
paralleleureqa.jl
CHANGED
|
@@ -1,30 +1,31 @@
|
|
| 1 |
include("eureqa.jl")
|
| 2 |
|
| 3 |
-
println("Lets try to learn (x2^2 + cos(x3)) using regularized evolution from scratch")
|
| 4 |
const nthreads = Threads.nthreads()
|
| 5 |
-
println("Running with $nthreads threads")
|
| 6 |
-
const npop = 300
|
| 7 |
-
const annealing = true
|
| 8 |
-
const ncyclesperiteration = 30000
|
| 9 |
-
const fractionReplaced = 0.1
|
| 10 |
|
| 11 |
-
function fullRun(niterations::Integer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
# Generate random initial populations
|
| 13 |
allPops = [Population(npop, 3) for j=1:nthreads]
|
| 14 |
# Repeat this many evolutions; we collect and migrate the best
|
| 15 |
# each time.
|
| 16 |
for k=1:niterations
|
| 17 |
-
|
| 18 |
# Spawn threads to run indepdent evolutions, then gather them
|
| 19 |
@inbounds Threads.@threads for i=1:nthreads
|
| 20 |
-
allPops[i] = run(allPops[i], ncyclesperiteration, annealing,
|
| 21 |
end
|
| 22 |
|
| 23 |
# Get best 10 models from each evolution. Copy because we re-assign later.
|
| 24 |
bestPops = deepcopy(Population([member for pop in allPops for member in bestSubPop(pop).members]))
|
| 25 |
bestCurScoreIdx = argmin([bestPops.members[member].score for member=1:bestPops.n])
|
| 26 |
bestCurScore = bestPops.members[bestCurScoreIdx].score
|
| 27 |
-
|
| 28 |
|
| 29 |
# Migration
|
| 30 |
for j=1:nthreads
|
|
@@ -36,4 +37,3 @@ function fullRun(niterations::Integer)
|
|
| 36 |
end
|
| 37 |
end
|
| 38 |
|
| 39 |
-
fullRun(10)
|
|
|
|
| 1 |
include("eureqa.jl")
|
| 2 |
|
|
|
|
| 3 |
const nthreads = Threads.nthreads()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
+
function fullRun(niterations::Integer;
|
| 6 |
+
npop::Integer=300,
|
| 7 |
+
annealing::Bool=true,
|
| 8 |
+
ncyclesperiteration::Integer=3000,
|
| 9 |
+
fractionReplaced::Float32=0.1f0,
|
| 10 |
+
verbosity::Integer=0,
|
| 11 |
+
)
|
| 12 |
+
debug(verbosity, "Lets try to learn (x2^2 + cos(x3)) using regularized evolution from scratch")
|
| 13 |
+
debug(verbosity, "Running with $nthreads threads")
|
| 14 |
# Generate random initial populations
|
| 15 |
allPops = [Population(npop, 3) for j=1:nthreads]
|
| 16 |
# Repeat this many evolutions; we collect and migrate the best
|
| 17 |
# each time.
|
| 18 |
for k=1:niterations
|
|
|
|
| 19 |
# Spawn threads to run indepdent evolutions, then gather them
|
| 20 |
@inbounds Threads.@threads for i=1:nthreads
|
| 21 |
+
allPops[i] = run(allPops[i], ncyclesperiteration, annealing, verbosity=verbosity)
|
| 22 |
end
|
| 23 |
|
| 24 |
# Get best 10 models from each evolution. Copy because we re-assign later.
|
| 25 |
bestPops = deepcopy(Population([member for pop in allPops for member in bestSubPop(pop).members]))
|
| 26 |
bestCurScoreIdx = argmin([bestPops.members[member].score for member=1:bestPops.n])
|
| 27 |
bestCurScore = bestPops.members[bestCurScoreIdx].score
|
| 28 |
+
debug(verbosity, bestCurScore, " is the score for ", stringTree(bestPops.members[bestCurScoreIdx].tree))
|
| 29 |
|
| 30 |
# Migration
|
| 31 |
for j=1:nthreads
|
|
|
|
| 37 |
end
|
| 38 |
end
|
| 39 |
|
|
|