用Python 3.11实战遗传算法:从零构建到函数优化

在算法学习的道路上,遗传算法(Genetic Algorithm, GA)常常因为涉及生物进化理论而让初学者望而生畏。但今天,我们将用Python 3.11和NumPy库,通过不到150行代码实现一个完整的遗传算法框架,并应用于经典的Rastrigin函数优化问题。这种方法完全跳过了复杂的数学推导,让你在代码编写和运行结果中直观理解种群进化、自然选择的精髓。

1. 环境准备与问题定义

首先确保你的Python环境已安装3.11版本,这是目前执行效率最高的Python版本之一。我们将使用以下库:

pip install numpy matplotlib

Rastrigin函数是优化算法常用的测试函数,其特点是具有大量局部极小值点,全局最小值在原点(0,0)。在二维情况下,函数定义为:

import numpy as np

def rastrigin(x):
    """二维Rastrigin函数"""
    A = 10
    return A*2 + (x[0]**2 - A*np.cos(2*np.pi*x[0])) + (x[1]**2 - A*np.cos(2*np.pi*x[1]))

为什么选择这个函数? 因为它的多模态特性能够很好地测试算法是否陷入局部最优。我们的目标是找到使函数值最小的(x,y)组合。

2. 遗传算法核心组件实现

2.1 种群初始化与编码设计

遗传算法的第一步是创建初始种群。我们采用实数编码而非二进制编码,这更适合连续函数优化问题:

def initialize_population(pop_size, x_bound, y_bound):
    """初始化种群"""
    population = np.zeros((pop_size, 2))
    population[:, 0] = np.random.uniform(x_bound[0], x_bound[1], size=pop_size)
    population[:, 1] = np.random.uniform(y_bound[0], y_bound[1], size=pop_size)
    return population

参数说明:

  • pop_size : 种群规模,通常20-100
  • x_bound , y_bound : 变量取值范围,如([-5.12, 5.12], [-5.12, 5.12])

2.2 适应度评估与选择机制

适应度函数决定了个体被选择的概率。对于最小化问题,我们采用以下转换:

def evaluate_fitness(population):
    """计算种群中每个个体的适应度"""
    fitness = 1 / (1 + rastrigin(population.T))
    return fitness / np.sum(fitness)  # 归一化

赌轮盘选择实现如下:

def selection(population, fitness, elite_size=2):
    """赌轮盘选择"""
    elite_indices = np.argsort(fitness)[-elite_size:]
    selected = population[elite_indices]
    
    # 轮盘赌选择剩余个体
    indices = np.random.choice(len(population), size=len(population)-elite_size, 
                              p=fitness, replace=True)
    selected = np.vstack([selected, population[indices]])
    return selected

注意:保留精英(elite_size)可以确保最优个体不被随机选择淘汰

2.3 交叉与变异操作

单点交叉和均匀变异是实现种群多样性的关键:

def crossover(parents, offspring_size):
    """单点交叉"""
    offspring = np.zeros(offspring_size)
    crossover_point = np.random.randint(1, offspring_size[1])
    
    for i in range(offspring_size[0]):
        parent1_idx = i % len(parents)
        parent2_idx = (i+1) % len(parents)
        offspring[i, :crossover_point] = parents[parent1_idx, :crossover_point]
        offspring[i, crossover_point:] = parents[parent2_idx, crossover_point:]
    return offspring

def mutation(offspring, mutation_rate=0.1, mutation_scale=0.5):
    """均匀变异"""
    for i in range(len(offspring)):
        if np.random.random() < mutation_rate:
            offspring[i] += np.random.normal(0, mutation_scale, size=2)
    return offspring

参数对比实验表明:

参数 推荐范围 影响效果
mutation_rate 0.05-0.2 过高导致随机游走,过低丧失多样性
mutation_scale 0.1-1.0 控制变异幅度,应与搜索空间匹配

3. 完整算法流程与可视化

将上述组件整合成完整遗传算法:

def genetic_algorithm(pop_size=50, generations=100, elite_size=2, 
                     mutation_rate=0.1, bounds=([-5.12,5.12],[-5.12,5.12])):
    # 初始化
    population = initialize_population(pop_size, *bounds)
    best_fitness = []
    
    for gen in range(generations):
        # 评估
        fitness = evaluate_fitness(population)
        best_idx = np.argmax(fitness)
        best_fitness.append(rastrigin(population[best_idx]))
        
        # 选择
        selected = selection(population, fitness, elite_size)
        
        # 交叉变异
        offspring = crossover(selected, (pop_size-elite_size, 2))
        offspring = mutation(offspring, mutation_rate)
        
        # 新一代种群
        population[:elite_size] = selected[:elite_size]
        population[elite_size:] = offspring
    
    return population, best_fitness

可视化收敛过程:

import matplotlib.pyplot as plt

def plot_results(best_fitness):
    plt.figure(figsize=(10,6))
    plt.plot(best_fitness, 'b', linewidth=2)
    plt.title('Genetic Algorithm Convergence')
    plt.xlabel('Generation')
    plt.ylabel('Best Fitness (Rastrigin Value)')
    plt.grid(True)
    plt.show()

4. 参数调优与实战技巧

通过多次实验,我们发现以下经验法则:

  1. 种群规模

    • 太小(<20):多样性不足,易早熟收敛
    • 太大(>100):计算开销大,收敛慢
    • 推荐:30-50
  2. 变异策略优化

    • 自适应变异率:随着代数增加逐渐降低
    def adaptive_mutation_rate(gen, max_gen, base_rate=0.2):
        return base_rate * (1 - gen/max_gen)
    
  3. 约束处理

    • 当变量超出边界时,可采用反射或随机重置
    def apply_bounds(population, bounds):
        for i in range(population.shape[1]):
            population[:,i] = np.clip(population[:,i], bounds[i][0], bounds[i][1])
        return population
    
  4. 并行评估

    • 利用多核加速适应度计算
    from multiprocessing import Pool
    
    def parallel_evaluate(population):
        with Pool() as p:
            results = p.map(rastrigin, population)
        return 1 / (1 + np.array(results))
    

5. 进阶扩展与工程实践

在实际项目中应用遗传算法时,还需要考虑:

  • 混合策略 :将GA与局部搜索(如梯度下降)结合

    def hybrid_optimize(individual, lr=0.01, steps=50):
        """用梯度下降微调GA结果"""
        x = individual.copy()
        for _ in range(steps):
            grad = numerical_gradient(rastrigin, x)
            x -= lr * grad
        return x
    
  • 多目标优化 :使用NSGA-II等算法处理多个目标函数

  • 早停机制 :当连续多代没有改进时终止迭代

    if len(best_fitness) > 20 and (np.min(best_fitness[-20:]) - np.min(best_fitness[-10:])) < 1e-6:
        break
    
  • 日志记录 :保存每代种群信息供后续分析

    import pandas as pd
    
    def save_generation_log(population, gen, filename):
        df = pd.DataFrame(population, columns=['x', 'y'])
        df['generation'] = gen
        df['fitness'] = evaluate_fitness(population)
        df.to_csv(filename, mode='a', header=not os.path.exists(filename))
    

在电商推荐系统优化项目中,我们曾用类似方法调整排序算法参数,相比网格搜索效率提升近10倍。关键是将业务指标(如转化率)巧妙设计为适应度函数,并通过限制参数范围确保解的可行性。

更多推荐