多目的最適化ライブラリjMetalを用いた最適化の実行方法

jMetalはマラガ⼤学(スペイン)のA. J. Nebro教授が開発したJavaベースの多⽬的最適化フレームワークです.普段私はこのjMetalをフォークして研究に使っています.ここではjMetalを使った多目的最適化の計算を実行する方法を紹介します.

0. 開発環境

  • Windows 10
  • OpenJDK 13.0.2
  • IntelliJ IDEA 2019.2.3 (Community Edition)
  • git version 2.22.0
  • jMetal 6.0

1. 開発環境のセットアップ

JDK

  • 以下からOpenJDKをダウンロードします.
    JDK Builds from Oracle
  • 任意のフォルダ(例:C:\Program Files\Java\)に解凍したJDKフォルダ(jdk-X.X.X)を設置します.
  • jdk-X.X.X\binをパスに追加しておきます.

IntelliJ IDEA

Git

  • jMetalライブラリをGitHubからCloneして使用する場合、Git for Windowsのインストールが必要です.以下からインストーラをダウンロードしてインストールします.
    Git
  • (プロキシ設定が必要な場合)Git Bashからプロキシを設定しておきます.
    git config --global http.proxy IPアドレス:ポート番号

2. jMetalプロジェクトの用意

プロジェクトのClone

  • GitHubのjMetalレポジトリからClone用URLをコピーしておきます.
    GitHub - jMetal/jMetal
  • IntelliJ IDEAの起動画面でCheck out from Version Control->Gitを選び、URLを指定しTest->Cloneすると,jMetalのMavenプロジェクトが作成できます.Mavenプロジェクトのpom.xmlをインポートするか聞かれるのでYesを押します.

プロジェクト設定の変更

  • 私の環境ではmaven-javadoc-pluginのエラーが発生するため、この箇所だけプロジェクトから除去しておきます.
    プロジェクトフォルダのpom.xmlを開き、以下をコメントアウトします.
  <!--
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.1.1</version>
        ...
        (中略)
        ...
    </plugin>
  !-->
  • (プロキシ設定が必要な場合)Mavenのプロキシを設定しておきます.
    右側のMavenペインを開き、適当なところで右クリック->Create settings.xmlを押すとC:\Users\ユーザー名\\.m2\settings.xmlが作成されるので、以下を追記します.
  <proxies> 
    <proxy> 
      <id>proxy01</id> 
      <active>true</active> 
      <protocol>http</protocol> 
      <host>IPアドレス</host> 
      <port>ポート</port> 
      <nonProxyHosts>192.168.0.*</nonProxyHosts> 
    </proxy> 
  </proxies>

3. 最適化計算の実行

最適化計算のサンプル

  • jMetalは単目的の最適化と多目的最適化どちらのアルゴリズムも多数含んだライブラリですが,ここでは多目的最適化アルゴリズムのサンプルとして org.uma.jmetal.example.multiobjective.nsgaii.ParallelNSGAIIExample.java
    を実行します.このクラスでは以下の計算が行われます.
  • ParallelNSGAIIExample.javaを開いて,右クリック->Debug `ParallelNSGAII....main()`をクリックするとデバッグ実行がスタートします.
    IntelliJ IDEAのConsoleがProcess finished with exit code 0で終了すれば最適化完了です.
  • プロジェクトフォルダに出力されたFUN.csvを開き散布図を描画すると、 トレードオフを考慮したパレート解が探索できていることが分かります.
    f:id:ohtayo:20200222000841p:plain

オリジナル問題の最適化計算の実行

  • ☝ではZDT問題の多目的最適化を行いましたが,jMetalでは最適化したい問題を自分で記述して,その最適化を行うことが可能です.
    org.uma.jmetal.problem.multiobjectiveに多数のベンチマーク問題が用意されているので、これらを参考に新たに問題のクラスを作成します.最適化問題クラス作成時には,概ね以下を記述する必要があります.
    • 問題種類:変数が実数か、2値かなど.変数の種類ごとにAbstract Classが用意されているので、それをextendsした最適化問題のクラスを作成します.
    • 変数の数,上下限値,目的の数:変数・目的がいくつあるかと,変数の値の範囲をコンストラクタで設定します.
    • 制約の数:制約がある場合、制約の数と制約関数も設定します.evaluateConstraints()が制約の計算で,これを目的関数evaluate()の最後に実行します.
  • ここでは例として,第1目的関数に二次関数(x2),第2目的関数に三次関数(x3)を持つ多目的最適化問題を作成してみます.変数の数は1つ,値の範囲は -5 \leq x \leq 5とします.

