别再死磕梯度下降了!用Python手搓一个遗传算法,5分钟搞定复杂函数优化
·
用Python实现遗传算法:5分钟突破传统优化瓶颈
当面对复杂函数优化问题时,许多开发者会本能地选择梯度下降这类传统方法。然而,当目标函数存在多个局部极值、不可导或参数空间维度较高时,这些方法往往陷入困境。遗传算法作为一种仿生优化技术,正成为解决这类问题的利器。
1. 遗传算法核心原理与优势
遗传算法模拟自然界"物竞天择"的进化机制,通过编码、选择、交叉和变异等操作在解空间中进行全局搜索。与传统优化方法相比,它具有三大独特优势:
- 无需梯度信息 :不依赖目标函数的可导性
- 全局搜索能力 :通过种群多样性避免陷入局部最优
- 并行搜索特性 :同时评估多个潜在解
下表对比了遗传算法与梯度下降的关键差异:
| 特性 | 遗传算法 | 梯度下降 |
|---|---|---|
| 需要导数 | 否 | 是 |
| 搜索方式 | 多点并行 | 单点串行 |
| 适用问题 | 非凸、多峰 | 凸、平滑 |
| 参数敏感 | 中等 | 高 |
# 示例:Rastrigin函数 - 典型的多峰测试函数
import numpy as np
def rastrigin(x):
return 10*len(x) + sum([(xi**2 - 10*np.cos(2*np.pi*xi)) for xi in x])
提示:当目标函数存在大量局部极小值时,传统优化方法极易陷入早熟收敛,而遗传算法能保持更好的搜索广度。
2. Python实现遗传算法框架
让我们从零构建一个完整的遗传算法实现。以下代码框架仅需NumPy库支持,适合各种优化场景。
2.1 种群初始化与编码
实数编码直接使用解向量作为染色体,是最直观的表示方式:
import numpy as np
class GeneticAlgorithm:
def __init__(self, pop_size, dna_size, cross_rate, mutation_rate, max_gen):
self.pop_size = pop_size # 种群规模
self.dna_size = dna_size # 解向量维度
self.cross_rate = cross_rate # 交叉概率
self.mut_rate = mutation_rate # 变异概率
self.max_gen = max_gen # 最大迭代次数
def init_population(self, bounds):
"""初始化种群"""
self.bounds = np.array(bounds)
self.pop = np.random.uniform(
low=self.bounds[:,0],
high=self.bounds[:,1],
size=(self.pop_size, self.dna_size)
)
2.2 适应度评估与选择
采用轮盘赌选择机制,适应度越高被选中的概率越大:
def evaluate(self, func):
"""评估种群适应度"""
self.fitness = 1 / (func(self.pop) + 1e-6) # 防止除零
return self.fitness
def select(self):
"""轮盘赌选择"""
idx = np.random.choice(
np.arange(self.pop_size),
size=self.pop_size,
replace=True,
p=self.fitness/self.fitness.sum()
)
self.pop = self.pop[idx]
2.3 遗传操作实现
交叉和变异是算法核心,直接影响搜索效率:
def crossover(self):
"""算术交叉"""
for parent in self.pop:
if np.random.rand() < self.cross_rate:
i_ = np.random.randint(0, self.pop_size)
cross_points = np.random.rand(self.dna_size) < 0.5
parent[cross_points] = self.pop[i_][cross_points]
def mutate(self):
"""非均匀变异"""
for child in self.pop:
if np.random.rand() < self.mut_rate:
mutate_points = np.random.rand(self.dna_size) < 0.2
noise = np.random.randn(self.dna_size) * 0.1
child[mutate_points] += noise[mutate_points]
# 边界处理
child[:] = np.clip(child, self.bounds[:,0], self.bounds[:,1])
3. 实战:多峰函数优化案例
让我们用实现的算法优化一个经典测试函数:
3.1 问题描述
优化Ackley函数: f(x) = -20*exp(-0.2√(1/n∑x²)) - exp(1/n∑cos(2πx)) + 20 + e
该函数在原点处有全局最小值0,同时包含大量局部极小点,是测试算法全局搜索能力的理想案例。
def ackley(x):
x = np.array(x)
term1 = -20 * np.exp(-0.2 * np.sqrt(np.mean(x**2)))
term2 = -np.exp(np.mean(np.cos(2 * np.pi * x)))
return term1 + term2 + 20 + np.e
3.2 参数配置与优化
合理设置算法参数对性能至关重要:
# 算法参数配置
ga = GeneticAlgorithm(
pop_size=200, # 种群规模
dna_size=10, # 解向量维度
cross_rate=0.8, # 交叉概率
mutation_rate=0.01, # 变异概率
max_gen=100 # 最大迭代次数
)
# 定义搜索空间 [-5,5]^10
bounds = [[-5,5]] * 10
ga.init_population(bounds)
# 进化过程记录
best_fitness = []
for generation in range(ga.max_gen):
fitness = ga.evaluate(ackley)
best_fitness.append(np.min(1/fitness - 1e-6))
ga.select()
ga.crossover()
ga.mutate()
3.3 结果可视化
import matplotlib.pyplot as plt
plt.plot(best_fitness)
plt.xlabel('Generation')
plt.ylabel('Best Fitness')
plt.title('Evolution Process')
plt.show()
经过100代进化,算法能找到非常接近理论最优解的结果,验证了实现的正确性。
4. 高级技巧与性能优化
要让遗传算法在实际问题中发挥最佳效果,还需要掌握以下进阶技巧:
4.1 自适应参数调整
固定参数难以适应不同进化阶段的需求,动态调整策略可显著提升性能:
def adaptive_params(self, gen):
"""自适应调整交叉和变异概率"""
# 早期鼓励探索,后期加强开发
self.cross_rate = 0.9 - 0.6 * (gen/self.max_gen)
self.mut_rate = 0.1 - 0.09 * (gen/self.max_gen)
4.2 精英保留策略
防止优秀个体在进化过程中丢失:
def elitism(self, new_pop, fitness):
"""保留前10%的精英个体"""
elite_size = int(self.pop_size * 0.1)
elite_idx = np.argsort(fitness)[-elite_size:]
new_pop[-elite_size:] = self.pop[elite_idx]
return new_pop
4.3 混合优化策略
结合局部搜索方法提升收敛精度:
from scipy.optimize import minimize
def local_search(self, best_individual):
"""用局部搜索优化最佳个体"""
res = minimize(
ackley,
best_individual,
bounds=self.bounds,
method='L-BFGS-B'
)
return res.x
在实际项目中,遗传算法常与梯度下降等传统方法结合使用——先用遗传算法进行全局探索,再通过局部搜索精细调优。这种混合策略兼具两者的优势,能高效解决复杂优化问题。
所有评论(0)