754 lines
28 KiB
Python
754 lines
28 KiB
Python
![]() |
#!/usr/bin/env python3
|
|||
|
"""
|
|||
|
Advanced Parameter Optimization for TTA-E Neural Decoding
|
|||
|
支持多种优化算法:差分进化、遗传算法、贝叶斯优化、粒子群优化
|
|||
|
"""
|
|||
|
|
|||
|
import os
|
|||
|
import sys
|
|||
|
import numpy as np
|
|||
|
import pickle
|
|||
|
import argparse
|
|||
|
import time
|
|||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|||
|
from typing import Dict, List, Tuple, Optional
|
|||
|
import json
|
|||
|
from dataclasses import dataclass
|
|||
|
from pathlib import Path
|
|||
|
|
|||
|
# GPU加速支持
|
|||
|
try:
|
|||
|
import cupy as cp
|
|||
|
GPU_AVAILABLE = True
|
|||
|
print("GPU acceleration available with CuPy")
|
|||
|
except ImportError:
|
|||
|
import numpy as cp
|
|||
|
GPU_AVAILABLE = False
|
|||
|
print("Using CPU computation with NumPy")
|
|||
|
|
|||
|
# 优化算法库
|
|||
|
try:
|
|||
|
from scipy.optimize import differential_evolution
|
|||
|
SCIPY_AVAILABLE = True
|
|||
|
except ImportError:
|
|||
|
SCIPY_AVAILABLE = False
|
|||
|
print("SciPy not available, some algorithms will be disabled")
|
|||
|
|
|||
|
try:
|
|||
|
import skopt
|
|||
|
from skopt import gp_minimize
|
|||
|
from skopt.space import Real
|
|||
|
SKOPT_AVAILABLE = True
|
|||
|
except ImportError:
|
|||
|
SKOPT_AVAILABLE = False
|
|||
|
print("scikit-optimize not available, Bayesian optimization disabled")
|
|||
|
|
|||
|
# 设置随机种子
|
|||
|
np.random.seed(42)
|
|||
|
if GPU_AVAILABLE:
|
|||
|
cp.random.seed(42)
|
|||
|
|
|||
|
@dataclass
|
|||
|
class OptimizationResult:
|
|||
|
"""优化结果数据类"""
|
|||
|
best_params: Dict
|
|||
|
best_score: float
|
|||
|
optimization_history: List[Tuple[Dict, float]]
|
|||
|
total_evaluations: int
|
|||
|
elapsed_time: float
|
|||
|
algorithm: str
|
|||
|
|
|||
|
def to_cpu(x):
|
|||
|
"""将CuPy数组转换为NumPy数组"""
|
|||
|
if GPU_AVAILABLE and hasattr(x, 'get'):
|
|||
|
return x.get()
|
|||
|
return x
|
|||
|
|
|||
|
def load_base_predictions(cache_file='base_predictions_cache.pkl'):
|
|||
|
"""加载预计算的基础预测结果"""
|
|||
|
if os.path.exists(cache_file):
|
|||
|
print(f"Loading base predictions from {cache_file}")
|
|||
|
with open(cache_file, 'rb') as f:
|
|||
|
return pickle.load(f)
|
|||
|
else:
|
|||
|
print(f"Cache file {cache_file} not found. Please run generate_base_predictions() first.")
|
|||
|
return None
|
|||
|
|
|||
|
def evaluate_config_gpu(params_dict, base_predictions_data):
|
|||
|
"""GPU加速的配置评估函数"""
|
|||
|
try:
|
|||
|
# 解析参数
|
|||
|
tta_weights = [params_dict[f'tta_weight_{i}'] for i in range(5)]
|
|||
|
gru_weight = params_dict['gru_weight']
|
|||
|
|
|||
|
# 转换为GPU数组(如果可用)
|
|||
|
if GPU_AVAILABLE:
|
|||
|
tta_weights = cp.array(tta_weights)
|
|||
|
else:
|
|||
|
tta_weights = np.array(tta_weights)
|
|||
|
|
|||
|
total_per = 0.0
|
|||
|
total_chars = 0
|
|||
|
|
|||
|
for trial_data in base_predictions_data:
|
|||
|
gru_probs = trial_data['gru_probs'] # shape: (5, seq_len, vocab_size)
|
|||
|
lstm_probs = trial_data['lstm_probs'] # shape: (5, seq_len, vocab_size)
|
|||
|
true_chars = trial_data['true_chars']
|
|||
|
|
|||
|
# 转换为GPU数组
|
|||
|
if GPU_AVAILABLE:
|
|||
|
gru_probs = cp.asarray(gru_probs)
|
|||
|
lstm_probs = cp.asarray(lstm_probs)
|
|||
|
|
|||
|
# 计算TTA加权平均
|
|||
|
tta_weights_norm = tta_weights / cp.sum(tta_weights) if cp.sum(tta_weights) > 0 else cp.ones_like(tta_weights) / len(tta_weights)
|
|||
|
|
|||
|
# 对每个TTA样本加权
|
|||
|
gru_weighted = cp.sum(gru_probs * tta_weights_norm[:, None, None], axis=0)
|
|||
|
lstm_weighted = cp.sum(lstm_probs * tta_weights_norm[:, None, None], axis=0)
|
|||
|
|
|||
|
# 模型集成
|
|||
|
total_weight = gru_weight + (1 - gru_weight)
|
|||
|
if total_weight > 0:
|
|||
|
ensemble_probs = (gru_weighted * gru_weight + lstm_weighted * (1 - gru_weight)) / total_weight
|
|||
|
else:
|
|||
|
ensemble_probs = (gru_weighted + lstm_weighted) / 2
|
|||
|
|
|||
|
# 解码预测
|
|||
|
if GPU_AVAILABLE:
|
|||
|
predicted_chars = cp.argmax(ensemble_probs, axis=1)
|
|||
|
predicted_chars = to_cpu(predicted_chars)
|
|||
|
else:
|
|||
|
predicted_chars = np.argmax(ensemble_probs, axis=1)
|
|||
|
|
|||
|
# 计算PER
|
|||
|
per = calculate_per(predicted_chars, true_chars)
|
|||
|
total_per += per * len(true_chars)
|
|||
|
total_chars += len(true_chars)
|
|||
|
|
|||
|
avg_per = total_per / total_chars if total_chars > 0 else 1.0
|
|||
|
return avg_per
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
print(f"Error in evaluate_config_gpu: {e}")
|
|||
|
return 1.0 # 返回最差分数
|
|||
|
|
|||
|
def calculate_per(predicted, true):
|
|||
|
"""计算音素错误率 (PER)"""
|
|||
|
if len(predicted) == 0 and len(true) == 0:
|
|||
|
return 0.0
|
|||
|
if len(predicted) == 0 or len(true) == 0:
|
|||
|
return 1.0
|
|||
|
|
|||
|
# 简单的字符级编辑距离
|
|||
|
n, m = len(predicted), len(true)
|
|||
|
dp = np.zeros((n + 1, m + 1))
|
|||
|
|
|||
|
for i in range(n + 1):
|
|||
|
dp[i][0] = i
|
|||
|
for j in range(m + 1):
|
|||
|
dp[0][j] = j
|
|||
|
|
|||
|
for i in range(1, n + 1):
|
|||
|
for j in range(1, m + 1):
|
|||
|
if predicted[i-1] == true[j-1]:
|
|||
|
dp[i][j] = dp[i-1][j-1]
|
|||
|
else:
|
|||
|
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
|
|||
|
|
|||
|
return dp[n][m] / max(n, m)
|
|||
|
|
|||
|
class DifferentialEvolution:
|
|||
|
"""差分进化算法"""
|
|||
|
|
|||
|
def __init__(self, bounds, popsize=15, maxiter=100, mutation=0.5, recombination=0.7):
|
|||
|
self.bounds = bounds
|
|||
|
self.popsize = popsize
|
|||
|
self.maxiter = maxiter
|
|||
|
self.mutation = mutation
|
|||
|
self.recombination = recombination
|
|||
|
|
|||
|
def optimize(self, objective_func, base_predictions):
|
|||
|
"""运行差分进化优化"""
|
|||
|
print(f"Starting Differential Evolution (popsize={self.popsize}, maxiter={self.maxiter})")
|
|||
|
|
|||
|
param_names = list(self.bounds.keys())
|
|||
|
scipy_bounds = [self.bounds[name] for name in param_names]
|
|||
|
|
|||
|
def objective_wrapper(x):
|
|||
|
params = {param_names[i]: x[i] for i in range(len(x))}
|
|||
|
return objective_func(params, base_predictions)
|
|||
|
|
|||
|
start_time = time.time()
|
|||
|
|
|||
|
if SCIPY_AVAILABLE:
|
|||
|
result = differential_evolution(
|
|||
|
objective_wrapper,
|
|||
|
scipy_bounds,
|
|||
|
popsize=self.popsize,
|
|||
|
maxiter=self.maxiter,
|
|||
|
mutation=self.mutation,
|
|||
|
recombination=self.recombination,
|
|||
|
seed=42,
|
|||
|
disp=True
|
|||
|
)
|
|||
|
|
|||
|
best_params = {param_names[i]: result.x[i] for i in range(len(param_names))}
|
|||
|
|
|||
|
return OptimizationResult(
|
|||
|
best_params=best_params,
|
|||
|
best_score=result.fun,
|
|||
|
optimization_history=[], # SciPy doesn't provide history
|
|||
|
total_evaluations=result.nfev,
|
|||
|
elapsed_time=time.time() - start_time,
|
|||
|
algorithm="Differential Evolution"
|
|||
|
)
|
|||
|
else:
|
|||
|
# 简单的手动实现
|
|||
|
return self._manual_de(objective_wrapper, scipy_bounds, param_names, start_time)
|
|||
|
|
|||
|
def _manual_de(self, objective_func, bounds, param_names, start_time):
|
|||
|
"""手动实现的差分进化"""
|
|||
|
n_params = len(bounds)
|
|||
|
|
|||
|
# 初始化种群
|
|||
|
population = np.random.rand(self.popsize, n_params)
|
|||
|
for i in range(n_params):
|
|||
|
low, high = bounds[i]
|
|||
|
population[:, i] = population[:, i] * (high - low) + low
|
|||
|
|
|||
|
# 评估初始种群
|
|||
|
fitness = np.array([objective_func(ind) for ind in population])
|
|||
|
|
|||
|
history = []
|
|||
|
best_idx = np.argmin(fitness)
|
|||
|
best_individual = population[best_idx].copy()
|
|||
|
best_fitness = fitness[best_idx]
|
|||
|
|
|||
|
print(f"Initial best fitness: {best_fitness:.6f}")
|
|||
|
|
|||
|
for generation in range(self.maxiter):
|
|||
|
new_population = population.copy()
|
|||
|
|
|||
|
for i in range(self.popsize):
|
|||
|
# 选择三个不同的个体
|
|||
|
candidates = list(range(self.popsize))
|
|||
|
candidates.remove(i)
|
|||
|
a, b, c = np.random.choice(candidates, 3, replace=False)
|
|||
|
|
|||
|
# 变异
|
|||
|
mutant = population[a] + self.mutation * (population[b] - population[c])
|
|||
|
|
|||
|
# 边界处理
|
|||
|
for j in range(n_params):
|
|||
|
low, high = bounds[j]
|
|||
|
mutant[j] = np.clip(mutant[j], low, high)
|
|||
|
|
|||
|
# 交叉
|
|||
|
trial = population[i].copy()
|
|||
|
crossover_points = np.random.rand(n_params) < self.recombination
|
|||
|
trial[crossover_points] = mutant[crossover_points]
|
|||
|
|
|||
|
# 评估试验个体
|
|||
|
trial_fitness = objective_func(trial)
|
|||
|
|
|||
|
# 选择
|
|||
|
if trial_fitness < fitness[i]:
|
|||
|
new_population[i] = trial
|
|||
|
fitness[i] = trial_fitness
|
|||
|
|
|||
|
if trial_fitness < best_fitness:
|
|||
|
best_individual = trial.copy()
|
|||
|
best_fitness = trial_fitness
|
|||
|
print(f"Generation {generation+1}: New best fitness {best_fitness:.6f}")
|
|||
|
|
|||
|
population = new_population
|
|||
|
history.append((best_individual.copy(), best_fitness))
|
|||
|
|
|||
|
best_params = {param_names[i]: best_individual[i] for i in range(len(param_names))}
|
|||
|
|
|||
|
return OptimizationResult(
|
|||
|
best_params=best_params,
|
|||
|
best_score=best_fitness,
|
|||
|
optimization_history=history,
|
|||
|
total_evaluations=self.popsize * (1 + self.maxiter),
|
|||
|
elapsed_time=time.time() - start_time,
|
|||
|
algorithm="Differential Evolution (Manual)"
|
|||
|
)
|
|||
|
|
|||
|
class GeneticAlgorithm:
|
|||
|
"""遗传算法"""
|
|||
|
|
|||
|
def __init__(self, bounds, popsize=50, generations=100, mutation_rate=0.1, crossover_rate=0.8):
|
|||
|
self.bounds = bounds
|
|||
|
self.popsize = popsize
|
|||
|
self.generations = generations
|
|||
|
self.mutation_rate = mutation_rate
|
|||
|
self.crossover_rate = crossover_rate
|
|||
|
|
|||
|
def optimize(self, objective_func, base_predictions):
|
|||
|
"""运行遗传算法优化"""
|
|||
|
print(f"Starting Genetic Algorithm (popsize={self.popsize}, generations={self.generations})")
|
|||
|
|
|||
|
param_names = list(self.bounds.keys())
|
|||
|
n_params = len(param_names)
|
|||
|
|
|||
|
start_time = time.time()
|
|||
|
|
|||
|
# 初始化种群
|
|||
|
population = []
|
|||
|
for _ in range(self.popsize):
|
|||
|
individual = {}
|
|||
|
for name in param_names:
|
|||
|
low, high = self.bounds[name]
|
|||
|
individual[name] = np.random.uniform(low, high)
|
|||
|
population.append(individual)
|
|||
|
|
|||
|
# 评估初始种群
|
|||
|
fitness_scores = [objective_func(ind, base_predictions) for ind in population]
|
|||
|
|
|||
|
history = []
|
|||
|
best_idx = np.argmin(fitness_scores)
|
|||
|
best_individual = population[best_idx].copy()
|
|||
|
best_score = fitness_scores[best_idx]
|
|||
|
|
|||
|
print(f"Initial best fitness: {best_score:.6f}")
|
|||
|
|
|||
|
for generation in range(self.generations):
|
|||
|
# 选择(锦标赛选择)
|
|||
|
new_population = []
|
|||
|
for _ in range(self.popsize):
|
|||
|
parent1 = self._tournament_selection(population, fitness_scores)
|
|||
|
parent2 = self._tournament_selection(population, fitness_scores)
|
|||
|
|
|||
|
# 交叉
|
|||
|
if np.random.rand() < self.crossover_rate:
|
|||
|
child = self._crossover(parent1, parent2, param_names)
|
|||
|
else:
|
|||
|
child = parent1.copy()
|
|||
|
|
|||
|
# 变异
|
|||
|
child = self._mutate(child, param_names)
|
|||
|
new_population.append(child)
|
|||
|
|
|||
|
# 评估新种群
|
|||
|
population = new_population
|
|||
|
fitness_scores = [objective_func(ind, base_predictions) for ind in population]
|
|||
|
|
|||
|
# 更新最佳个体
|
|||
|
current_best_idx = np.argmin(fitness_scores)
|
|||
|
current_best_score = fitness_scores[current_best_idx]
|
|||
|
|
|||
|
if current_best_score < best_score:
|
|||
|
best_individual = population[current_best_idx].copy()
|
|||
|
best_score = current_best_score
|
|||
|
print(f"Generation {generation+1}: New best fitness {best_score:.6f}")
|
|||
|
|
|||
|
history.append((best_individual.copy(), best_score))
|
|||
|
|
|||
|
return OptimizationResult(
|
|||
|
best_params=best_individual,
|
|||
|
best_score=best_score,
|
|||
|
optimization_history=history,
|
|||
|
total_evaluations=self.popsize * (1 + self.generations),
|
|||
|
elapsed_time=time.time() - start_time,
|
|||
|
algorithm="Genetic Algorithm"
|
|||
|
)
|
|||
|
|
|||
|
def _tournament_selection(self, population, fitness_scores, tournament_size=3):
|
|||
|
"""锦标赛选择"""
|
|||
|
tournament_indices = np.random.choice(len(population), tournament_size, replace=False)
|
|||
|
tournament_fitness = [fitness_scores[i] for i in tournament_indices]
|
|||
|
winner_idx = tournament_indices[np.argmin(tournament_fitness)]
|
|||
|
return population[winner_idx].copy()
|
|||
|
|
|||
|
def _crossover(self, parent1, parent2, param_names):
|
|||
|
"""均匀交叉"""
|
|||
|
child = {}
|
|||
|
for name in param_names:
|
|||
|
if np.random.rand() < 0.5:
|
|||
|
child[name] = parent1[name]
|
|||
|
else:
|
|||
|
child[name] = parent2[name]
|
|||
|
return child
|
|||
|
|
|||
|
def _mutate(self, individual, param_names):
|
|||
|
"""高斯变异"""
|
|||
|
mutated = individual.copy()
|
|||
|
for name in param_names:
|
|||
|
if np.random.rand() < self.mutation_rate:
|
|||
|
low, high = self.bounds[name]
|
|||
|
# 高斯变异,标准差为范围的10%
|
|||
|
noise = np.random.normal(0, (high - low) * 0.1)
|
|||
|
mutated[name] = np.clip(individual[name] + noise, low, high)
|
|||
|
return mutated
|
|||
|
|
|||
|
class ParticleSwarmOptimization:
|
|||
|
"""粒子群优化算法"""
|
|||
|
|
|||
|
def __init__(self, bounds, n_particles=30, max_iter=100, w=0.5, c1=1.5, c2=1.5):
|
|||
|
self.bounds = bounds
|
|||
|
self.n_particles = n_particles
|
|||
|
self.max_iter = max_iter
|
|||
|
self.w = w # 惯性权重
|
|||
|
self.c1 = c1 # 个体学习因子
|
|||
|
self.c2 = c2 # 社会学习因子
|
|||
|
|
|||
|
def optimize(self, objective_func, base_predictions):
|
|||
|
"""运行粒子群优化"""
|
|||
|
print(f"Starting Particle Swarm Optimization (particles={self.n_particles}, iterations={self.max_iter})")
|
|||
|
|
|||
|
param_names = list(self.bounds.keys())
|
|||
|
n_params = len(param_names)
|
|||
|
|
|||
|
start_time = time.time()
|
|||
|
|
|||
|
# 初始化粒子位置和速度
|
|||
|
positions = np.zeros((self.n_particles, n_params))
|
|||
|
velocities = np.zeros((self.n_particles, n_params))
|
|||
|
personal_best_positions = np.zeros((self.n_particles, n_params))
|
|||
|
personal_best_scores = np.full(self.n_particles, float('inf'))
|
|||
|
|
|||
|
# 初始化位置
|
|||
|
for i in range(n_params):
|
|||
|
low, high = self.bounds[param_names[i]]
|
|||
|
positions[:, i] = np.random.uniform(low, high, self.n_particles)
|
|||
|
velocities[:, i] = np.random.uniform(-abs(high-low)*0.1, abs(high-low)*0.1, self.n_particles)
|
|||
|
|
|||
|
# 评估初始位置
|
|||
|
for i in range(self.n_particles):
|
|||
|
params = {param_names[j]: positions[i, j] for j in range(n_params)}
|
|||
|
score = objective_func(params, base_predictions)
|
|||
|
|
|||
|
personal_best_positions[i] = positions[i].copy()
|
|||
|
personal_best_scores[i] = score
|
|||
|
|
|||
|
global_best_idx = np.argmin(personal_best_scores)
|
|||
|
global_best_position = personal_best_positions[global_best_idx].copy()
|
|||
|
global_best_score = personal_best_scores[global_best_idx]
|
|||
|
|
|||
|
print(f"Initial best fitness: {global_best_score:.6f}")
|
|||
|
|
|||
|
history = []
|
|||
|
|
|||
|
for iteration in range(self.max_iter):
|
|||
|
for i in range(self.n_particles):
|
|||
|
# 更新速度
|
|||
|
r1, r2 = np.random.rand(2)
|
|||
|
velocities[i] = (self.w * velocities[i] +
|
|||
|
self.c1 * r1 * (personal_best_positions[i] - positions[i]) +
|
|||
|
self.c2 * r2 * (global_best_position - positions[i]))
|
|||
|
|
|||
|
# 更新位置
|
|||
|
positions[i] += velocities[i]
|
|||
|
|
|||
|
# 边界处理
|
|||
|
for j in range(n_params):
|
|||
|
low, high = self.bounds[param_names[j]]
|
|||
|
positions[i, j] = np.clip(positions[i, j], low, high)
|
|||
|
|
|||
|
# 评估新位置
|
|||
|
params = {param_names[j]: positions[i, j] for j in range(n_params)}
|
|||
|
score = objective_func(params, base_predictions)
|
|||
|
|
|||
|
# 更新个体最佳
|
|||
|
if score < personal_best_scores[i]:
|
|||
|
personal_best_positions[i] = positions[i].copy()
|
|||
|
personal_best_scores[i] = score
|
|||
|
|
|||
|
# 更新全局最佳
|
|||
|
if score < global_best_score:
|
|||
|
global_best_position = positions[i].copy()
|
|||
|
global_best_score = score
|
|||
|
print(f"Iteration {iteration+1}: New best fitness {global_best_score:.6f}")
|
|||
|
|
|||
|
history.append((global_best_position.copy(), global_best_score))
|
|||
|
|
|||
|
best_params = {param_names[i]: global_best_position[i] for i in range(len(param_names))}
|
|||
|
|
|||
|
return OptimizationResult(
|
|||
|
best_params=best_params,
|
|||
|
best_score=global_best_score,
|
|||
|
optimization_history=history,
|
|||
|
total_evaluations=self.n_particles * (1 + self.max_iter),
|
|||
|
elapsed_time=time.time() - start_time,
|
|||
|
algorithm="Particle Swarm Optimization"
|
|||
|
)
|
|||
|
|
|||
|
class BayesianOptimization:
|
|||
|
"""贝叶斯优化(需要scikit-optimize)"""
|
|||
|
|
|||
|
def __init__(self, bounds, n_calls=100, n_initial_points=10, acq_func='gp_hedge'):
|
|||
|
self.bounds = bounds
|
|||
|
self.n_calls = n_calls
|
|||
|
self.n_initial_points = n_initial_points
|
|||
|
self.acq_func = acq_func
|
|||
|
|
|||
|
def optimize(self, objective_func, base_predictions):
|
|||
|
"""运行贝叶斯优化"""
|
|||
|
if not SKOPT_AVAILABLE:
|
|||
|
raise ImportError("scikit-optimize is required for Bayesian optimization")
|
|||
|
|
|||
|
print(f"Starting Bayesian Optimization (calls={self.n_calls}, initial_points={self.n_initial_points})")
|
|||
|
|
|||
|
param_names = list(self.bounds.keys())
|
|||
|
dimensions = [Real(self.bounds[name][0], self.bounds[name][1], name=name) for name in param_names]
|
|||
|
|
|||
|
def objective_wrapper(x):
|
|||
|
params = {param_names[i]: x[i] for i in range(len(x))}
|
|||
|
return objective_func(params, base_predictions)
|
|||
|
|
|||
|
start_time = time.time()
|
|||
|
|
|||
|
result = gp_minimize(
|
|||
|
func=objective_wrapper,
|
|||
|
dimensions=dimensions,
|
|||
|
n_calls=self.n_calls,
|
|||
|
n_initial_points=self.n_initial_points,
|
|||
|
acq_func=self.acq_func,
|
|||
|
random_state=42
|
|||
|
)
|
|||
|
|
|||
|
best_params = {param_names[i]: result.x[i] for i in range(len(param_names))}
|
|||
|
|
|||
|
# 构建历史记录
|
|||
|
history = []
|
|||
|
for i in range(len(result.x_iters)):
|
|||
|
params = {param_names[j]: result.x_iters[i][j] for j in range(len(param_names))}
|
|||
|
score = result.func_vals[i]
|
|||
|
history.append((params, score))
|
|||
|
|
|||
|
return OptimizationResult(
|
|||
|
best_params=best_params,
|
|||
|
best_score=result.fun,
|
|||
|
optimization_history=history,
|
|||
|
total_evaluations=len(result.x_iters),
|
|||
|
elapsed_time=time.time() - start_time,
|
|||
|
algorithm="Bayesian Optimization"
|
|||
|
)
|
|||
|
|
|||
|
def generate_base_predictions():
|
|||
|
"""生成基础预测结果缓存"""
|
|||
|
print("Generating base predictions cache...")
|
|||
|
# 这里应该调用原始的评估代码来生成所有TTA样本的预测
|
|||
|
# 为了演示,我们创建一个简单的模拟数据
|
|||
|
|
|||
|
cache_file = 'base_predictions_cache.pkl'
|
|||
|
if os.path.exists(cache_file):
|
|||
|
print(f"Cache file {cache_file} already exists.")
|
|||
|
return
|
|||
|
|
|||
|
# 模拟数据 - 实际使用时应该替换为真实的预测生成代码
|
|||
|
print("Generating mock base predictions for demonstration...")
|
|||
|
|
|||
|
n_trials = 10 # 模拟10个试验
|
|||
|
seq_len = 50 # 序列长度
|
|||
|
vocab_size = 31 # 词汇表大小
|
|||
|
n_tta = 5 # TTA样本数
|
|||
|
|
|||
|
base_predictions = []
|
|||
|
|
|||
|
for trial in range(n_trials):
|
|||
|
# 模拟GRU和LSTM的概率预测
|
|||
|
gru_probs = np.random.rand(n_tta, seq_len, vocab_size)
|
|||
|
lstm_probs = np.random.rand(n_tta, seq_len, vocab_size)
|
|||
|
|
|||
|
# 归一化为概率分布
|
|||
|
gru_probs = gru_probs / np.sum(gru_probs, axis=2, keepdims=True)
|
|||
|
lstm_probs = lstm_probs / np.sum(lstm_probs, axis=2, keepdims=True)
|
|||
|
|
|||
|
# 模拟真实字符序列
|
|||
|
true_chars = np.random.randint(0, vocab_size, seq_len)
|
|||
|
|
|||
|
base_predictions.append({
|
|||
|
'gru_probs': gru_probs,
|
|||
|
'lstm_probs': lstm_probs,
|
|||
|
'true_chars': true_chars
|
|||
|
})
|
|||
|
|
|||
|
# 保存缓存
|
|||
|
with open(cache_file, 'wb') as f:
|
|||
|
pickle.dump(base_predictions, f)
|
|||
|
|
|||
|
print(f"Base predictions cache saved to {cache_file}")
|
|||
|
|
|||
|
def save_results(results: List[OptimizationResult], output_dir='optimization_results'):
|
|||
|
"""保存优化结果"""
|
|||
|
os.makedirs(output_dir, exist_ok=True)
|
|||
|
|
|||
|
# 保存详细结果
|
|||
|
for result in results:
|
|||
|
filename = f"{result.algorithm.lower().replace(' ', '_')}_result.json"
|
|||
|
filepath = os.path.join(output_dir, filename)
|
|||
|
|
|||
|
result_dict = {
|
|||
|
'algorithm': result.algorithm,
|
|||
|
'best_params': result.best_params,
|
|||
|
'best_score': result.best_score,
|
|||
|
'total_evaluations': result.total_evaluations,
|
|||
|
'elapsed_time': result.elapsed_time,
|
|||
|
'optimization_history': [
|
|||
|
{'params': params if isinstance(params, dict) else params.tolist(), 'score': score}
|
|||
|
for params, score in result.optimization_history
|
|||
|
]
|
|||
|
}
|
|||
|
|
|||
|
with open(filepath, 'w') as f:
|
|||
|
json.dump(result_dict, f, indent=2)
|
|||
|
|
|||
|
print(f"Results saved to {filepath}")
|
|||
|
|
|||
|
# 保存汇总比较
|
|||
|
summary_file = os.path.join(output_dir, 'optimization_summary.json')
|
|||
|
summary = {
|
|||
|
'comparison': [
|
|||
|
{
|
|||
|
'algorithm': result.algorithm,
|
|||
|
'best_score': result.best_score,
|
|||
|
'total_evaluations': result.total_evaluations,
|
|||
|
'elapsed_time': result.elapsed_time,
|
|||
|
'best_params': result.best_params
|
|||
|
}
|
|||
|
for result in results
|
|||
|
],
|
|||
|
'best_overall': min(results, key=lambda x: x.best_score).__dict__
|
|||
|
}
|
|||
|
|
|||
|
# 处理numpy数组序列化
|
|||
|
best_overall = summary['best_overall']
|
|||
|
if hasattr(best_overall['best_params'], 'tolist'):
|
|||
|
best_overall['best_params'] = best_overall['best_params'].tolist()
|
|||
|
|
|||
|
with open(summary_file, 'w') as f:
|
|||
|
json.dump(summary, f, indent=2, default=str)
|
|||
|
|
|||
|
print(f"Summary saved to {summary_file}")
|
|||
|
|
|||
|
def main():
|
|||
|
parser = argparse.ArgumentParser(description='Advanced Parameter Optimization for TTA-E')
|
|||
|
parser.add_argument('--algorithms', type=str, nargs='+',
|
|||
|
default=['de', 'ga', 'pso'],
|
|||
|
choices=['de', 'ga', 'pso', 'bayes', 'all'],
|
|||
|
help='Optimization algorithms to run')
|
|||
|
parser.add_argument('--generate_cache', action='store_true',
|
|||
|
help='Generate base predictions cache')
|
|||
|
parser.add_argument('--cache_file', type=str, default='base_predictions_cache.pkl',
|
|||
|
help='Base predictions cache file')
|
|||
|
parser.add_argument('--output_dir', type=str, default='optimization_results',
|
|||
|
help='Output directory for results')
|
|||
|
|
|||
|
# 算法特定参数
|
|||
|
parser.add_argument('--de_popsize', type=int, default=15, help='DE population size')
|
|||
|
parser.add_argument('--de_maxiter', type=int, default=50, help='DE max iterations')
|
|||
|
parser.add_argument('--ga_popsize', type=int, default=30, help='GA population size')
|
|||
|
parser.add_argument('--ga_generations', type=int, default=50, help='GA generations')
|
|||
|
parser.add_argument('--pso_particles', type=int, default=20, help='PSO particle count')
|
|||
|
parser.add_argument('--pso_iterations', type=int, default=50, help='PSO iterations')
|
|||
|
parser.add_argument('--bayes_calls', type=int, default=100, help='Bayesian optimization calls')
|
|||
|
|
|||
|
args = parser.parse_args()
|
|||
|
|
|||
|
# 生成缓存(如果需要)
|
|||
|
if args.generate_cache:
|
|||
|
generate_base_predictions()
|
|||
|
return
|
|||
|
|
|||
|
# 加载基础预测
|
|||
|
base_predictions = load_base_predictions(args.cache_file)
|
|||
|
if base_predictions is None:
|
|||
|
print("Please run with --generate_cache first to create base predictions")
|
|||
|
return
|
|||
|
|
|||
|
# 定义参数边界
|
|||
|
bounds = {
|
|||
|
'tta_weight_0': (0.0, 2.0), # 原始
|
|||
|
'tta_weight_1': (0.0, 2.0), # 噪声
|
|||
|
'tta_weight_2': (0.0, 2.0), # 缩放
|
|||
|
'tta_weight_3': (0.0, 2.0), # 偏移
|
|||
|
'tta_weight_4': (0.0, 2.0), # 平滑
|
|||
|
'gru_weight': (0.0, 1.0) # GRU权重
|
|||
|
}
|
|||
|
|
|||
|
# 处理算法选择
|
|||
|
if 'all' in args.algorithms:
|
|||
|
algorithms_to_run = ['de', 'ga', 'pso']
|
|||
|
if SKOPT_AVAILABLE:
|
|||
|
algorithms_to_run.append('bayes')
|
|||
|
else:
|
|||
|
algorithms_to_run = args.algorithms
|
|||
|
|
|||
|
# 运行优化算法
|
|||
|
results = []
|
|||
|
|
|||
|
for algo in algorithms_to_run:
|
|||
|
print(f"\n{'='*60}")
|
|||
|
print(f"Running {algo.upper()} optimization...")
|
|||
|
print(f"{'='*60}")
|
|||
|
|
|||
|
try:
|
|||
|
if algo == 'de':
|
|||
|
optimizer = DifferentialEvolution(
|
|||
|
bounds=bounds,
|
|||
|
popsize=args.de_popsize,
|
|||
|
maxiter=args.de_maxiter
|
|||
|
)
|
|||
|
elif algo == 'ga':
|
|||
|
optimizer = GeneticAlgorithm(
|
|||
|
bounds=bounds,
|
|||
|
popsize=args.ga_popsize,
|
|||
|
generations=args.ga_generations
|
|||
|
)
|
|||
|
elif algo == 'pso':
|
|||
|
optimizer = ParticleSwarmOptimization(
|
|||
|
bounds=bounds,
|
|||
|
n_particles=args.pso_particles,
|
|||
|
max_iter=args.pso_iterations
|
|||
|
)
|
|||
|
elif algo == 'bayes':
|
|||
|
if not SKOPT_AVAILABLE:
|
|||
|
print("Skipping Bayesian optimization (scikit-optimize not available)")
|
|||
|
continue
|
|||
|
optimizer = BayesianOptimization(
|
|||
|
bounds=bounds,
|
|||
|
n_calls=args.bayes_calls
|
|||
|
)
|
|||
|
else:
|
|||
|
print(f"Unknown algorithm: {algo}")
|
|||
|
continue
|
|||
|
|
|||
|
result = optimizer.optimize(evaluate_config_gpu, base_predictions)
|
|||
|
results.append(result)
|
|||
|
|
|||
|
print(f"\n{result.algorithm} Results:")
|
|||
|
print(f"Best Score: {result.best_score:.6f}")
|
|||
|
print(f"Best Parameters: {result.best_params}")
|
|||
|
print(f"Total Evaluations: {result.total_evaluations}")
|
|||
|
print(f"Elapsed Time: {result.elapsed_time:.2f}s")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
print(f"Error running {algo}: {e}")
|
|||
|
continue
|
|||
|
|
|||
|
# 保存结果
|
|||
|
if results:
|
|||
|
save_results(results, args.output_dir)
|
|||
|
|
|||
|
print(f"\n{'='*60}")
|
|||
|
print("FINAL COMPARISON")
|
|||
|
print(f"{'='*60}")
|
|||
|
|
|||
|
# 按分数排序
|
|||
|
results.sort(key=lambda x: x.best_score)
|
|||
|
|
|||
|
for i, result in enumerate(results, 1):
|
|||
|
print(f"{i}. {result.algorithm}")
|
|||
|
print(f" Score: {result.best_score:.6f}")
|
|||
|
print(f" Time: {result.elapsed_time:.2f}s")
|
|||
|
print(f" Evaluations: {result.total_evaluations}")
|
|||
|
print(f" Parameters: {result.best_params}")
|
|||
|
print()
|
|||
|
|
|||
|
print(f"Best overall: {results[0].algorithm} with score {results[0].best_score:.6f}")
|
|||
|
|
|||
|
if __name__ == '__main__':
|
|||
|
main()
|