preparing new evaluation method

This commit is contained in:
Jonas Weinz 2020-03-12 19:14:30 +01:00
parent 006bf59eb0
commit 7d6d835a01
2 changed files with 4706 additions and 5275 deletions

File diff suppressed because it is too large Load Diff

View File

@ -659,6 +659,9 @@ class RecipeTreeNode(object):
return r return r
def serialize_subtree(self):
return [n.serialize() for n in self.traverse()]
def node_score(self): def node_score(self):
raise NotImplementedError() raise NotImplementedError()
@ -781,8 +784,8 @@ class MixNode(RecipeTreeNode):
#if c_grouped_mix[ia,ib] > 0 or c_grouped_mix[ib,ia] > 0: #if c_grouped_mix[ia,ib] > 0 or c_grouped_mix[ib,ia] > 0:
# s += 1 # s += 1
ia = m_mix._label_index[grouped_ing_a.to_json()] ia = m_mix._label_index[ing_a.to_json()]
ib = m_mix._label_index[grouped_ing_b.to_json()] ib = m_mix._label_index[ing_b.to_json()]
if c_mix[ia,ib] > 0 or c_mix[ib,ia] > 0: if c_mix[ia,ib] > 0 or c_mix[ib,ia] > 0:
s += 1 s += 1
@ -1290,10 +1293,10 @@ class Tree(object):
if node.name() in constant_ingredients: if node.name() in constant_ingredients:
node._constant = True node._constant = True
return Tree(root) return Tree(root, main_ingredients)
@staticmethod @staticmethod
def from_serialization(s): def from_serialization(s, main_ingredients = None):
def empty_node(raw_n): def empty_node(raw_n):
if raw_n['type'] == "MixNode": if raw_n['type'] == "MixNode":
node = MixNode(raw_n['constant']) node = MixNode(raw_n['constant'])
@ -1317,7 +1320,7 @@ class Tree(object):
for c in childs: for c in childs:
nodes[id].add_child(nodes[c]) nodes[id].add_child(nodes[c])
return Tree(nodes[s[0]['id']]) return Tree(nodes[s[0]['id']], main_ingredients)
def __init__(self, root, main_ingredients=None): def __init__(self, root, main_ingredients=None):
@ -1354,6 +1357,7 @@ class Tree(object):
def collect_scores(self): def collect_scores(self):
self._mix_scores = [] self._mix_scores = []
self._mix_weights = []
self._act_scores = [] self._act_scores = []
self._ing_scores = [] self._ing_scores = []
@ -1366,6 +1370,7 @@ class Tree(object):
for n in nodes: for n in nodes:
if type(n) == MixNode: if type(n) == MixNode:
self._mix_scores.append(n.node_score()) self._mix_scores.append(n.node_score())
self._mix_weights.append(len(n.childs()))
self._n_mix_nodes += 1 self._n_mix_nodes += 1
if type(n) == ActionNode: if type(n) == ActionNode:
self._act_scores.append(n.node_score()) self._act_scores.append(n.node_score())
@ -1385,12 +1390,13 @@ class Tree(object):
seen_ingredients.add(n.name()) seen_ingredients.add(n.name())
self._mix_scores = np.array(self._mix_scores) self._mix_scores = np.array(self._mix_scores)
self._mix_weights = np.array(self._mix_weights)
self._act_scores = np.array(self._act_scores) self._act_scores = np.array(self._act_scores)
self._ing_scores = np.array(self._ing_scores) self._ing_scores = np.array(self._ing_scores)
def mix_scores(self): def mix_scores(self):
return self._mix_scores return self._mix_scores, self._mix_weights
def action_scores(self): def action_scores(self):
return self._act_scores return self._act_scores
@ -1426,11 +1432,11 @@ class Tree(object):
return self._score return self._score
self.collect_scores() self.collect_scores()
s_mix = self.mix_scores() s_mix, s_mix_weights = self.mix_scores()
s_act = self.action_scores() s_act = self.action_scores()
s_ing = self.ing_scores() s_ing = self.ing_scores()
n = len(s_mix) + len(s_act) + len(s_ing) #n = len(s_mix) + len(s_act) + len(s_ing)
avg_mix = np.average(s_mix) if len(s_mix) > 0 else 1 avg_mix = np.average(s_mix) if len(s_mix) > 0 else 1
avg_act = np.average(s_act) if len(s_act) > 0 else 1 avg_act = np.average(s_act) if len(s_act) > 0 else 1
@ -1441,21 +1447,34 @@ class Tree(object):
sum_ing = np.sum(s_ing) if len(s_ing) > 0 else 0 sum_ing = np.sum(s_ing) if len(s_ing) > 0 else 0
self._touched = False self._touched = False
contains_main_ingred = True
base_main_ings = [i._base_ingredient for i in self.root().traverse_ingredients()]
for ing in self._main_ingredients:
if ing not in base_main_ings:
contains_main_ingred = False
self._score = 0
break
if contains_main_ingred:
# boost creativity # boost creativity
if len(s_act) < 3: if len(s_act) < 3:
self._score = 0 self._score = 0
elif len(s_ing) < 3: elif len(s_ing) < 3:
self._score = 0 self._score = 0
else: else:
self._score = (sum_mix + sum_act + sum_ing) / n weighted_mix_score = np.array([s_mix[i] * s_mix_weights[i] for i in range(len(s_mix))])
#print(weighted_mix_score)
n = len(s_act) + len(s_ing) + np.sum(s_mix_weights)
self._score = (np.sum(weighted_mix_score) + sum_act + sum_ing) / n
#print(self._score)
self._score *= (len(s_ing) - self._n_duplicates) / len(s_ing) self._score *= (len(s_ing) - self._n_duplicates) / len(s_ing)
#self._score = 0.95 * self._score + 0.05 * self.main_ingredient_score() #self._score = 0.95 * self._score + 0.05 * self.main_ingredient_score()
return self._score return self._score
def copy(self): def copy(self):
return Tree.from_serialization(self.serialize()) return Tree.from_serialization(self.serialize(), self._main_ingredients)
# ## Population # ## Population
@ -1494,8 +1513,59 @@ class Population(object):
self.population = new_population self.population = new_population
def crossover(self):
# shuffle indices
indices = list(range(len(self.population) // 2))
indices = [i + len(self.population) // 2 for i in indices]
random.shuffle(indices)
# perform crossover for random pairs
for i in range(len(self.population) // 4):
i_a = indices[2*i]
i_b = indices[2*i+1]
self.pairwise_crossover(self.population[i_a], self.population[i_b])
def pairwise_crossover(self, tree_a, tree_b):
# for crossover: find a random subtree in both trees, and switch them
# first, select one random mix node from both
a_nodes = tree_a.root().traverse()
b_nodes = tree_b.root().traverse()
a_mix_nodes = []
b_mix_nodes = []
for n in a_nodes:
if type(n) == MixNode:
a_mix_nodes.append(n)
for n in b_nodes:
if type(n) == MixNode:
b_mix_nodes.append(n)
a_mix_node = np.random.choice(a_mix_nodes)
b_mix_node = np.random.choice(b_mix_nodes)
# now select one random child, we will switch the subtrees there
a_child = np.random.choice(list(a_mix_node.childs()))
b_child = np.random.choice(list(b_mix_node.childs()))
# ...and perform the switch
# manually remove references
a_mix_node.remove_child(a_child)
b_mix_node.remove_child(b_child)
# and add child to other subtree
a_mix_node.add_child(b_child)
b_mix_node.add_child(a_child)
def hold_best(self, n=10): def hold_best(self, n=10):
scores = [tree.score() for tree in self.population] scores = np.array([tree.score() for tree in self.population])
sorted_indices = np.argsort(-scores) sorted_indices = np.argsort(-scores)
@ -1505,8 +1575,8 @@ class Population(object):
avg_scores = [] avg_scores = []
for i in tqdm(range(n), desc="run evolutionary cycles"): for i in tqdm(range(n), desc="run evolutionary cycles"):
self.mutate() self.mutate()
#self.mutate()
#self.collect_scores() self.crossover()
self.pairwise_competition() self.pairwise_competition()
#self.collect_scores() #self.collect_scores()
@ -1530,17 +1600,13 @@ class Population(object):
# ## Run Evolutionary Algorithm # ## Run Evolutionary Algorithm
#p = Population(["noodle"],['noodle'], min_additional=4, max_additional=13, n_population = 50) p = Population(["noodle"],['noodle'], min_additional=4, max_additional=13, n_population = 50, mutations=1)
#p_ingredient_unprepared(list(p.population[0].root().childs())[0]._name) < 0.2 #p_ingredient_unprepared(list(p.population[0].root().childs())[0]._name) < 0.2
#p.run(50) #avg = p.run(10)
#p.plot_population(n_best=20) #p.plot_population(n_best=20)