文档章节

jMetal使用教程(一)

mseeds
 mseeds
发布于 2017/06/12 00:09
字数 2094
阅读 303
收藏 0

最近发现jMetal框架更新到了5.2,我也把以前写代码顺着框架重新改动一遍,正好整理出来供大家参考。

jMetal是Java实现的一套多目标优化框架,只需要很少量的改动就能定制自己的算法。关键是开源包里包括了几乎所有常用的算法,大大方便了懒人群体。

关于项目的其他信息,请移步:https://jmetal.github.io/jMetal/,github:https://github.com/jMetal/jMetal

下载使用

jMetal的5.0版本以后终于maven化了

maven依赖:

<!-- https://mvnrepository.com/artifact/org.uma.jmetal/jmetal-core -->
<dependency>
    <groupId>org.uma.jmetal</groupId>
    <artifactId>jmetal-core</artifactId>
    <version>5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.uma.jmetal/jmetal-algorithm -->
<dependency>
    <groupId>org.uma.jmetal</groupId>
    <artifactId>jmetal-algorithm</artifactId>
    <version>5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.uma.jmetal/jmetal-problem -->
<dependency>
    <groupId>org.uma.jmetal</groupId>
    <artifactId>jmetal-problem</artifactId>
    <version>5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.uma.jmetal/jmetal-exec -->
<dependency>
    <groupId>org.uma.jmetal</groupId>
    <artifactId>jmetal-exec</artifactId>
    <version>5.2</version>
</dependency>

如果不使用maven,可以去官网下载对应的jar包添加到工程里面。

jmetal-core是核心包,jmetal-exec是算法配置包。jmetal-algorithm是各类算法的具体实现,而jmetal-problem是各类优化问题包。其中,jmetal-core是必须的,其余三个包视情况而定,建议都添加。

下面先对jMetal的基本框架和主要的类做一个介绍,下一节再贴出一个完整的Demo。

算法框架

UML class diagram of jMetal 5.0 core classes

jmetal-core框架

The Algorithm :

Algorithm是所有优化算法的模板,只定义了两个接口方法run()和getResult()。run()是算法的运行入口,getResult()用于返回结果集,一般就是算法得到的Pareto集。代码如下:

package org.uma.jmetal.algorithm;

/**
 * Interface representing an algorithm
 * @author Antonio J. Nebro
 * @version 0.1
 * @param <Result> Result
 */
public interface Algorithm<Result> extends Runnable {
  void run() ;
  Result getResult() ;
}

你的自己的算法需要继承的是 AbstractGeneticAlgorithm这个类。作者的意图是写一个多目标优化的通用框架,遗传算法只是其中之一,而AbstractGeneticAlgorithm就是遗传算法的算法模板。

这个AbstractEvolutionaryAlgorithm的代码如下:

public abstract class AbstractGeneticAlgorithm<S, Result> extends AbstractEvolutionaryAlgorithm<S, Result> {
  protected int maxPopulationSize ;
  protected SelectionOperator<List<S>, S> selectionOperator ;
  protected CrossoverOperator<S> crossoverOperator ;
  protected MutationOperator<S> mutationOperator ;
...
  /**
   * Constructor
   * @param problem The problem to solve
   */
  public AbstractGeneticAlgorithm(Problem<S> problem) {
    setProblem(problem);
  }

  /**
   * This method implements a default scheme create the initial population of genetic algorithm
   * @return
   */
  protected List<S> createInitialPopulation() {
    List<S> population = new ArrayList<>(getMaxPopulationSize());
    for (int i = 0; i < getMaxPopulationSize(); i++) {
      S newIndividual = getProblem().createSolution();
      population.add(newIndividual);
    }
    return population;
  }

