best for now RMSE: 30.937
This commit is contained in:
@@ -37,3 +37,62 @@ def grow_mutation(chromosome: Chromosome, max_depth: int) -> Chromosome:
|
|||||||
chromosome.root = subtree
|
chromosome.root = subtree
|
||||||
|
|
||||||
return chromosome
|
return chromosome
|
||||||
|
|
||||||
|
|
||||||
|
def node_replacement_mutation(chromosome: Chromosome) -> Chromosome:
|
||||||
|
"""Мутация замены операции (Node Replacement Mutation).
|
||||||
|
|
||||||
|
Выбирает случайный узел с операцией (arity > 0) и заменяет его
|
||||||
|
на случайную другую операцию той же арности, сохраняя поддеревья.
|
||||||
|
|
||||||
|
Если подходящей альтернативы нет — возвращает копию без изменений.
|
||||||
|
"""
|
||||||
|
chromosome = chromosome.copy()
|
||||||
|
|
||||||
|
operation_nodes = [n for n in chromosome.root.list_nodes() if n.value.arity > 0]
|
||||||
|
if not operation_nodes:
|
||||||
|
return chromosome
|
||||||
|
|
||||||
|
target_node = random.choice(operation_nodes)
|
||||||
|
current_arity = target_node.value.arity
|
||||||
|
|
||||||
|
same_arity_ops = [
|
||||||
|
op
|
||||||
|
for op in chromosome.operations
|
||||||
|
if op.arity == current_arity and op != target_node.value
|
||||||
|
]
|
||||||
|
if not same_arity_ops:
|
||||||
|
return chromosome
|
||||||
|
|
||||||
|
new_operation = random.choice(same_arity_ops)
|
||||||
|
|
||||||
|
target_node.value = new_operation
|
||||||
|
|
||||||
|
return chromosome
|
||||||
|
|
||||||
|
|
||||||
|
def hoist_mutation(chromosome: Chromosome) -> Chromosome:
|
||||||
|
"""Hoist-мутация (анти-bloat).
|
||||||
|
|
||||||
|
Выбирает случайное поддерево, затем внутри него — случайное поддерево меньшей глубины,
|
||||||
|
и заменяет исходное поддерево на это внутреннее.
|
||||||
|
|
||||||
|
В результате дерево становится короче, сохраняя часть структуры.
|
||||||
|
"""
|
||||||
|
chromosome = chromosome.copy()
|
||||||
|
|
||||||
|
operation_nodes = [n for n in chromosome.root.list_nodes() if n.value.arity > 0]
|
||||||
|
if not operation_nodes:
|
||||||
|
return chromosome
|
||||||
|
|
||||||
|
outer_subtree = random.choice(operation_nodes)
|
||||||
|
outer_nodes = outer_subtree.list_nodes()[1:] # исключаем корень
|
||||||
|
|
||||||
|
inner_subtree = random.choice(outer_nodes).copy_subtree()
|
||||||
|
|
||||||
|
if outer_subtree.parent:
|
||||||
|
outer_subtree.parent.replace_child(outer_subtree, inner_subtree)
|
||||||
|
else:
|
||||||
|
chromosome.root = inner_subtree
|
||||||
|
|
||||||
|
return chromosome
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class Node:
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
def list_nodes(self) -> list[Node]:
|
def list_nodes(self) -> list[Node]:
|
||||||
|
"""Список всех узлов поддерева, начиная с текущего (aka depth-first-search)."""
|
||||||
nodes: list[Node] = [self]
|
nodes: list[Node] = [self]
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
nodes.extend(child.list_nodes())
|
nodes.extend(child.list_nodes())
|
||||||
|
|||||||
@@ -26,3 +26,63 @@ def roulette_selection(population: Population, fitnesses: Fitnesses) -> Populati
|
|||||||
selected.append(population[idx])
|
selected.append(population[idx])
|
||||||
|
|
||||||
return selected
|
return selected
|
||||||
|
|
||||||
|
|
||||||
|
def tournament_selection(
|
||||||
|
population: Population,
|
||||||
|
fitnesses: Fitnesses,
|
||||||
|
k: int = 3,
|
||||||
|
) -> Population:
|
||||||
|
"""Турнирная селекция.
|
||||||
|
|
||||||
|
В каждом турнире случайно выбирается k особей, и побеждает та,
|
||||||
|
у которой лучшее (наибольшее) значение фитнеса. Для минимизации
|
||||||
|
значения фитнеса нужно предварительно инвертировать.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
population: список особей (Population)
|
||||||
|
fitnesses: список или массив фитнесов (Fitnesses)
|
||||||
|
k: размер турнира
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Новая популяция того же размера
|
||||||
|
"""
|
||||||
|
size = len(population)
|
||||||
|
selected = []
|
||||||
|
for _ in range(size):
|
||||||
|
idxs = np.random.choice(size, size=k, replace=False)
|
||||||
|
|
||||||
|
fits = fitnesses[idxs]
|
||||||
|
|
||||||
|
winner_idx = idxs[np.argmax(fits)]
|
||||||
|
selected.append(population[winner_idx])
|
||||||
|
return selected
|
||||||
|
|
||||||
|
|
||||||
|
def stochastic_tournament_selection(
|
||||||
|
population: Population,
|
||||||
|
fitnesses: Fitnesses,
|
||||||
|
k: int = 3,
|
||||||
|
p_best: float = 0.75,
|
||||||
|
) -> Population:
|
||||||
|
"""Стохастическая турнирная селекция.
|
||||||
|
|
||||||
|
Побеждает лучший в турнире с вероятностью p_best, иначе выбирается
|
||||||
|
случайный участник турнира.
|
||||||
|
"""
|
||||||
|
size = len(population)
|
||||||
|
selected = []
|
||||||
|
|
||||||
|
for _ in range(size):
|
||||||
|
idxs = np.random.choice(size, size=k, replace=False)
|
||||||
|
fits = fitnesses[idxs]
|
||||||
|
order = np.argsort(-fits)
|
||||||
|
|
||||||
|
if np.random.random() < p_best:
|
||||||
|
winner_idx = idxs[order[0]]
|
||||||
|
else:
|
||||||
|
winner_idx = np.random.choice(idxs[1:]) if k > 1 else idxs[0]
|
||||||
|
|
||||||
|
selected.append(population[winner_idx])
|
||||||
|
|
||||||
|
return selected
|
||||||
|
|||||||
30
lab4/main.py
30
lab4/main.py
@@ -15,16 +15,21 @@ from gp.fitness import (
|
|||||||
RMSEFitness,
|
RMSEFitness,
|
||||||
)
|
)
|
||||||
from gp.ga import GARunConfig, genetic_algorithm
|
from gp.ga import GARunConfig, genetic_algorithm
|
||||||
from gp.mutations import grow_mutation, shrink_mutation
|
from gp.mutations import (
|
||||||
|
grow_mutation,
|
||||||
|
hoist_mutation,
|
||||||
|
node_replacement_mutation,
|
||||||
|
shrink_mutation,
|
||||||
|
)
|
||||||
from gp.ops import ADD, COS, DIV, EXP, MUL, NEG, POW, SIN, SQUARE, SUB
|
from gp.ops import ADD, COS, DIV, EXP, MUL, NEG, POW, SIN, SQUARE, SUB
|
||||||
from gp.population import ramped_initialization
|
from gp.population import ramped_initialization
|
||||||
from gp.primitive import Const, Var
|
from gp.primitive import Const, Var
|
||||||
from gp.selection import roulette_selection
|
from gp.selection import roulette_selection, tournament_selection
|
||||||
|
|
||||||
NUM_VARS = 9
|
NUM_VARS = 9
|
||||||
TEST_POINTS = 10000
|
TEST_POINTS = 10000
|
||||||
MAX_DEPTH = 13
|
MAX_DEPTH = 15
|
||||||
MAX_GENERATIONS = 500
|
MAX_GENERATIONS = 200
|
||||||
np.random.seed(17)
|
np.random.seed(17)
|
||||||
random.seed(17)
|
random.seed(17)
|
||||||
X = np.random.uniform(-5.536, 5.536, size=(TEST_POINTS, NUM_VARS))
|
X = np.random.uniform(-5.536, 5.536, size=(TEST_POINTS, NUM_VARS))
|
||||||
@@ -81,7 +86,7 @@ def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
|||||||
# fitness_function = PenalizedFitness(
|
# fitness_function = PenalizedFitness(
|
||||||
# target_function, lambda: X, base_fitness=fitness, lambda_=0.003
|
# target_function, lambda: X, base_fitness=fitness, lambda_=0.003
|
||||||
# )
|
# )
|
||||||
fitness_function = HuberFitness(target_function, lambda: X)
|
fitness_function = RMSEFitness(target_function, lambda: X)
|
||||||
# fitness_function = PenalizedFitness(
|
# fitness_function = PenalizedFitness(
|
||||||
# target_function, lambda: X, base_fitness=fitness, lambda_=0.003
|
# target_function, lambda: X, base_fitness=fitness, lambda_=0.003
|
||||||
# )
|
# )
|
||||||
@@ -103,9 +108,13 @@ def adaptive_mutation(
|
|||||||
|
|
||||||
r = random.random()
|
r = random.random()
|
||||||
|
|
||||||
# 50% grow, 50% shrink
|
if r < 0.4:
|
||||||
if r < 0.5:
|
|
||||||
return grow_mutation(chromosome, max_depth=max_depth)
|
return grow_mutation(chromosome, max_depth=max_depth)
|
||||||
|
elif r < 0.7:
|
||||||
|
return node_replacement_mutation(chromosome)
|
||||||
|
elif r < 0.85:
|
||||||
|
return hoist_mutation(chromosome)
|
||||||
|
|
||||||
return shrink_mutation(chromosome)
|
return shrink_mutation(chromosome)
|
||||||
|
|
||||||
|
|
||||||
@@ -151,14 +160,15 @@ config = GARunConfig(
|
|||||||
mutation_fn=lambda chrom, gen_num: adaptive_mutation(
|
mutation_fn=lambda chrom, gen_num: adaptive_mutation(
|
||||||
chrom, gen_num, MAX_GENERATIONS, MAX_DEPTH
|
chrom, gen_num, MAX_GENERATIONS, MAX_DEPTH
|
||||||
),
|
),
|
||||||
selection_fn=roulette_selection,
|
# selection_fn=roulette_selection,
|
||||||
|
selection_fn=lambda p, f: tournament_selection(p, f, k=3),
|
||||||
init_population=ramped_initialization(
|
init_population=ramped_initialization(
|
||||||
15, [4, 5, 6, 6, 7, 7, 8, 9, 10, 11], terminals, operations
|
10, [4, 5, 6, 6, 7, 7, 8, 9, 10, 11], terminals, operations
|
||||||
),
|
),
|
||||||
seed=17,
|
seed=17,
|
||||||
pc=0.9,
|
pc=0.9,
|
||||||
pm=0.3,
|
pm=0.3,
|
||||||
elitism=30,
|
elitism=10,
|
||||||
max_generations=MAX_GENERATIONS,
|
max_generations=MAX_GENERATIONS,
|
||||||
log_every_generation=True,
|
log_every_generation=True,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user