hyperoptで整数値の探索

今回もhyperoptの話題です。
この前の記事では、2変数関数の最小値を探すとき、
それぞれの変数を、一定区間の一様分布の中から探索しました。
そのときは、hp.uniformを使いました。

ただ、場合によっては、変数は実数ではなく整数を取ることもあります。
多くの例があると思いますが、機械学習で言えばDNNの中間層のユニット数などがそうですね。

この場合、 hyperoptで探索する空間を定義するときは、hp.randint(label, upper)というのを使うことで実現できます。
ドキュメントはこちら。
2.1 Parameter Expressions
ただ、これよく見ると最小値の指定ができません。
(ドキュメントに書かれてないだけで実は動くのではと思い試しましたが、本当にできません)

そのような時にどうしようかというのが今回の記事です。
0からではなく、1以上の数で探したいケースや、負の数も扱いたい時などありますからね。

今回使う関数はこちら。
$$f(x, y) = (x-5)^2 + (y+7)^2$$
この関数の$-10 \leq x\leq 10, -10 \leq y\leq 10$ の範囲で最小値を取る点を探します。
正解は明らかに$(x, y) = (5, -7)$ です。

それを実行するコードがこちら。


from hyperopt import hp, fmin, tpe, Trials


# 最小値を探したい関数
def f(x, y):
    return (x-5)**2 + (y+7)**2


# 探索する関数を定義する
def objective(args):
    x = args["x"]
    y = args["y"]

    return f(x, y)


# 探索する空間を定義する
space = {
        'x': -10 + hp.randint('x_', 21),
        'y': -10 + hp.randint('y_', 21),
    }

trials = Trials()

best = fmin(
            fn=objective,
            space=space,
            algo=tpe.suggest,
            max_evals=100,
            trials=trials,
        )

print(best)

# 以下出力
100%|██████████| 100/100 [00:00<00:00, 427.25it/s, best loss: 2.0]
{'x_': 16, 'y_': 2}

ちょっと惜しいですが、 f(x, y)が 2になる点を見つけたようです。

ポイントはここ


space = {
        'x': -10 + hp.randint('x_', 21),
        'y': -10 + hp.randint('y_', 21),
    }

このように定義することで、x_y_ はそれぞれ 0〜20の値を取り、
-10することで、xyは-10から10の値を取ります。
21にはならないので注意。(trials.vals["x_"]の値を確認するとx_=21は探していないことがわかります。)

出力の、{'x_': 16, 'y_': 2}から、
$x=16-10=6, y=2-10=8$ を見つけていることがわかります。

ただ、本当はx,yの値が欲しいのに、x_,y_を戻されると少しだけ不便です。
このようなときは、space_evalという関数が使えるようです。
ドキュメントが見つからないのですが、このページのサンプルコードで使われているのを参考にやってみます。


from hyperopt import space_eval
print(space_eval(space, best))

# 出力
{'x': 6, 'y': -8}

これで必要だった(x, y)の値が取れました。
正解から1ずつずれているのがちょっと惜しいですね。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です