目录
- 摘要
- 1、算法原理
- 1.1、种群初始化
- 1.2、变异
- 1.3、交叉
- 1.4、选择
- 2、算法实现
- 2.1、种群初始化
- 2.2、变异
- 2.3、交叉
- 2.4、选择
- 2.5、选取终代种群中最优秀个体
摘要
如何选取一组最佳的参数,使得代价函数值最优?这是优化算法做的事,一个直觉的方法是使用网格搜索,遍历参数值在值域上的所有组合,选取代价函数值最优的一组参数值,该方法搜索时无方向驱使,需遍历整个参数空间,复杂度太高。梯度下降算法通过代价函数对参数的导数有方向的更新参数值,无需搜索整个向量空间,速度很快,但无法解决不可导问题。而差分进化算法是一种基于“适者生存”思想的最优参数搜索算法。通过变异、交叉、选择循环维护 N P NP NP个最优秀个体的种群实现“适者生存”,达到最优参数搜索的目的,无需代价函数对参数可导。注意种群中的每个个体表示参数空间的一组向量值,个体的一个基因表示一个参数。本文参考97年原始论文,根据差分进化算法原理复现代码。
1、算法原理
差分进化算法核心是种群的演化,第 G G G代种群中的第 i i i个个体可用数学符号表示为 x i , G , i = 1 , 2 , … , N P x_{i, G},i=1,2, \ldots, NP xi,G,i=1,2,…,NP。流程如下图,初始化时在值域空间随机选取 N P 个个体 NP个个体 NP个个体作为初始种群,通过变异交叉得到种群中每个个体对应的实验个体, 比较种群的现有个体与实验个体的代价值,选择更优的作为下一代种群中的个体,循环往复,直至满足结束条件。
1.1、种群初始化
根据参数的取值范围,随机生成 N P NP NP个个体,作为初始种群。
1.2、变异
对于种群中的每一个个体,随机抽取的2个个体加权差与之加和,得到变异个体 v i , G + 1 v_{i, G+1} vi,G+1,数学表示为:
v i , G + 1 = x r 1 , G + F ⋅ ( x r 2 , G − x r 3 , G ) (1) v_{i, G+1}=x_{r_{1}, G}+F \cdot\left(x_{r_{2}, G}-x_{r_{3}, G}\right)\tag1 vi,G+1=xr1,G+F⋅(xr2,G−xr3,G)(1)
r 1 , r 2 , r 3 ∈ [ 1 , N P ] r_1,r_2,r_3\in[1,NP] r1,r2,r3∈[1,NP],是种群中的个体索引, F ∈ [ 0 , 2 ] F\in[0,2] F∈[0,2],是常数因子,用与控制差分的缩放。
1.3、交叉
变异个体与目标个体随机交换基因,得到下一代的实验个体。数学表示为:
u j i , G + 1 = { v j i , G + 1 if ( randb ( j ) ≤ C R ) or j = rnbr ( i ) x j i , G if ( randb ( j ) > C R ) and j ≠ rnbr ( i ) j = 1 , 2 , … , D . (2) \begin{aligned} u_{j i, G+1} & =\left\{\begin{array}{ll} v_{j i, G+1} & \text { if }(\operatorname{randb}(j) \leq C R) \text { or } j=\operatorname{rnbr}(i) \\ x_{j i, G} & \text { if }(\operatorname{randb}(j)>C R) \text { and } j \neq \operatorname{rnbr}(i) \end{array}\right. \\ j & =1,2, \ldots, D . \end{aligned}\tag2 uji,G+1j={vji,G+1xji,G if (randb(j)≤CR) or j=rnbr(i) if (randb(j)>CR) and j=rnbr(i)=1,2,…,D.(2)
其中 j j j是个体基因的索引, u j i , G + 1 u_{j i, G+1} uji,G+1表示第 G + 1 G+1 G+1代种群第 i i i个个体的第 j j j个基因, r a n d b ( j ) randb(j) randb(j)是一个随机数生成器,生成值范围为 [ 0 , 1 ] [0,1] [0,1],CR是 [ 0 , 1 ] [0,1] [0,1]的交叉常数,当 r a n d b ( j ) > C R randb(j)>CR randb(j)>CR时交换目标个体和变异个体第 j j j个位置的基因,从而得到实验个体, r n b r ( i ) {rnbr}(i) rnbr(i)用于确保个体中至少一个基因发生交叉。
1.4、选择
通过比较种群中的个体与对应实验个体的代价函数,选择更优的作为下一轮种群中的个体,并判断是否达到搜索的终止条件,达到终止条件则从当前种群中选取代价函数值最优的个体作为结果。
2、算法实现
以搜索让代价函数 y = x 1 2 + ∣ x 2 ∣ + x 3 3 y=x_1^2+|x_2|+x_3^3 y=x12+∣x2∣+x33值最小的一组参数为例,其中 x 1 , x 2 , x 3 ∈ [ − 30 , 30 ] x_1,x_2,x_3\in[-30, 30] x1,x2,x3∈[−30,30]。全部如下代码:
import math
import numpy as npdef cost_func(x:np.array)->float:"""代价函数:param x: 参数列表:return: 参数值对应的代价值"""return math.pow(x[0], 2) + np.abs(x[1]) + math.pow(x[2], 3)def population_init(x_bounds:list, n=50)->np.array:"""种群初始化:param x_bounds: 各参数上下边界:param n: 初始种群大小:return: 初始种群"""population = []for _ in range(n):vector = []for low, high in x_bounds:vector.append(np.random.uniform(low, high))population.append(vector)return np.array(population)def mutation(population:np.array, x_bounds:list, F=1.0):"""变异操作:param population: 种群:param x_bounds: 参数边界范围:param F: 变异的常数因子:return: 变异种群"""n = len(population)mutation_vectors = []for r1 in range(n):r2 = np.random.randint(1, n)r3 = np.random.randint(1, n)v = population[r1] + F*(population[r2]-population[r3])v = edge_deal(v, x_bounds)mutation_vectors.append(v)return np.array(mutation_vectors)def edge_deal(x:np.array, x_bounds:list)->np.array:"""边界处理:param x: 种群中的一个个体:param x_bounds: 参数边界范围:return: 满足边界条件的个体"""D = len(x)for j in range(D):low, high = x_bounds[j][0], x_bounds[j][1]if x[j] < low:x[j] = lowif x[j] > high:x[j] = highreturn xdef crossover(population:np.array, mutation_vectors:np.array, CR=0.5):"""交叉操作:param population: 种群:param mutation_vectors: 变异种群:param CR: 交叉的常数因子:return: 实验种群(种群和变异种群基因交叉后得到的)"""D = len(population[0])for i in range(len(population)):rnbr = np.random.randint(1, D)for j in range(D):if np.random.rand() > CR or j == rnbr:mutation_vectors[i][j] = population[i][j]return mutation_vectorsdef selector(population:np.array, trial_population:np.array, cost_func):"""选择操作:param population: 种群:param trial_population: 实验种群:param cost_func: 代价函数:return: 更优秀的下一代种群"""next_population = []for i in range(len(population)):if cost_func(population[i]) < cost_func(trial_population[i]):next_population.append(population[i])else:next_population.append(trial_population[i])return next_populationdef min_cost_get(population:np.array):"""获取种群中代价最小的个体:param population: 种群:return: 最优秀的个体和其对应的代价值"""min_cost = cost_func(population[0])min_cost_index = 0for i in range(1, len(population)):if cost_func(population[i]) < min_cost:min_cost = cost_func(population[i])min_cost_index = ireturn population[min_cost_index], min_costif __name__ == '__main__':max_items = 200x_bounds = [(-30, 30)] * 3population = population_init(x_bounds, 50)for i in range(max_items):mutation_vectors = mutation(population, x_bounds)trial_population = crossover(population, mutation_vectors)population = selector(population, trial_population, cost_func)print(min_cost_get(population))
2.1、种群初始化
def population_init(x_bounds:list, n=50)->np.array:"""种群初始化:param x_bounds: 各参数上下边界:param n: 初始种群大小:return: 初始种群"""population = []for _ in range(n):vector = []for low, high in x_bounds:vector.append(np.random.uniform(low, high))population.append(vector)return np.array(population)if __name__ == '__main__':max_items = 200x_bounds = [(-30, 30)] * 3population = population_init(x_bounds, 50)
2.2、变异
def mutation(population:np.array, x_bounds:list, F=1.0):"""变异操作:param population: 种群:param x_bounds: 参数边界范围:param F: 变异的常数因子:return: 变异种群"""n = len(population)mutation_vectors = []for r1 in range(n):r2 = np.random.randint(1, n)r3 = np.random.randint(1, n)v = population[r1] + F*(population[r2]-population[r3])v = edge_deal(v, x_bounds)mutation_vectors.append(v)return np.array(mutation_vectors)def edge_deal(x:np.array, x_bounds:list)->np.array:"""边界处理:param x: 种群中的一个个体:param x_bounds: 参数边界范围:return: 满足边界条件的个体"""D = len(x)for j in range(D):low, high = x_bounds[j][0], x_bounds[j][1]if x[j] < low:x[j] = lowif x[j] > high:x[j] = highreturn x
2.3、交叉
def crossover(population:np.array, mutation_vectors:np.array, CR=0.5):"""交叉操作:param population: 种群:param mutation_vectors: 变异种群:param CR: 交叉的常数因子:return: 实验种群(种群和变异种群基因交叉后得到的)"""D = len(population[0])for i in range(len(population)):rnbr = np.random.randint(1, D)for j in range(D):if np.random.rand() > CR or j == rnbr:mutation_vectors[i][j] = population[i][j]return mutation_vectors
2.4、选择
def selector(population:np.array, trial_population:np.array, cost_func):"""选择操作:param population: 种群:param trial_population: 实验种群:param cost_func: 代价函数:return: 更优秀的下一代种群"""next_population = []for i in range(len(population)):if cost_func(population[i]) < cost_func(trial_population[i]):next_population.append(population[i])else:next_population.append(trial_population[i])return next_population
2.5、选取终代种群中最优秀个体
def min_cost_get(population:np.array):"""获取种群中代价最小的个体:param population: 种群:return: 代价最小的个体和其对应的代价值"""min_cost = cost_func(population[0])min_cost_index = 0for i in range(1, len(population)):if cost_func(population[i]) < min_cost:min_cost = cost_func(population[i])min_cost_index = ireturn population[min_cost_index], min_cost