  /**
   * This method iteratively applies a {@link SelectionOperator} to the population to fill the mating pool population.
   *
   * @param population
   * @return The mating pool population
   */
  @Override
  protected List<S> selection(List<S> population) {
    List<S> matingPopulation = new ArrayList<>(population.size());
    for (int i = 0; i < getMaxPopulationSize(); i++) {
      S solution = selectionOperator.execute(population);
      matingPopulation.add(solution);
    }

    return matingPopulation;
  }
   /**
   * This methods iteratively applies a {@link CrossoverOperator} a  {@link MutationOperator} to the population to
   * create the offspring population. The population size must be divisible by the number of parents required
   * by the {@link CrossoverOperator}; this way, the needed parents are taken sequentially from the population.
   *
   * No limits are imposed to the number of solutions returned by the {@link CrossoverOperator}.
   *
   * @param population
   * @return The new created offspring population
   */
  @Override
  protected List<S> reproduction(List<S> population) {
    int numberOfParents = crossoverOperator.getNumberOfParents() ;

    checkNumberOfParents(population, numberOfParents);

    List<S> offspringPopulation = new ArrayList<>(getMaxPopulationSize());
    for (int i = 0; i < getMaxPopulationSize(); i += numberOfParents) {
      List<S> parents = new ArrayList<>(numberOfParents);
      for (int j = 0; j < numberOfParents; j++) {
        parents.add(population.get(i+j));
      }

      List<S> offspring = crossoverOperator.execute(parents);

      for(S s: offspring){
        mutationOperator.execute(s);
        offspringPopulation.add(s);
      }
    }
    return offspringPopulation;
  }
...
}

run()函数内部就是整个遗传算法的流程了,一目了然。各个具体的遗传算法只需要继承这个类,对很少的函数进行override就得到了一个完整的算法。

比如官方NSGA-II:

public class NSGAII<S extends Solution<?>> extends AbstractGeneticAlgorithm<S, List<S>> {
  ...
/**
   * Constructor
   */
  public NSGAII(Problem<S> problem, int maxEvaluations, int populationSize,
      CrossoverOperator<S> crossoverOperator, MutationOperator<S> mutationOperator,
      SelectionOperator<List<S>, S> selectionOperator, SolutionListEvaluator<S> evaluator) {
    super(problem);
    this.maxEvaluations = maxEvaluations;
    setMaxPopulationSize(populationSize); ;

    this.crossoverOperator = crossoverOperator;
    this.mutationOperator = mutationOperator;
    this.selectionOperator = selectionOperator;

    this.evaluator = evaluator;
  }
  ...
 @Override protected List<S> replacement(List<S> population, List<S> offspringPopulation) {
    List<S> jointPopulation = new ArrayList<>();
    jointPopulation.addAll(population);
    jointPopulation.addAll(offspringPopulation);

    RankingAndCrowdingSelection<S> rankingAndCrowdingSelection ;
    rankingAndCrowdingSelection = new RankingAndCrowdingSelection<S>(getMaxPopulationSize()) ;

    return rankingAndCrowdingSelection.execute(jointPopulation) ;
  }
  ...
}

在构造函数里,你需要提供算法的其他组件(不用担心,这些组件,包里都有现成的):

  1. Problem:要解决的问题类
  2. CrossoverOperator:交叉算子
  3. MutationOperator:变异算子
  4. SelectionOperator:matepool的选择算子
  5. Evaluator:优化函数评估算子
  6. populationSIze:种群大小
  7. maxEvations:最大评估次数(作为停止条件)

AbstractGeneticAlgorithm会在对应的函数中调用这些组件,比如reproduction()会调用crossoverOperator、CrossoverOperator和MutationOperator三个组件,如果你需要自己定义选择、交叉、变异这些操作的话,可以实现对应的接口,然后将这些类作为组件提供给具体的Algorithm。

The Solution:

注意构造函数中的泛型参数S extends Soluiton。Solution是个体定义的接口,每个Solution就是种群中一个个体,Solution需要做的是定义染色体编码方案,记录优化函数值,约束目标值,其他信息等,默认提供了BinarySoluiton、DoubleSolution、IntegerSoluiton等。同样的,如果需要定义新的Solution,你需要继承的是AbstractGenericSolution这个类。

public abstract class AbstractGenericSolution<T, P extends Problem<?>> implements Solution<T> {
  private double[] objectives;
  private List<T> variables;
  protected P problem ;
  protected double overallConstraintViolationDegree ;
  protected int numberOfViolatedConstraints ;
  protected Map<Object, Object> attributes ;
  protected final JMetalRandom randomGenerator ;
/**
   * Constructor
   */
  protected AbstractGenericSolution(P problem) {
    this.problem = problem ;
    attributes = new HashMap<>() ;
    randomGenerator = JMetalRandom.getInstance() ;

    objectives = new double[problem.getNumberOfObjectives()] ;
    variables = new ArrayList<>(problem.getNumberOfVariables()) ;
    for (int i = 0; i < problem.getNumberOfVariables(); i++) {
      variables.add(i, null) ;
    }
  }
  ...
}

