2018-02-28 (Wed.)
Wikipedia は読まなくていいです.
scipy に実装があります.
勉強のために実装してみた. 参考文献 [1] 先と同様に Ackley 関数上での最適化を試してみる. 下図に示すようにこの関数は \(0\) で最小値 \(0\) を取るが \(\cos\) を重ねているのでいろんなところで極小値を取り, 単純な勾配法だと簡単に局所解に落ち着いてしまう.

注意点として, \(0\) のすぐ付近だけだと単純な凸関数で, 実際より簡単な最適化問題になってしまうので, 広い範囲で実験しないと意味がない. (参考文献 [1] だと \([0,1)\) でしか探索してない?)
import random
from typing import Tuple
import numpy
def Ackley(x: numpy.array) -> float:
    """目的関数"""
    a = 20
    b = 0.2
    c = 2 * numpy.pi
    ret = a - a * numpy.exp(-b * numpy.sqrt(numpy.sum(x ** 2) / len(x))) \
        + numpy.e - numpy.exp(numpy.sum(numpy.cos(c * x)) / len(x))
    return ret
def DE(target, ranges, np=40, cr=0.5, f=0.5, loop=10, verbose=False) -> Tuple[numpy.array, float]:
    """最適化の実行
    Parameters
    ----------
    target: numpy.array -> float
        最小化する目的関数
    ranges: List[Tuple(float, float)]
        パラメータ空間(超区間とする)
    np: int
        num of population
        シードを保存するプールのサイズ
    cr: float
        交叉確率
        assert 0 < cr < 1
    f: float
        交叉のさせ方(混ぜ方)
        assert 0 < f < 1
    loop: int
        ステップ実行回数
    """
    def make_random():
        x = numpy.array([numpy.random.uniform(left, right) for left, right in ranges])
        return x
    xs = [make_random() for _ in range(np)]
    ys = [target(x) for x in xs]
    mini = ys[0]
    min_index = 0
    for _ in range(loop):
        for i in range(np):
            j1, j2, j3 = random.sample(range(np - 1), 3)
            a = xs[(i + j1 + 1) % np]
            b = xs[(i + j2 + 1) % np]
            c = xs[(i + j3 + 1) % np]
            x_new = xs[i] + 0.0
            # cross
            k = random.choice(range(len(ranges)))
            for j in range(len(ranges)):
                if j == k or random.random() < cr:
                    x_new[j] = a[j] + f * (b[j] - c[j])
            x_new = numpy.array(x_new)
            y_new = target(x_new)
            if y_new < ys[i]:
                xs[i] = x_new
                ys[i] = y_new
                if y_new < mini:
                    mini = y_new
                    min_index = i
                    if verbose:
                        print(mini)
    return xs[min_index], ys[min_index]
result = DE(Ackley, [[0, 20], [-100, 100]], loop=100, verbose=True)
print(result)
例えば探索範囲を 100 くらいにまで広げると、ループ回数が 10 とかだと全然到達できないが、100 までにしとけば大体安定する.