change project structure and using .ini as config file
This commit is contained in:
@@ -1 +0,0 @@
|
||||
from .pipeline import Pipeline
|
||||
@@ -1,9 +1,8 @@
|
||||
import numpy as np
|
||||
import jax
|
||||
from utils import Configer
|
||||
from algorithms.neat import Pipeline
|
||||
from time_utils import using_cprofile
|
||||
from algorithms.neat.function_factory import FunctionFactory
|
||||
from neat import Pipeline
|
||||
from neat import FunctionFactory
|
||||
from problems import EnhanceLogic
|
||||
import time
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import numpy as np
|
||||
import jax
|
||||
from utils import Configer
|
||||
from algorithms.neat import Pipeline
|
||||
from time_utils import using_cprofile
|
||||
from algorithms.neat.function_factory import FunctionFactory
|
||||
from problems import EnhanceLogic
|
||||
import time
|
||||
|
||||
|
||||
def evaluate(problem, func):
|
||||
outs = func(problem.inputs)
|
||||
outs = jax.device_get(outs)
|
||||
fitnesses = -np.mean((problem.outputs - outs) ** 2, axis=(1, 2))
|
||||
return fitnesses
|
||||
|
||||
|
||||
def main():
|
||||
config = Configer.load_config()
|
||||
problem = EnhanceLogic("xor", n=3)
|
||||
problem.refactor_config(config)
|
||||
function_factory = FunctionFactory(config)
|
||||
evaluate_func = lambda func: evaluate(problem, func)
|
||||
|
||||
# precompile
|
||||
pipeline = Pipeline(config, function_factory, seed=114514)
|
||||
pipeline.auto_run(evaluate_func)
|
||||
|
||||
for r in range(10):
|
||||
print(f"running: {r}/{10}")
|
||||
tic = time.time()
|
||||
|
||||
pipeline = Pipeline(config, function_factory, seed=r)
|
||||
pipeline.auto_run(evaluate_func)
|
||||
|
||||
total_time = time.time() - tic
|
||||
evaluate_time = pipeline.evaluate_time
|
||||
total_it = pipeline.generation
|
||||
print(f"total time: {total_time:.2f}s, evaluate time: {evaluate_time:.2f}s, total_it: {total_it}")
|
||||
|
||||
if total_it >= 500:
|
||||
res = "fail"
|
||||
else:
|
||||
res = "success"
|
||||
|
||||
with open("log", "ab") as f:
|
||||
f.write(f"{res}, total time: {total_time:.2f}s, evaluate time: {evaluate_time:.2f}s, total_it: {total_it}\n".encode("utf-8"))
|
||||
f.write(str(pipeline.generation_time_list).encode("utf-8"))
|
||||
|
||||
compile_time = function_factory.compile_time
|
||||
|
||||
print("total_compile_time:", compile_time)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
import numpy as np
|
||||
import jax
|
||||
from utils import Configer
|
||||
from algorithms.neat import Pipeline
|
||||
from time_utils import using_cprofile
|
||||
from algorithms.neat.function_factory import FunctionFactory
|
||||
from problems import EnhanceLogic
|
||||
import time
|
||||
|
||||
|
||||
def evaluate(problem, func):
|
||||
outs = func(problem.inputs)
|
||||
outs = jax.device_get(outs)
|
||||
fitnesses = -np.mean((problem.outputs - outs) ** 2, axis=(1, 2))
|
||||
return fitnesses
|
||||
|
||||
|
||||
def main():
|
||||
config = Configer.load_config()
|
||||
problem = EnhanceLogic("xor", n=3)
|
||||
problem.refactor_config(config)
|
||||
|
||||
evaluate_func = lambda func: evaluate(problem, func)
|
||||
|
||||
for p in [100, 200, 500, 1000, 2000, 5000, 10000, 20000]:
|
||||
config.neat.population.pop_size = p
|
||||
tic = time.time()
|
||||
function_factory = FunctionFactory(config)
|
||||
print(f"running: {p}")
|
||||
|
||||
pipeline = Pipeline(config, function_factory, seed=2)
|
||||
pipeline.auto_run(evaluate_func)
|
||||
|
||||
total_time = time.time() - tic
|
||||
evaluate_time = pipeline.evaluate_time
|
||||
total_it = pipeline.generation
|
||||
print(f"total time: {total_time:.2f}s, evaluate time: {evaluate_time:.2f}s, total_it: {total_it}")
|
||||
|
||||
with open("2060_log2", "ab") as f:
|
||||
f.write \
|
||||
(f"{p}, total time: {total_time:.2f}s, compile time: {function_factory.compile_time:.2f}s, total_it: {total_it}\n".encode
|
||||
("utf-8"))
|
||||
f.write(f"{str(pipeline.generation_time_list)}\n".encode("utf-8"))
|
||||
|
||||
compile_time = function_factory.compile_time
|
||||
|
||||
print("total_compile_time:", compile_time)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,12 +1,9 @@
|
||||
import jax
|
||||
import numpy as np
|
||||
from algorithms.neat.function_factory import FunctionFactory
|
||||
from algorithms.neat.genome.debug.tools import check_array_valid
|
||||
from neat import FunctionFactory
|
||||
from neat.genome.debug.tools import check_array_valid
|
||||
from utils import Configer
|
||||
|
||||
from algorithms.neat.genome.crossover import crossover
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
config = Configer.load_config()
|
||||
function_factory = FunctionFactory(config, debug=True)
|
||||
|
||||
@@ -25,10 +25,12 @@ def jax_mutate_population(seed, pop_x):
|
||||
def numpy_mutate_population(pop_x):
|
||||
return np.stack([numpy_mutate(x) for x in pop_x])
|
||||
|
||||
|
||||
def numpy_mutate_population_vmap(pop_x):
|
||||
noise = np.random.normal(size=pop_x.shape) * 0.1
|
||||
return pop_x + noise
|
||||
|
||||
|
||||
def main():
|
||||
seed = jax.random.PRNGKey(0)
|
||||
i = 10
|
||||
@@ -53,5 +55,6 @@ def main():
|
||||
|
||||
i = int(i * 1.3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from neat import FunctionFactory
|
||||
from utils import Configer
|
||||
from algorithms.neat import Pipeline
|
||||
from time_utils import using_cprofile
|
||||
from problems import Sin, Xor, DIY
|
||||
from neat import Pipeline
|
||||
from problems import Xor
|
||||
import time
|
||||
|
||||
|
||||
@@ -10,11 +10,14 @@ import time
|
||||
def main():
|
||||
tic = time.time()
|
||||
config = Configer.load_config()
|
||||
print(config)
|
||||
assert False
|
||||
problem = Xor()
|
||||
problem.refactor_config(config)
|
||||
pipeline = Pipeline(config, seed=6)
|
||||
function_factory = FunctionFactory(config)
|
||||
pipeline = Pipeline(config, function_factory, seed=6)
|
||||
nodes, cons = pipeline.auto_run(problem.evaluate)
|
||||
# print(nodes, cons)
|
||||
print(nodes, cons)
|
||||
total_time = time.time() - tic
|
||||
compile_time = pipeline.function_factory.compile_time
|
||||
total_it = pipeline.generation
|
||||
|
||||
2
neat/__init__.py
Normal file
2
neat/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .pipeline import Pipeline
|
||||
from .function_factory import FunctionFactory
|
||||
@@ -8,7 +8,7 @@ from jax import jit, vmap, Array
|
||||
from jax import numpy as jnp
|
||||
|
||||
# from .utils import fetch_first, I_INT
|
||||
from algorithms.neat.genome.utils import fetch_first, I_INT
|
||||
from neat.genome.utils import fetch_first, I_INT
|
||||
|
||||
|
||||
@jit
|
||||
@@ -1,78 +1,51 @@
|
||||
import json
|
||||
import os
|
||||
import warnings
|
||||
|
||||
from .dotdict import DotDict
|
||||
import configparser
|
||||
|
||||
|
||||
class Configer:
|
||||
@classmethod
|
||||
def __load_default_config(cls):
|
||||
par_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
default_config_path = os.path.join(par_dir, "./default_config.json")
|
||||
default_config_path = os.path.join(par_dir, "default_config.ini")
|
||||
return cls.__load_config(default_config_path)
|
||||
|
||||
@classmethod
|
||||
def __load_config(cls, config_path):
|
||||
with open(config_path, "r") as f:
|
||||
text = "".join(f.readlines())
|
||||
try:
|
||||
j = json.loads(text)
|
||||
except ValueError:
|
||||
raise Exception("Invalid config")
|
||||
return DotDict.from_dict(j, "root")
|
||||
c = configparser.ConfigParser()
|
||||
c.read(config_path)
|
||||
config = {}
|
||||
|
||||
for section in c.sections():
|
||||
for key, value in c.items(section):
|
||||
config[key] = eval(value)
|
||||
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def __check_redundant_config(cls, default_config, config):
|
||||
for key in config:
|
||||
if key not in default_config:
|
||||
warnings.warn(f"Redundant config: {key} in {config.name}")
|
||||
continue
|
||||
if isinstance(default_config[key], DotDict):
|
||||
cls.__check_redundant_config(default_config[key], config[key])
|
||||
|
||||
@classmethod
|
||||
def __complete_config(cls, default_config, config):
|
||||
for key in default_config:
|
||||
if key not in config:
|
||||
config[key] = default_config[key]
|
||||
continue
|
||||
if isinstance(default_config[key], DotDict):
|
||||
cls.__complete_config(default_config[key], config[key])
|
||||
|
||||
@classmethod
|
||||
def __decorate_config(cls, config):
|
||||
if config.neat.gene.activation.options == 'all':
|
||||
config.neat.gene.activation.options = [
|
||||
"sigmoid", "tanh", "sin", "gauss", "relu", "elu", "lelu", "selu", "softplus", "identity", "clamped",
|
||||
"inv", "log", "exp", "abs", "hat", "square", "cube"
|
||||
]
|
||||
if isinstance(config.neat.gene.activation.options, str):
|
||||
config.neat.gene.activation.options = [config.neat.gene.activation.options]
|
||||
|
||||
if config.neat.gene.aggregation.options == 'all':
|
||||
config.neat.gene.aggregation.options = ["product", "sum", "max", "min", "median", "mean"]
|
||||
if isinstance(config.neat.gene.aggregation.options, str):
|
||||
config.neat.gene.aggregation.options = [config.neat.gene.aggregation.options]
|
||||
|
||||
@classmethod
|
||||
def load_config(cls, config_path=None):
|
||||
default_config = cls.__load_default_config()
|
||||
if config_path is None:
|
||||
config = DotDict("root")
|
||||
config = {}
|
||||
elif not os.path.exists(config_path):
|
||||
warnings.warn(f"config file {config_path} not exist!")
|
||||
config = DotDict("root")
|
||||
config = {}
|
||||
else:
|
||||
config = cls.__load_config(config_path)
|
||||
|
||||
cls.__check_redundant_config(default_config, config)
|
||||
cls.__complete_config(default_config, config)
|
||||
cls.__decorate_config(config)
|
||||
# cls.__decorate_config(config)
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def write_config(cls, config, write_path):
|
||||
text = json.dumps(config, indent=2)
|
||||
with open(write_path, "w") as f:
|
||||
f.write(text)
|
||||
|
||||
65
utils/default_config.ini
Normal file
65
utils/default_config.ini
Normal file
@@ -0,0 +1,65 @@
|
||||
[basic]
|
||||
num_inputs = 2
|
||||
num_outputs = 1
|
||||
init_maximum_nodes = 20
|
||||
init_maximum_connections = 20
|
||||
init_maximum_species = 10
|
||||
expands_coe = 2
|
||||
forward_way = "pop_batch"
|
||||
|
||||
[population]
|
||||
fitness_threshold = 100000
|
||||
generation_limit = 100
|
||||
fitness_criterion = "max"
|
||||
pop_size = 150
|
||||
|
||||
[genome]
|
||||
compatibility_disjoint = 1.0
|
||||
compatibility_weight = 0.5
|
||||
conn_add_prob = 0.5
|
||||
conn_add_trials = 1
|
||||
conn_delete_prob = 0
|
||||
node_add_prob = 0.2
|
||||
node_delete_prob = 0
|
||||
|
||||
[species]
|
||||
compatibility_threshold = 3.0
|
||||
species_elitism = 2
|
||||
species_max_stagnation = 15
|
||||
genome_elitism = 2
|
||||
survival_threshold = 0.2
|
||||
min_species_size = 1
|
||||
|
||||
[gene-bias]
|
||||
bias_init_mean = 0.0
|
||||
bias_init_stdev = 1.0
|
||||
bias_mutate_power = 0.5
|
||||
bias_mutate_rate = 0.7
|
||||
bias_replace_rate = 0.1
|
||||
|
||||
[gene-response]
|
||||
response_init_mean = 1.0
|
||||
response_init_stdev = 0.0
|
||||
response_mutate_power = 0.0
|
||||
response_mutate_rate = 0.0
|
||||
response_replace_rate = 0.0
|
||||
|
||||
[gene-activation]
|
||||
activation_default = "sigmoid"
|
||||
activation_options = ["sigmoid"]
|
||||
activation_replace_rate = 0.0
|
||||
|
||||
[gene-aggregation]
|
||||
aggregation_default = "sum"
|
||||
aggregation_options = ["sum"]
|
||||
aggregation_replace_rate = 0.0
|
||||
|
||||
[gene-weight]
|
||||
weight_init_mean = 0.0
|
||||
weight_init_stdev = 1.0
|
||||
weight_mutate_power = 0.5
|
||||
weight_mutate_rate = 0.8
|
||||
weight_replace_rate = 0.1
|
||||
|
||||
[gene-enable]
|
||||
enable_mutate_rate = 0.01
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"basic": {
|
||||
"num_inputs": 2,
|
||||
"num_outputs": 1,
|
||||
"problem_batch": 4,
|
||||
"init_maximum_nodes": 20,
|
||||
"init_maximum_connections": 20,
|
||||
"init_maximum_species": 10,
|
||||
"expands_coe": 2,
|
||||
"pre_compile_times": 3,
|
||||
"forward_way": "pop_batch"
|
||||
},
|
||||
"neat": {
|
||||
"population": {
|
||||
"fitness_criterion": "max",
|
||||
"fitness_threshold": 1e-2,
|
||||
"generation_limit": 100,
|
||||
"pop_size": 1000,
|
||||
"reset_on_extinction": "False"
|
||||
},
|
||||
"gene": {
|
||||
"bias": {
|
||||
"init_mean": 0.0,
|
||||
"init_stdev": 1.0,
|
||||
"mutate_power": 0.5,
|
||||
"mutate_rate": 0.7,
|
||||
"replace_rate": 0.1
|
||||
},
|
||||
"response": {
|
||||
"init_mean": 1.0,
|
||||
"init_stdev": 0.0,
|
||||
"mutate_power": 0.0,
|
||||
"mutate_rate": 0.0,
|
||||
"replace_rate": 0.0
|
||||
},
|
||||
"activation": {
|
||||
"default": "sigmoid",
|
||||
"options": ["sigmoid"],
|
||||
"mutate_rate": 0.1
|
||||
},
|
||||
"aggregation": {
|
||||
"default": "sum",
|
||||
"options": "sum",
|
||||
"mutate_rate": 0.1
|
||||
},
|
||||
"weight": {
|
||||
"init_mean": 0.0,
|
||||
"init_stdev": 1.0,
|
||||
"mutate_power": 0.5,
|
||||
"mutate_rate": 0.8,
|
||||
"replace_rate": 0.1
|
||||
},
|
||||
"enabled": {
|
||||
"mutate_rate": 0.01
|
||||
}
|
||||
},
|
||||
"genome": {
|
||||
"compatibility_disjoint_coefficient": 1.0,
|
||||
"compatibility_weight_coefficient": 0.5,
|
||||
"single_structural_mutation": "False",
|
||||
"conn_add_prob": 0.6,
|
||||
"conn_delete_prob": 0,
|
||||
"node_add_prob": 0.3,
|
||||
"node_delete_prob": 0
|
||||
},
|
||||
"species": {
|
||||
"compatibility_threshold": 2.5,
|
||||
"species_fitness_func": "max",
|
||||
"max_stagnation": 20,
|
||||
"species_elitism": 2,
|
||||
"genome_elitism": 2,
|
||||
"survival_threshold": 0.2,
|
||||
"min_species_size": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
# DotDict For Config. Case Insensitive.
|
||||
|
||||
class DotDict(dict):
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self["name"] = name
|
||||
|
||||
def __getattr__(self, attr):
|
||||
attr = attr.lower() # case insensitive
|
||||
if attr in self:
|
||||
return self[attr]
|
||||
else:
|
||||
raise AttributeError(f"'{self.__class__.__name__}-{self.name}' has no attribute '{attr}'")
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
attr = attr.lower() # case insensitive
|
||||
if attr not in self:
|
||||
raise AttributeError(f"'{self.__class__.__name__}-{self.name}' has no attribute '{attr}'")
|
||||
self[attr] = value
|
||||
|
||||
def __delattr__(self, attr):
|
||||
attr = attr.lower() # case insensitive
|
||||
if attr in self:
|
||||
del self[attr]
|
||||
else:
|
||||
raise AttributeError(f"{self.__class__.__name__}-{self.name} object has no attribute '{attr}'")
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d, name):
|
||||
if not isinstance(d, dict):
|
||||
return d
|
||||
|
||||
dot_dict = cls(name)
|
||||
for key, value in d.items():
|
||||
key = key.lower() # case insensitive
|
||||
if isinstance(value, dict):
|
||||
dot_dict[key] = cls.from_dict(value, key)
|
||||
else:
|
||||
dot_dict[key] = value
|
||||
if dot_dict[key] == "True": # Fuck! Json has no bool type!
|
||||
dot_dict[key] = True
|
||||
if dot_dict[key] == "False":
|
||||
dot_dict[key] = False
|
||||
if dot_dict[key] == "None":
|
||||
dot_dict[key] = None
|
||||
return dot_dict
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
nested_dict = {
|
||||
"a": 1,
|
||||
"b": {
|
||||
"c": 2,
|
||||
"ACDeef": {
|
||||
"e": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dd = DotDict.from_dict(nested_dict, "root")
|
||||
print(dd.b.acdeef.e) # 输出:3
|
||||
Reference in New Issue
Block a user