概述
最近搞进化算法搞得比较多,所以想来总结以下,一来是对7月实训的总结,二来是对今后学习的一个反思。
进化算法的思想大同小异,具体的操作可以分为一下几个步骤:
-
编码可行解
对于进化算法这一类的启发式算法,可行解一般会被编码,比如遗传算法里头常用的0-1编码;在计算适应值的时候会进行解码,比如0-1编码可能被映射成整数。 -
初始化
可行解一般需要经过初始化得到最初的种群,种群的大小和最大迭代次数是此类优化算法的超参数。初始化可以使用随机数来实现,使用[0,1]内的随机数即可,然后将该值映射到可行的取值范围,比如可行解的取值范围为 [ x m i n , x m a x ] [x_{min},x_{max}] [xmin,xmax],生成的随机数为 λ \lambda λ,则可行解的实际值可以通过以下方式计算:
x = λ ∗ ( x m a x − x m i n ) + x m i n x=\lambda *(x_{max}-x_{min})+x_{min} x=λ∗(xmax−xmin)+xmin -
生成新一代
每次迭代都会生成新的解,新解的生成方法不尽相同。遗传算法会选择最好的k个解,通过杂交和变异生成新的解;粒子群算法中粒子会朝着全局最优粒子的位置以及自身以往的最优位置移动从而到达新的位置;差分进化算法会随机选择三个与自身不同的个体从而生成新个体。 -
选择与淘汰
演化算法会淘汰不合格的解,这个不合格的解是相对于当前解而言,这个很好理解,毕竟优胜劣汰嘛,通过淘汰,种群中的个体的适应值越来越好,这样当前解也会更好。为了避免陷入局部最优,遗传和差分进化算法会使用变异操作,来试图跳出局部最优。 -
结束
算法的结束一般有两种形式,一种是静态的,比如设定最大迭代次数,达到最大次数后中止算法,不管当前解是否收敛;一种是动态,判断当前解是否已收敛,比如前k个最优解的差别在一定范围内时结束算法。
比较
三种算法的比较如下
* | GA | PSO | DE |
---|---|---|---|
是否需要对可行解进行排序 | 需要 | 不需要 | 不需要 |
种群规模对运行时间的影响 | 指数级 | 线性 | 线性 |
最优解对种群的影响 | 一般 | 很大 | 很小 |
是否会过早收敛 | 有可能 | 很可能 | 不太可能 |
搜索空间的连续性 | 不太连续,一般是离散的 | 连续 | 连续 |
适用于连续性/离散型问题 | 离散型 | 连续性,有离散版本 | 可以用于离散型和连续型 |
常用算法包
关于启发式算法的算法包有很多,我按照不同类型的编程语言来讲解。
- java
我目前用过的java进化算法包主要是jenetics(遗传算法)和jswarm(粒子群算法),jenetics功能很强大,采用的是java stream,有点复杂,我吉纳丹用了一下,速度还不错,示例代码如下:
import io.jenetics.Optimize;
import io.jenetics.Phenotype;
import io.jenetics.engine.Codec;
import io.jenetics.DoubleGene;
import io.jenetics.engine.Codecs;
import io.jenetics.engine.Engine;
import io.jenetics.engine.EvolutionStatistics;
import io.jenetics.util.DoubleRange;
import static io.jenetics.engine.EvolutionResult.toBestPhenotype;
public class Main {
public static void main(String[] args){
solve();
}
public static double evaluate(double[] positions){
double sum=0;
for(double i:positions){
sum+=i*i;
}
return sum/positions.length;
}
public static void solve(){
final Engine<DoubleGene,Double> engine= Engine.builder(Main::evaluate,
Codecs.ofVector(DoubleRange.of(-100,100),20))
.optimize(Optimize.MINIMUM).build();
final EvolutionStatistics<Double, ?>
statistics = EvolutionStatistics.ofNumber();
final Phenotype<DoubleGene, Double> best = engine.stream()
.limit(3000)
.peek(statistics)
.collect(toBestPhenotype());
Object[] bestPosition = best.getGenotype().getChromosome().toSeq().asList().toArray();
double result=best.getFitness();
for (int i = 0; i < bestPosition.length; i++) {
DoubleGene dg = (DoubleGene) bestPosition[i];
System.out.print(dg.getAllele() + "\t");
}
System.out.println("\n"+result);
}
}
不过遗传算法不太适合连续型问题。
jswarm与jenetics相比要简单很多,使用jswarm需要定义Particle,指定Particle的维度,然后实现FitnessFunction,具体代码如下:
这是粒子的定义
public class PSOParticle extends Particle {
public static int dimension=10;
public PSOParticle(){
super(dimension);
}
}
这是FitnessFuntion的定义
import net.sourceforge.jswarm_pso.FitnessFunction;
import net.sourceforge.jswarm_pso.Particle;
import net.sourceforge.jswarm_pso.Swarm;
import static io.jenetics.engine.EvolutionResult.toBestPhenotype;
public class Main extends FitnessFunction {
public static void main(String[] args){
Swarm s=new Swarm(100,new PSOParticle(),new Main());
s.setMaxPosition(100);
s.setMinPosition(-100);
for(int i=0;i<2000;i++){
s.evolve();
}
for(double i:s.getBestPosition())
System.out.print(i+"\t");
System.out.println("\n"+s.getBestFitness());
}
public double evaluate(double[] positions){
double sum=0;
for(double i:positions){
sum+=i*i;
}
return -sum;
}
}
jswarm包可以从jswarm下载。
- matlab
matlab的演化算法包很多,可以从matlab的附加功能里下载。我用的是matlab自带的global optimization toolbox,里面包含了常见的优化算法。
目标函数如下:function [out]= fitness(in) out=sum(in.*in) end
particleswarm对应的是粒子群算法,ga对应的是遗传算法,算法的中止条件是动态的,当解趋近于收敛的时候会中止循环,matlab求解的速度是很快的,不过,粒子群算法收敛得比遗传要快,因为粒子群更适合连续性的优化问题。%[~,fval]=particleswarm(@fitness,100,ones(20,1)*(-100),ones(20,1)*100); [~,res]=ga(@fitness,100,[],[],[],[],ones(20,1)*(-100),ones(20,1)*100); %disp(fval); disp(res);
- python
python的话我推荐geatpy,它的资料比较齐全,功能也很强大。
可以到geatpy官网查看相关资料。