若干ハマって解決法が出てこなかったのでメモ。
X,yともにデータ(m件)が入っていて、Xがm×nの行列、yがm次元のベクトルとします。例えばロジスティック回帰のコスト関数
import numpy as np
from scipy.optimize import fmin
def cost_function(theta, X, y):
h_theta = 1 / (1 + np.exp(np.dot(-X, theta)))
J = np.sum(-y * np.log(h_theta) - (1 - y) * np.log(1 - h_theta)) / len(y)
grad = np.zeros(len(theta))
for j in range(len(theta)):
grad[j] = sum((h_theta - y) * X[:, j]) / len(y)
return J, grad
でthetaを変化させて、Jを最小化したいような問題を解きたいとします。scipy.optimize
モジュールには組み込みの関数があります。
参考:Optimization (scipy.optimize)
fmin関数を使うとシンプレックス法で最小化問題を解くことができます。
##ダメな例
fmin(cost_function, initial_theta, args=(X, y, ))
出力
print(fmin(cost_function, initial_theta, args=(X, y, )))
File "C:\Program Files\Anaconda3\lib\site-packages\scipy\optimize\optimize.py"
, line 393, in fmin
res = _minimize_neldermead(func, x0, args, callback=callback, **opts)
File "C:\Program Files\Anaconda3\lib\site-packages\scipy\optimize\optimize.py"
, line 517, in _minimize_neldermead
fsim[k] = func(sim[k])
ValueError: setting an array element with a sequence.
スタックトレースからはわかりづらいが、原因はどの返り値で最適化していいかわからないかららしい。
##解決策
最適化に必要な1つの返り値のみを返す関数で1回ラップして、そのラップした関数を最適化します。
cost_wrap = lambda theta,X,y : cost_function(theta, X, y)[0]
fmin(cost_wrap, initial_theta, args=(X, y, ))
これで上手く行きました。
ちなみにargsの引数には、最適化で動かしても変化させない定数(例えばデータなど)を入れます。このargsを使えば目的関数の引数が複数の場合でも対応できます。