i think i've done this shit RMSE: 0.64 !!!!
This commit is contained in:
@@ -77,32 +77,6 @@ class MAEFitness(BaseFitness):
|
|||||||
return float(np.mean(np.abs(predicted - true_values)))
|
return float(np.mean(np.abs(predicted - true_values)))
|
||||||
|
|
||||||
|
|
||||||
class HuberFitness(BaseFitness):
|
|
||||||
"""Huber Loss (компромисс между MSE и MAE)"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
target_fn: TargetFunction,
|
|
||||||
test_points_fn: TestPointsFn,
|
|
||||||
delta: float = 1.0,
|
|
||||||
):
|
|
||||||
super().__init__(target_fn, test_points_fn)
|
|
||||||
self.delta = delta
|
|
||||||
|
|
||||||
def fitness_fn(
|
|
||||||
self,
|
|
||||||
chromosome: Chromosome,
|
|
||||||
predicted: NDArray[np.float64],
|
|
||||||
true_values: NDArray[np.float64],
|
|
||||||
) -> float:
|
|
||||||
error = predicted - true_values
|
|
||||||
mask = np.abs(error) <= self.delta
|
|
||||||
squared = 0.5 * (error[mask] ** 2)
|
|
||||||
linear = self.delta * (np.abs(error[~mask]) - 0.5 * self.delta)
|
|
||||||
huber = np.concatenate([squared, linear])
|
|
||||||
return float(np.mean(huber))
|
|
||||||
|
|
||||||
|
|
||||||
class NRMSEFitness(BaseFitness):
|
class NRMSEFitness(BaseFitness):
|
||||||
"""Нормализованный RMSE (масштаб-инвариантен)"""
|
"""Нормализованный RMSE (масштаб-инвариантен)"""
|
||||||
|
|
||||||
@@ -128,12 +102,18 @@ class PenalizedFitness(BaseFitness):
|
|||||||
base_fitness: BaseFitness,
|
base_fitness: BaseFitness,
|
||||||
lambda_: float = 0.001,
|
lambda_: float = 0.001,
|
||||||
depth_weight: float = 0.2,
|
depth_weight: float = 0.2,
|
||||||
|
scale_penalty: bool | None = None,
|
||||||
):
|
):
|
||||||
super().__init__(target_fn, test_points_fn)
|
super().__init__(target_fn, test_points_fn)
|
||||||
self.base_fitness = base_fitness
|
self.base_fitness = base_fitness
|
||||||
self.lambda_ = lambda_
|
self.lambda_ = lambda_
|
||||||
self.depth_weight = depth_weight
|
self.depth_weight = depth_weight
|
||||||
|
|
||||||
|
# Масштабировать штраф необязательно, если функция фитнеса нормализована
|
||||||
|
if scale_penalty is None:
|
||||||
|
scale_penalty = not isinstance(base_fitness, NRMSEFitness)
|
||||||
|
self.scale_penalty = scale_penalty
|
||||||
|
|
||||||
def fitness_fn(
|
def fitness_fn(
|
||||||
self,
|
self,
|
||||||
chromosome: Chromosome,
|
chromosome: Chromosome,
|
||||||
@@ -146,4 +126,8 @@ class PenalizedFitness(BaseFitness):
|
|||||||
depth = chromosome.root.get_depth()
|
depth = chromosome.root.get_depth()
|
||||||
|
|
||||||
penalty = self.lambda_ * (size + self.depth_weight * depth)
|
penalty = self.lambda_ * (size + self.depth_weight * depth)
|
||||||
|
|
||||||
|
if self.scale_penalty:
|
||||||
|
penalty *= base
|
||||||
|
|
||||||
return float(base + penalty)
|
return float(base + penalty)
|
||||||
|
|||||||
@@ -42,29 +42,25 @@ def grow_mutation(chromosome: Chromosome, max_depth: int) -> Chromosome:
|
|||||||
def node_replacement_mutation(chromosome: Chromosome) -> Chromosome:
|
def node_replacement_mutation(chromosome: Chromosome) -> Chromosome:
|
||||||
"""Мутация замены операции (Node Replacement Mutation).
|
"""Мутация замены операции (Node Replacement Mutation).
|
||||||
|
|
||||||
Выбирает случайный узел с операцией (arity > 0) и заменяет его
|
Выбирает случайный узел и заменяет его
|
||||||
на случайную другую операцию той же арности, сохраняя поддеревья.
|
на случайную другую операцию той же арности или терминал, сохраняя поддеревья.
|
||||||
|
|
||||||
Если подходящей альтернативы нет — возвращает копию без изменений.
|
Если подходящей альтернативы нет — возвращает копию без изменений.
|
||||||
"""
|
"""
|
||||||
chromosome = chromosome.copy()
|
chromosome = chromosome.copy()
|
||||||
|
|
||||||
operation_nodes = [n for n in chromosome.root.list_nodes() if n.value.arity > 0]
|
target_node = random.choice(chromosome.root.list_nodes())
|
||||||
if not operation_nodes:
|
|
||||||
return chromosome
|
|
||||||
|
|
||||||
target_node = random.choice(operation_nodes)
|
|
||||||
current_arity = target_node.value.arity
|
current_arity = target_node.value.arity
|
||||||
|
|
||||||
same_arity_ops = [
|
same_arity = [
|
||||||
op
|
op
|
||||||
for op in chromosome.operations
|
for op in list(chromosome.operations) + list(chromosome.terminals)
|
||||||
if op.arity == current_arity and op != target_node.value
|
if op.arity == current_arity and op != target_node.value
|
||||||
]
|
]
|
||||||
if not same_arity_ops:
|
if not same_arity:
|
||||||
return chromosome
|
return chromosome
|
||||||
|
|
||||||
new_operation = random.choice(same_arity_ops)
|
new_operation = random.choice(same_arity)
|
||||||
|
|
||||||
target_node.value = new_operation
|
target_node.value = new_operation
|
||||||
|
|
||||||
|
|||||||
109
lab4/main.py
109
lab4/main.py
@@ -7,7 +7,6 @@ from numpy.typing import NDArray
|
|||||||
from gp import Chromosome
|
from gp import Chromosome
|
||||||
from gp.crossovers import crossover_subtree
|
from gp.crossovers import crossover_subtree
|
||||||
from gp.fitness import (
|
from gp.fitness import (
|
||||||
HuberFitness,
|
|
||||||
MAEFitness,
|
MAEFitness,
|
||||||
MSEFitness,
|
MSEFitness,
|
||||||
NRMSEFitness,
|
NRMSEFitness,
|
||||||
@@ -26,12 +25,13 @@ from gp.population import ramped_initialization
|
|||||||
from gp.primitive import Const, Var
|
from gp.primitive import Const, Var
|
||||||
from gp.selection import roulette_selection, tournament_selection
|
from gp.selection import roulette_selection, tournament_selection
|
||||||
|
|
||||||
NUM_VARS = 9
|
NUM_VARS = 8
|
||||||
TEST_POINTS = 10000
|
TEST_POINTS = 10000
|
||||||
MAX_DEPTH = 15
|
MAX_DEPTH = 10
|
||||||
MAX_GENERATIONS = 200
|
MAX_GENERATIONS = 200
|
||||||
np.random.seed(17)
|
SEED = 17
|
||||||
random.seed(17)
|
np.random.seed(SEED)
|
||||||
|
random.seed(SEED)
|
||||||
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))
|
||||||
# axes = [np.linspace(-5.536, 5.536, TEST_POINTS) for _ in range(NUM_VARS)]
|
# axes = [np.linspace(-5.536, 5.536, TEST_POINTS) for _ in range(NUM_VARS)]
|
||||||
# X = np.array(np.meshgrid(*axes)).T.reshape(-1, NUM_VARS)
|
# X = np.array(np.meshgrid(*axes)).T.reshape(-1, NUM_VARS)
|
||||||
@@ -40,25 +40,6 @@ operations = [SQUARE, SIN, COS, EXP, ADD, SUB, MUL, DIV, POW]
|
|||||||
terminals = [Var(f"x{i}") for i in range(1, NUM_VARS + 1)]
|
terminals = [Var(f"x{i}") for i in range(1, NUM_VARS + 1)]
|
||||||
|
|
||||||
|
|
||||||
# def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
||||||
# """
|
|
||||||
# f(x) = x1 + x2 + sin(x1)
|
|
||||||
# x имеет форму (n_samples, n_vars)
|
|
||||||
# """
|
|
||||||
# x1 = x[:, 0]
|
|
||||||
# x2 = x[:, 1]
|
|
||||||
# return x1 + x2 + np.sin(x1) + np.sin(x2) + np.exp(-x1) + np.cos(x2)
|
|
||||||
|
|
||||||
|
|
||||||
# def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
||||||
# """
|
|
||||||
# Простая тестовая функция: сумма косинусов всех переменных.
|
|
||||||
# f(x) = sum_i cos(x_i)
|
|
||||||
# x имеет форму (n_samples, n_vars)
|
|
||||||
# """
|
|
||||||
# return np.sum(x, axis=1)
|
|
||||||
|
|
||||||
|
|
||||||
def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
||||||
"""
|
"""
|
||||||
Векторизованная версия функции:
|
Векторизованная версия функции:
|
||||||
@@ -72,23 +53,16 @@ def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
|||||||
return np.sum(prefix_sums, axis=1)
|
return np.sum(prefix_sums, axis=1)
|
||||||
|
|
||||||
|
|
||||||
# def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
||||||
# """
|
|
||||||
# Rastrigin function.
|
|
||||||
# f(x) = 10 * n + sum(x_i^2 - 10 * cos(2πx_i))
|
|
||||||
# x: shape (n_samples, n_vars)
|
|
||||||
# """
|
|
||||||
# n = x.shape[1]
|
|
||||||
# return 10 * n + np.sum(x**2 - 10 * np.cos(2 * np.pi * x), axis=1)
|
|
||||||
|
|
||||||
# fitness_function = MSEFitness(target_function, lambda: X)
|
# fitness_function = MSEFitness(target_function, lambda: X)
|
||||||
# fitness = HuberFitness(target_function, lambda: X, delta=1.0)
|
# fitness_function = HuberFitness(target_function, lambda: X, delta=0.5)
|
||||||
# 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.1
|
||||||
# )
|
# )
|
||||||
|
# fitness_function = NRMSEFitness(target_function, lambda: X)
|
||||||
fitness_function = RMSEFitness(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_function, lambda_=0.0001
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
|
||||||
@@ -98,14 +72,6 @@ def adaptive_mutation(
|
|||||||
max_generations: int,
|
max_generations: int,
|
||||||
max_depth: int,
|
max_depth: int,
|
||||||
) -> Chromosome:
|
) -> Chromosome:
|
||||||
"""Адаптивная мутация.
|
|
||||||
|
|
||||||
Меняет вероятность типов мутации по ходу эволюции:
|
|
||||||
- Ранняя фаза (<30%): 70% grow, 30% shrink
|
|
||||||
- Средняя фаза (30–70%): 40% grow, 60% shrink
|
|
||||||
- Поздняя фаза (>=70%): 20% grow, 80% shrink
|
|
||||||
"""
|
|
||||||
|
|
||||||
r = random.random()
|
r = random.random()
|
||||||
|
|
||||||
if r < 0.4:
|
if r < 0.4:
|
||||||
@@ -118,57 +84,25 @@ def adaptive_mutation(
|
|||||||
return shrink_mutation(chromosome)
|
return shrink_mutation(chromosome)
|
||||||
|
|
||||||
|
|
||||||
# def adaptive_mutation(
|
init_population = ramped_initialization(
|
||||||
# chromosome: Chromosome,
|
20, [i for i in range(MAX_DEPTH - 9, MAX_DEPTH + 1)], terminals, operations
|
||||||
# generation: int,
|
)
|
||||||
# max_generations: int,
|
|
||||||
# max_depth: int,
|
|
||||||
# ) -> Chromosome:
|
|
||||||
# """Адаптивная мутация.
|
|
||||||
|
|
||||||
# Меняет вероятность типов мутации по ходу эволюции:
|
|
||||||
# - Ранняя фаза (<30%): 70% grow, 30% shrink
|
|
||||||
# - Средняя фаза (30–70%): 40% grow, 60% shrink
|
|
||||||
# - Поздняя фаза (>=70%): 20% grow, 80% shrink
|
|
||||||
# """
|
|
||||||
|
|
||||||
# # Вычисляем прогресс в диапазоне [0, 1]
|
|
||||||
# if max_generations <= 0:
|
|
||||||
# progress = 0.0
|
|
||||||
# else:
|
|
||||||
# progress = min(1.0, max(0.0, generation / max_generations))
|
|
||||||
|
|
||||||
# r = random.random()
|
|
||||||
|
|
||||||
# # Определяем тип мутации
|
|
||||||
# if progress < 0.3:
|
|
||||||
# do_grow = r < 0.7
|
|
||||||
# elif progress < 0.7:
|
|
||||||
# do_grow = r < 0.4
|
|
||||||
# else:
|
|
||||||
# do_grow = r < 0.2
|
|
||||||
|
|
||||||
# # Выполняем выбранную мутацию
|
|
||||||
# if do_grow:
|
|
||||||
# return grow_mutation(chromosome, max_depth=max_depth)
|
|
||||||
# return shrink_mutation(chromosome)
|
|
||||||
|
|
||||||
|
print("Population size:", len(init_population))
|
||||||
|
|
||||||
config = GARunConfig(
|
config = GARunConfig(
|
||||||
fitness_func=fitness_function,
|
fitness_func=fitness_function,
|
||||||
crossover_fn=lambda p1, p2: crossover_subtree(p1, p2, max_depth=8),
|
crossover_fn=lambda p1, p2: crossover_subtree(p1, p2, max_depth=MAX_DEPTH),
|
||||||
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),
|
selection_fn=lambda p, f: tournament_selection(p, f, k=3),
|
||||||
init_population=ramped_initialization(
|
init_population=init_population,
|
||||||
10, [4, 5, 6, 6, 7, 7, 8, 9, 10, 11], terminals, operations
|
seed=SEED,
|
||||||
),
|
pc=0.85,
|
||||||
seed=17,
|
pm=0.15,
|
||||||
pc=0.9,
|
elitism=15,
|
||||||
pm=0.3,
|
|
||||||
elitism=10,
|
|
||||||
max_generations=MAX_GENERATIONS,
|
max_generations=MAX_GENERATIONS,
|
||||||
log_every_generation=True,
|
log_every_generation=True,
|
||||||
)
|
)
|
||||||
@@ -182,6 +116,7 @@ print(result.best_generation.best.root.to_str_tree())
|
|||||||
print(f"Лучшее значение фитнеса: {result.best_generation.best_fitness:.6f}")
|
print(f"Лучшее значение фитнеса: {result.best_generation.best_fitness:.6f}")
|
||||||
print(f"Количество поколений: {result.generations_count}")
|
print(f"Количество поколений: {result.generations_count}")
|
||||||
print(f"Время выполнения: {result.time_ms:.2f} мс")
|
print(f"Время выполнения: {result.time_ms:.2f} мс")
|
||||||
|
print("Population size:", len(init_population))
|
||||||
|
|
||||||
mse_fitness = MSEFitness(target_function, lambda: X)
|
mse_fitness = MSEFitness(target_function, lambda: X)
|
||||||
print(f"MSE: {mse_fitness(result.best_generation.best):.6f}")
|
print(f"MSE: {mse_fitness(result.best_generation.best):.6f}")
|
||||||
@@ -189,7 +124,5 @@ rmse_fitness = RMSEFitness(target_function, lambda: X)
|
|||||||
print(f"RMSE: {rmse_fitness(result.best_generation.best):.6f}")
|
print(f"RMSE: {rmse_fitness(result.best_generation.best):.6f}")
|
||||||
mae_fitness = MAEFitness(target_function, lambda: X)
|
mae_fitness = MAEFitness(target_function, lambda: X)
|
||||||
print(f"MAE: {mae_fitness(result.best_generation.best):.6f}")
|
print(f"MAE: {mae_fitness(result.best_generation.best):.6f}")
|
||||||
huber_fitness = HuberFitness(target_function, lambda: X, delta=1.0)
|
|
||||||
print(f"Huber: {huber_fitness(result.best_generation.best):.6f}")
|
|
||||||
nrmse_fitness = NRMSEFitness(target_function, lambda: X)
|
nrmse_fitness = NRMSEFitness(target_function, lambda: X)
|
||||||
print(f"NRMSE: {nrmse_fitness(result.best_generation.best):.6f}")
|
print(f"NRMSE: {nrmse_fitness(result.best_generation.best):.6f}")
|
||||||
|
|||||||
Reference in New Issue
Block a user