The Problem:

构造函数中稍微难理解一点的就是这个Problem<S>,problem定义了需要解决的问题,比如有几个决策变量,几个优化函数。其中每个个体的优化函数的计算过程就是由Problem.evluate()来实现的,这也是Problem类最重要的部分。举个例子:ZDT1,ZDT是一个经典的多目标优化测试函数集,定义如下:

(两个优化函数,30个决策变量)

 jMetal中ZDT1的代码如下:

public class ZDT1 extends AbstractDoubleProblem {

  /** Constructor. Creates default instance of problem ZDT1 (30 decision variables) */
  public ZDT1() {
    this(30);
  }

  /**
   * Creates a new instance of problem ZDT1.
   *
   * @param numberOfVariables Number of variables.
   */
  public ZDT1(Integer numberOfVariables) {
    setNumberOfVariables(numberOfVariables);
    setNumberOfObjectives(2);
    setName("ZDT1");

    List<Double> lowerLimit = new ArrayList<>(getNumberOfVariables()) ;
    List<Double> upperLimit = new ArrayList<>(getNumberOfVariables()) ;

    for (int i = 0; i < getNumberOfVariables(); i++) {
      lowerLimit.add(0.0);
      upperLimit.add(1.0);
    }

    setLowerLimit(lowerLimit);
    setUpperLimit(upperLimit);
  }

  /** Evaluate() method */
  public void evaluate(DoubleSolution solution) {
    double[] f = new double[getNumberOfObjectives()];

    f[0] = solution.getVariableValue(0);
    double g = this.evalG(solution);
    double h = this.evalH(f[0], g);
    f[1] = h * g;

    solution.setObjective(0, f[0]);
    solution.setObjective(1, f[1]);
  }

  /**
   * Returns the value of the ZDT1 function G.
   *
   * @param solution Solution
   */
  private double evalG(DoubleSolution solution) {
    double g = 0.0;
    for (int i = 1; i < solution.getNumberOfVariables(); i++) {
      g += solution.getVariableValue(i);
    }
    double constant = 9.0 / (solution.getNumberOfVariables() - 1);
    g = constant * g;
    g = g + 1.0;
    return g;
  }

  /**
   * Returns the value of the ZDT1 function H.
   *
   * @param f First argument of the function H.
   * @param g Second argument of the function H.
   */
  public double evalH(double f, double g) {
    double h ;
    h = 1.0 - Math.sqrt(f / g);
    return h;
  }
}

代码很简单。其中AbstractDoubleProblem 是通用的DoubleProblem模板,如果你的问题是Double类型的实现这类就可以了。evaluate()由AbstractEvolutionaryAlgorithm.evaluatePopulation()调用,在你的算法中,如果需要自己定义优化函数,就可以在evaluate()中进行定义。

The Operator:

交叉、变异和选择算子都是Operator接口的实现,这里用SimpleRandomMutation这个类来说明,SimpleRandomMutation实现变量在定义域内变异。

public class SimpleRandomMutation implements MutationOperator<DoubleSolution> {
  private double mutationProbability ;
  private RandomGenerator<Double> randomGenerator ;
  
  ...
/** Execute() method */
	@Override
  public DoubleSolution execute(DoubleSolution solution) throws JMetalException {
    if (null == solution) {
      throw new JMetalException("Null parameter") ;
    }

    doMutation(mutationProbability, solution) ;
    
    return solution;
  }

  /** Implements the mutation operation */
	private void doMutation(double probability, DoubleSolution solution) {
    for (int i = 0; i < solution.getNumberOfVariables(); i++) {
      if (randomGenerator.getRandomValue() <= probability) {
      	Double value = solution.getLowerBound(i) +
      			((solution.getUpperBound(i) - solution.getLowerBound(i)) * randomGenerator.getRandomValue()) ;
      	
      	solution.setVariableValue(i, value) ;
      }
    }
	}
}

逻辑非常简单,不过注意在DoubleSolution里面设定好决策变量的定义域大小,如果每个决策变量的定义域不一样的话可以用ArrayDoubleSolution(extends DoubleSolution)这个类。

