Spaces:
Running
Running
MilesCranmer
commited on
Commit
•
fe36e3a
1
Parent(s):
964b669
Add option to slowly increase maxsize
Browse files- TODO.md +1 -0
- docs/options.md +4 -0
- julia/sr.jl +35 -14
- pysr/sr.py +6 -0
TODO.md
CHANGED
@@ -56,6 +56,7 @@
|
|
56 |
- [x] Create backup csv file so always something to copy from for `PySR`. Also use random hall of fame file by default. Call function to read from csv after running, so dont need to run again. Dump scores alongside MSE to .csv (and return with Pandas).
|
57 |
- [x] Better cleanup of zombie processes after <ctl-c>
|
58 |
- [x] Consider printing output sorted by score, not by complexity.
|
|
|
59 |
- [ ] Sort these todo lists by priority
|
60 |
|
61 |
## Feature ideas
|
|
|
56 |
- [x] Create backup csv file so always something to copy from for `PySR`. Also use random hall of fame file by default. Call function to read from csv after running, so dont need to run again. Dump scores alongside MSE to .csv (and return with Pandas).
|
57 |
- [x] Better cleanup of zombie processes after <ctl-c>
|
58 |
- [x] Consider printing output sorted by score, not by complexity.
|
59 |
+
- [x] Increase max complexity slowly over time up to the actual max.
|
60 |
- [ ] Sort these todo lists by priority
|
61 |
|
62 |
## Feature ideas
|
docs/options.md
CHANGED
@@ -107,6 +107,10 @@ constants, variables). `maxdepth` is by default not used, but can be set
|
|
107 |
to control the maximum depth of an equation. These will make processing
|
108 |
faster, as longer equations take longer to test.
|
109 |
|
|
|
|
|
|
|
|
|
110 |
|
111 |
## Batching
|
112 |
One can turn on mini-batching, with the `batching` flag,
|
|
|
107 |
to control the maximum depth of an equation. These will make processing
|
108 |
faster, as longer equations take longer to test.
|
109 |
|
110 |
+
One can warm up the maxsize from a small number to encourage
|
111 |
+
PySR to start simple, by using the `warmupMaxsize` argument.
|
112 |
+
This specifies that maxsize increases every `warmupMaxsize`.
|
113 |
+
|
114 |
|
115 |
## Batching
|
116 |
One can turn on mini-batching, with the `batching` flag,
|
julia/sr.jl
CHANGED
@@ -597,7 +597,7 @@ end
|
|
597 |
|
598 |
# Go through one simulated annealing mutation cycle
|
599 |
# exp(-delta/T) defines probability of accepting a change
|
600 |
-
function iterate(member::PopMember, T::Float32)::PopMember
|
601 |
prev = member.tree
|
602 |
tree = copyNode(prev)
|
603 |
#TODO - reconsider this
|
@@ -610,23 +610,31 @@ function iterate(member::PopMember, T::Float32)::PopMember
|
|
610 |
mutationChoice = rand()
|
611 |
weightAdjustmentMutateConstant = min(8, countConstants(tree))/8.0
|
612 |
cur_weights = copy(mutationWeights) .* 1.0
|
|
|
613 |
cur_weights[1] *= weightAdjustmentMutateConstant
|
614 |
-
cur_weights /= sum(cur_weights)
|
615 |
-
cweights = cumsum(cur_weights)
|
616 |
n = countNodes(tree)
|
617 |
depth = countDepth(tree)
|
618 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
619 |
if mutationChoice < cweights[1]
|
620 |
tree = mutateConstant(tree, T)
|
621 |
elseif mutationChoice < cweights[2]
|
622 |
tree = mutateOperator(tree)
|
623 |
-
elseif mutationChoice < cweights[3]
|
624 |
if rand() < 0.5
|
625 |
tree = appendRandomOp(tree)
|
626 |
else
|
627 |
tree = prependRandomOp(tree)
|
628 |
end
|
629 |
-
elseif mutationChoice < cweights[4]
|
630 |
tree = insertRandomOp(tree)
|
631 |
elseif mutationChoice < cweights[5]
|
632 |
tree = deleteRandomOp(tree)
|
@@ -711,7 +719,7 @@ end
|
|
711 |
|
712 |
# Pass through the population several times, replacing the oldest
|
713 |
# with the fittest of a small subsample
|
714 |
-
function regEvolCycle(pop::Population, T::Float32)::Population
|
715 |
# Batch over each subsample. Can give 15% improvement in speed; probably moreso for large pops.
|
716 |
# but is ultimately a different algorithm than regularized evolution, and might not be
|
717 |
# as good.
|
@@ -732,7 +740,7 @@ function regEvolCycle(pop::Population, T::Float32)::Population
|
|
732 |
end
|
733 |
end
|
734 |
allstar = pop.members[best_idx]
|
735 |
-
babies[i] = iterate(allstar, T)
|
736 |
end
|
737 |
|
738 |
# Replace the n_evol_cycles-oldest members of each population
|
@@ -743,7 +751,7 @@ function regEvolCycle(pop::Population, T::Float32)::Population
|
|
743 |
else
|
744 |
for i=1:round(Integer, pop.n/ns)
|
745 |
allstar = bestOfSample(pop)
|
746 |
-
baby = iterate(allstar, T)
|
747 |
#printTree(baby.tree)
|
748 |
oldest = argmin([pop.members[member].birth for member=1:pop.n])
|
749 |
pop.members[oldest] = baby
|
@@ -757,16 +765,17 @@ end
|
|
757 |
# printing the fittest equation every 10% through
|
758 |
function run(
|
759 |
pop::Population,
|
760 |
-
ncycles::Integer
|
|
|
761 |
verbosity::Integer=0
|
762 |
-
|
763 |
|
764 |
allT = LinRange(1.0f0, 0.0f0, ncycles)
|
765 |
for iT in 1:size(allT)[1]
|
766 |
if annealing
|
767 |
-
pop = regEvolCycle(pop, allT[iT])
|
768 |
else
|
769 |
-
pop = regEvolCycle(pop, 1.0f0)
|
770 |
end
|
771 |
|
772 |
if verbosity > 0 && (iT % verbosity == 0)
|
@@ -909,6 +918,10 @@ function fullRun(niterations::Integer;
|
|
909 |
channels = [RemoteChannel(1) for j=1:npopulations]
|
910 |
bestSubPops = [Population(1) for j=1:npopulations]
|
911 |
hallOfFame = HallOfFame()
|
|
|
|
|
|
|
|
|
912 |
|
913 |
for i=1:npopulations
|
914 |
future = @spawnat :any Population(npop, 3)
|
@@ -917,10 +930,11 @@ function fullRun(niterations::Integer;
|
|
917 |
|
918 |
# # 2. Start the cycle on every process:
|
919 |
@sync for i=1:npopulations
|
920 |
-
@async allPops[i] = @spawnat :any run(fetch(allPops[i]), ncyclesperiteration, verbosity=verbosity)
|
921 |
end
|
922 |
println("Started!")
|
923 |
cycles_complete = npopulations * niterations
|
|
|
924 |
|
925 |
last_print_time = time()
|
926 |
num_equations = 0.0
|
@@ -1006,7 +1020,7 @@ function fullRun(niterations::Integer;
|
|
1006 |
|
1007 |
@async begin
|
1008 |
allPops[i] = @spawnat :any let
|
1009 |
-
tmp_pop = run(cur_pop, ncyclesperiteration, verbosity=verbosity)
|
1010 |
@inbounds @simd for j=1:tmp_pop.n
|
1011 |
if rand() < 0.1
|
1012 |
tmp_pop.members[j].tree = simplifyTree(tmp_pop.members[j].tree)
|
@@ -1027,6 +1041,13 @@ function fullRun(niterations::Integer;
|
|
1027 |
end
|
1028 |
|
1029 |
cycles_complete -= 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1030 |
num_equations += ncyclesperiteration * npop / 10.0
|
1031 |
end
|
1032 |
end
|
|
|
597 |
|
598 |
# Go through one simulated annealing mutation cycle
|
599 |
# exp(-delta/T) defines probability of accepting a change
|
600 |
+
function iterate(member::PopMember, T::Float32, curmaxsize::Integer)::PopMember
|
601 |
prev = member.tree
|
602 |
tree = copyNode(prev)
|
603 |
#TODO - reconsider this
|
|
|
610 |
mutationChoice = rand()
|
611 |
weightAdjustmentMutateConstant = min(8, countConstants(tree))/8.0
|
612 |
cur_weights = copy(mutationWeights) .* 1.0
|
613 |
+
#More constants => more likely to do constant mutation
|
614 |
cur_weights[1] *= weightAdjustmentMutateConstant
|
|
|
|
|
615 |
n = countNodes(tree)
|
616 |
depth = countDepth(tree)
|
617 |
|
618 |
+
# If equation too big, don't add new operators
|
619 |
+
if n >= curmaxsize || depth >= maxdepth
|
620 |
+
cur_weights[3] = 0.0
|
621 |
+
cur_weights[4] = 0.0
|
622 |
+
end
|
623 |
+
|
624 |
+
cur_weights /= sum(cur_weights)
|
625 |
+
cweights = cumsum(cur_weights)
|
626 |
+
|
627 |
if mutationChoice < cweights[1]
|
628 |
tree = mutateConstant(tree, T)
|
629 |
elseif mutationChoice < cweights[2]
|
630 |
tree = mutateOperator(tree)
|
631 |
+
elseif mutationChoice < cweights[3]
|
632 |
if rand() < 0.5
|
633 |
tree = appendRandomOp(tree)
|
634 |
else
|
635 |
tree = prependRandomOp(tree)
|
636 |
end
|
637 |
+
elseif mutationChoice < cweights[4]
|
638 |
tree = insertRandomOp(tree)
|
639 |
elseif mutationChoice < cweights[5]
|
640 |
tree = deleteRandomOp(tree)
|
|
|
719 |
|
720 |
# Pass through the population several times, replacing the oldest
|
721 |
# with the fittest of a small subsample
|
722 |
+
function regEvolCycle(pop::Population, T::Float32, curmaxsize::Integer)::Population
|
723 |
# Batch over each subsample. Can give 15% improvement in speed; probably moreso for large pops.
|
724 |
# but is ultimately a different algorithm than regularized evolution, and might not be
|
725 |
# as good.
|
|
|
740 |
end
|
741 |
end
|
742 |
allstar = pop.members[best_idx]
|
743 |
+
babies[i] = iterate(allstar, T, curmaxsize)
|
744 |
end
|
745 |
|
746 |
# Replace the n_evol_cycles-oldest members of each population
|
|
|
751 |
else
|
752 |
for i=1:round(Integer, pop.n/ns)
|
753 |
allstar = bestOfSample(pop)
|
754 |
+
baby = iterate(allstar, T, curmaxsize)
|
755 |
#printTree(baby.tree)
|
756 |
oldest = argmin([pop.members[member].birth for member=1:pop.n])
|
757 |
pop.members[oldest] = baby
|
|
|
765 |
# printing the fittest equation every 10% through
|
766 |
function run(
|
767 |
pop::Population,
|
768 |
+
ncycles::Integer,
|
769 |
+
curmaxsize::Integer;
|
770 |
verbosity::Integer=0
|
771 |
+
)::Population
|
772 |
|
773 |
allT = LinRange(1.0f0, 0.0f0, ncycles)
|
774 |
for iT in 1:size(allT)[1]
|
775 |
if annealing
|
776 |
+
pop = regEvolCycle(pop, allT[iT], curmaxsize)
|
777 |
else
|
778 |
+
pop = regEvolCycle(pop, 1.0f0, curmaxsize)
|
779 |
end
|
780 |
|
781 |
if verbosity > 0 && (iT % verbosity == 0)
|
|
|
918 |
channels = [RemoteChannel(1) for j=1:npopulations]
|
919 |
bestSubPops = [Population(1) for j=1:npopulations]
|
920 |
hallOfFame = HallOfFame()
|
921 |
+
curmaxsize = 3
|
922 |
+
if warmupMaxsize == 0
|
923 |
+
curmaxsize = maxsize
|
924 |
+
end
|
925 |
|
926 |
for i=1:npopulations
|
927 |
future = @spawnat :any Population(npop, 3)
|
|
|
930 |
|
931 |
# # 2. Start the cycle on every process:
|
932 |
@sync for i=1:npopulations
|
933 |
+
@async allPops[i] = @spawnat :any run(fetch(allPops[i]), ncyclesperiteration, curmaxsize, verbosity=verbosity)
|
934 |
end
|
935 |
println("Started!")
|
936 |
cycles_complete = npopulations * niterations
|
937 |
+
curmaxsize += 1
|
938 |
|
939 |
last_print_time = time()
|
940 |
num_equations = 0.0
|
|
|
1020 |
|
1021 |
@async begin
|
1022 |
allPops[i] = @spawnat :any let
|
1023 |
+
tmp_pop = run(cur_pop, ncyclesperiteration, curmaxsize, verbosity=verbosity)
|
1024 |
@inbounds @simd for j=1:tmp_pop.n
|
1025 |
if rand() < 0.1
|
1026 |
tmp_pop.members[j].tree = simplifyTree(tmp_pop.members[j].tree)
|
|
|
1041 |
end
|
1042 |
|
1043 |
cycles_complete -= 1
|
1044 |
+
cycles_elapsed = npopulations * niterations - cycles_complete
|
1045 |
+
if warmupMaxsize != 0 && cycles_elapsed % warmupMaxsize == 0
|
1046 |
+
curmaxsize += 1
|
1047 |
+
if curmaxsize > maxsize
|
1048 |
+
curmaxsize = maxsize
|
1049 |
+
end
|
1050 |
+
end
|
1051 |
num_equations += ncyclesperiteration * npop / 10.0
|
1052 |
end
|
1053 |
end
|
pysr/sr.py
CHANGED
@@ -85,6 +85,7 @@ def pysr(X=None, y=None, weights=None,
|
|
85 |
batching=False,
|
86 |
batchSize=50,
|
87 |
select_k_features=None,
|
|
|
88 |
threads=None, #deprecated
|
89 |
julia_optimization=3,
|
90 |
):
|
@@ -157,6 +158,10 @@ def pysr(X=None, y=None, weights=None,
|
|
157 |
Python using random forests, before passing to the symbolic regression
|
158 |
code. None means no feature selection; an int means select that many
|
159 |
features.
|
|
|
|
|
|
|
|
|
160 |
:param julia_optimization: int, Optimization level (0, 1, 2, 3)
|
161 |
:returns: pd.DataFrame, Results dataframe, giving complexity, MSE, and equations
|
162 |
(as strings).
|
@@ -268,6 +273,7 @@ const mutationWeights = [
|
|
268 |
{weightRandomize:f},
|
269 |
{weightDoNothing:f}
|
270 |
]
|
|
|
271 |
"""
|
272 |
|
273 |
if X.shape[1] == 1:
|
|
|
85 |
batching=False,
|
86 |
batchSize=50,
|
87 |
select_k_features=None,
|
88 |
+
warmupMaxsize=0,
|
89 |
threads=None, #deprecated
|
90 |
julia_optimization=3,
|
91 |
):
|
|
|
158 |
Python using random forests, before passing to the symbolic regression
|
159 |
code. None means no feature selection; an int means select that many
|
160 |
features.
|
161 |
+
:param warmupMaxsize: int, whether to slowly increase max size from
|
162 |
+
a small number up to the maxsize (if greater than 0).
|
163 |
+
If greater than 0, says how many cycles before the maxsize
|
164 |
+
is increased.
|
165 |
:param julia_optimization: int, Optimization level (0, 1, 2, 3)
|
166 |
:returns: pd.DataFrame, Results dataframe, giving complexity, MSE, and equations
|
167 |
(as strings).
|
|
|
273 |
{weightRandomize:f},
|
274 |
{weightDoNothing:f}
|
275 |
]
|
276 |
+
const warmupMaxsize = {warmupMaxsize:d}
|
277 |
"""
|
278 |
|
279 |
if X.shape[1] == 1:
|