github.com

    package org.uma.jmetal.problem.multiobjective;

    import org.uma.jmetal.problem.doubleproblem.impl.AbstractDoubleProblem;
    import org.uma.jmetal.solution.doublesolution.DoubleSolution;
    import java.util.ArrayList;
    import java.util.List;

    @SuppressWarnings("serial")
    public class QuadraticCubic extends AbstractDoubleProblem {
    /** コンストラクタ */
    public QuadraticCubic() {
        setNumberOfVariables(1);  // 変数は1つ
        setNumberOfObjectives(2); // 目的は2つ
        setName("QuadraticCubic");
        List<Double> lowerLimit = new ArrayList<>(getNumberOfVariables()) ;
        List<Double> upperLimit = new ArrayList<>(getNumberOfVariables()) ;
        for (int i = 0; i < getNumberOfVariables(); i++) {
        lowerLimit.add(-5.0); // 変数の最小値は-5
        upperLimit.add(5.0); // 変数の最大値は5
        }
        setVariableBounds(lowerLimit, upperLimit);
    }
    /** 目的関数計算 */
    public void evaluate(DoubleSolution solution) {
        double[] x = new double[getNumberOfVariables()];
        double[] f = new double[getNumberOfObjectives()];
        for ( int i=0; i<getNumberOfVariables(); i++ ) x[i]=solution.getVariable(i); 
        f[0] = x[0]*x[0]; // 第1目的関数:二次関数
        f[1] = x[0]*x[0]*x[0]; // 第2目的関数:三次関数
        for ( int i=0; i<getNumberOfObjectives(); i++ ) solution.setObjective(i, f[i]);
      }
    }
  • あとは,上記の問題の多目的最適化を実行します.最適化アルゴリズムに合わせた実行クラスを作成し、問題とパラメータを指定します.例えばParallelNSGAIIExampleクラスであれば,problemNameという変数に問題を設定して実行していますが,このproblemNameを先ほど作成したQuadraticCubicクラスに置き換えます.
    problemName = "org.uma.jmetal.problem.multiobjective.QuadraticCubic";
    また、必要であれば最適化のパラメータを変更します.NSGA-IIでは以下のパラメータがあります.
    • 個体数(populationSize)
    • 世代数(termination):何世代探索したら最適化を終了するか指定します.探索終了の条件は計算時間とすることもできます.
    • 交叉方法(crossover):遺伝的アルゴリズムでは親個体を掛け合わせて新たな子個体を生成しますが,その方法を指定します.
    • 突然変異方法(mutation):生成された子個体の一部の変数を変異させて,新たな個体を生成する方法を指定します.
    • 淘汰方法(selection):親個体と生成された子個体のうち,次世代まで生存させる個体の選択方法を指定します.

    これらのパラメータを指定したら,先ほどと同様にアルゴリズムを実行します.二次関数と三次関数の形状と取りうる値は以下のようになっています.jMetalでは目的関数を最小にする変数を探索しますので,取りうる値のうち左下側の曲線がパレートフロントとなります. f:id:ohtayo:20200222003108p:plain この問題をNSGA-IIで最適化した結果,以下のようにトレードオフを表すパレート解集合が得られました.このようにして最適化アルゴリズムを問題に適用します.もちろん最適化アルゴリズム自体を改良して探索させることもできます. f:id:ohtayo:20200222003119p:plain

4. 実行ファイルの出力

  • 多目的最適化計算を他のコア数の多いPCやワークステーションなどで実行する場合,実行ファイル(jar)にコンパイルして実行できると便利です.
    IntelliJ IDEAの右側のMavenペインからorg.uma.jmetal:jmetal->Lifecycle->packageをダブルクリックすると,ビルド,テストをして,依存ライブラリを含めた実行ファイル(jar)を生成してくれます.
    jMetalにはサブプロジェクトとしてjmetal-core, jmetal-solution, jmetal-algorithm, jmetal-problem, jmetal-example, jmetal-lab, jmetal-autoがあるのですが,それぞれにjmetal-xx\target\jmetal-xx-6.0-SNAPSHOT-jar-with-dependencies.jarが生成されます.これらをコピーすれば,以下のようなコマンドで,多目的最適化計算をJREのインストールされた他のPCで実行できます.
    java ‐cp jmetal‐core.jar:jmetal‐solution.jar:jmetal‐problem.jar:jmetal‐algorithm.jar;jmetal-example.jar;jmetal-lab.jar;jmetal-auto.jar “org.uma.jmetal.example.multiobjective.nsgaii.ParallelNSGAIIExample”

まとめ

jMetalを使って多目的最適化計算を実行するまでの手順を紹介しました.アルゴリズム開発に必要な周辺の関数群も比較的充実しており,Javaを使っているのであれば, jMetalはメタヒューリスティクスに関する研究開発のツールとして良い選択肢になると思います.
jMetal 6からは自動でパラメータチューニングを行うjmetal-auto, 探索経過表示やアルゴリズム比較を行うためのjmetal-labも追加されており,これらについても利用方法を紹介できればと思います.