交叉选择算子和变异算子很类似,可以自己看包里的源码,这里就不做说明了。

结果集

算法运行完后,是通过调用Algorithem.getResult()来得到结果集的,对大部分算法来说也就是Pareto集。NSGA-II中的实现代码如下:

 @Override public List<S> getResult() {
    return getNonDominatedSolutions(getPopulation());
  }
 protected List<S> getNonDominatedSolutions(List<S> solutionList) {
    return SolutionListUtils.getNondominatedSolutions(solutionList);
  }

SolutionListUtils类提供了对种群的操作,如对种群按支配关系排序,得到最好最差解等。和4.0版本采用SolutionSet不同的是5.0版本以后采用的是List<Solution>的方式来定义种群。

以上就是整个框架的大致组成了,总结一下:

  1. 实现AbstractGenericSolution,在具体类里面定义编码方案,定义决策变量,定义优化目标函数储存方法;
  2. 实现AbstractDoubleProblem,在具体类中定义需要解决的问题,overide evaluate()来定义优化函数;
  3. 实现各个Operator:选择、交叉、变异;
  4. 实现AbstractEvolutionaryAlgorithm,Operator提供给具体的Algorithm类,并设定参数;
  5. 调用run()跑算法,调用getResult()得到结果集。

上述步骤的具体实现在包里几乎都有,所以应用起来会非常简单。

下一节给出NSGA-II的一个具体例子,免得还是云里雾里的。

© 著作权归作者所有

共有 人打赏支持
mseeds
粉丝 2
博文 6
码字总数 5122
作品 0
南京
私信 提问
2014年DevExpress使用教程合集

DevExpress系列教程 DevExpress Universal Subscription是DevExpress旗下重要的用户界面控件,也是全球使用最多的.NET用户界面控件套包。自2014年以来,慧都小编为大家奉献了很多使用教程。如...

咲晚杍
2014/12/31
0
1
“我爱智能”原创性博客索引

不知不觉,博客也写出了一点小体系,新的阶段已经开始,未来希望再接再厉继续补充这一体系,在成长中写博客,在博客中成长,在此先做一个小的梳理,谢谢大家的支持。 一)关于深度学习系列 ...

on2way
2015/08/29
0
0
Flash图表组件FusionCharts使用教程大全

Flash图表组件FusionCharts使用教程一:调用静态方法RenderChart Flash图表组件FusionCharts使用教程二:为饼图/环形图添加图例 Flash图表组件FusionCharts使用教程三:在iPhone/iPad中生成J...

lanmeimei
2013/01/09
2.1K
5
Linq to Oracle 使用教程目录

Linq to Oracle 使用教程(一)准备工作 Linq to Oracle 使用教程(二)创建实体类 Linq to Oracle 使用教程(三)数据的增、删、改 Linq to Oracle 使用教程(四)验证数据 Linq to Oracle...

长平狐
2013/06/17
104
0
运维之我的saltstack短篇教程

下面是之前写过的一些有关saltstack的文章 SaltStack使用教程(一):安装并简单配置使用 SaltStack使用教程(二):文件和目录管理 SaltStack使用教程(三):定时任务管理cron.present Sa...

qq850900633
2017/01/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

撬动世界的支点——《引爆点》读书笔记2900字优秀范文

撬动世界的支点——《引爆点》读书笔记2900字优秀范文: 作者:挽弓如月。因为加入火种协会的读书活动,最近我连续阅读了两本论述流行的大作,格拉德威尔的《引爆点》和乔纳伯杰的《疯传》。...

原创小博客
16分钟前
0
0
《配电网自动化技术》第一章

写了配电网的组成、历程、难点、存在问题、解决方案,还是蛮好的。尤其是各地建设的系统后续又无法实用化,以及多种终端反而增加了运维工作量等,都是目前切实存在的让大家不停吐槽的内容。

max佩恩
21分钟前
0
0

中国龙-扬科
39分钟前
2
0
使用vuex的state状态对象的5种方式

vuex是一个专门为vue.js设计的状态管理模式,并且也可以使用devtools进行调试。 下面给大家来贴一下我的vuex的结构 下面是store文件夹下的state.js和index.js内容 //state.jsconst state =...

peakedness丶
43分钟前
2
0
NetCore MVC Demo

地址:http://114.116.9.72:5411

whltian
50分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部