scipy.optimize.minimizeとは
scipy.optimize.minimize は、Pythonの科学計算ライブラリSciPyに含まれる最適化関数です。与えた目的関数(最小化したい関数)の最小値と、そのときの変数の値を求めます。
機械学習のハイパーパラメータ調整、コスト最適化、物理シミュレーションなど、数値的に最小値を探したい場面で広く使われます。
この記事では、minimize の基本的な使い方から、実務でよく使う3つの要素である bounds(変数の範囲制限)・constraints(制約条件)・method(最適化手法) の設定方法を実例とともに解説します。
基本的な使い方
まず、引数なしで最もシンプルな形から確認します。
SciPyがインストールされていない場合は、以下のコマンドでインストールしてください。
pip install scipy
最小限のコード例
目的関数として f(x) = (x - 3)² を最小化します。最小値は x = 3 のときです。
from scipy.optimize import minimize
# 目的関数
def objective(x):
return (x[0] - 3) ** 2
# 初期値(xの初期推定値)
x0 = [0.0]
result = minimize(objective, x0)
print(result.x) # 最適解
print(result.fun) # 目的関数の最小値
print(result.success) # 最適化が成功したか
[3.]
8.881784197001252e-16
True
result.x が最適解(ここでは x ≈ 3)、result.fun が目的関数の最小値です。数値誤差のためゼロに非常に近い値が出ますが、実質的にゼロとみなせます。
minimizeの主な引数
| 引数 | 内容 |
|---|---|
fun | 最小化したい目的関数 |
x0 | 変数の初期値(リストまたはnumpy配列) |
method | 最適化アルゴリズムの指定 |
bounds | 各変数の下限・上限 |
constraints | 等式・不等式の制約条件 |
options | 最大繰り返し回数などの設定 |
bounds(変数の範囲制限)の使い方
bounds は、各変数に「この範囲の中だけで解を探してほしい」という制限をかけるときに使います。
たとえば、商品の仕入れ数量は0以上でなければならない、割引率は0〜1の範囲に収まる必要がある、といった場面で使います。
boundsの書き方
from scipy.optimize import minimize
# 目的関数(2変数)
def objective(x):
return (x[0] - 3) ** 2 + (x[1] - 5) ** 2
x0 = [0.0, 0.0]
# 各要素が (下限, 上限)。Noneは制限なしを意味する
bounds = [(0, 2), (0, 4)]
result = minimize(objective, x0, bounds=bounds)
print(result.x)
print(result.fun)
[2. 4.]
2.0
x[0] の最適解は本来3ですが、上限が2のため x[0] = 2 に抑えられています。同様に x[1] は上限4で抑えられています。
Boundsクラスを使う方法
scipy.optimize.Bounds クラスを使って書くこともできます。変数が多い場合はこちらの方がコードが整理されます。
from scipy.optimize import minimize, Bounds
def objective(x):
return (x[0] - 3) ** 2 + (x[1] - 5) ** 2
x0 = [0.0, 0.0]
# Boundsクラスで指定(lb=下限, ub=上限)
bounds = Bounds(lb=[0, 0], ub=[2, 4])
result = minimize(objective, x0, bounds=bounds)
print(result.x)
[2. 4.]
片方だけ制限する場合
下限だけ設けたい場合は上限に np.inf または None を使います。
import numpy as np
from scipy.optimize import minimize
def objective(x):
return (x[0] - 3) ** 2
x0 = [0.0]
# 下限0、上限なし
bounds = [(0, None)]
result = minimize(objective, x0, bounds=bounds)
print(result.x)
[3.]
constraints(制約条件)の使い方
constraints は、変数の組み合わせに条件をかけるときに使います。boundsが「各変数の範囲」であるのに対して、constraintsは「複数の変数の関係」に制約をかけます。
constraintsの種類
| 種類 | キー | 内容 |
|---|---|---|
| 等式制約 | 'eq' | fun(x) = 0 を満たすように探す |
| 不等式制約 | 'ineq' | fun(x) >= 0 を満たすように探す |
辞書形式で {'type': 'eq', 'fun': 関数} のように指定します。
等式制約の例
x[0] + x[1] = 1(合計が1になる)という条件を設けます。
from scipy.optimize import minimize
def objective(x):
return (x[0] - 0.3) ** 2 + (x[1] - 0.7) ** 2
x0 = [0.5, 0.5]
# x[0] + x[1] - 1 = 0 と書く
constraints = {'type': 'eq', 'fun': lambda x: x[0] + x[1] - 1}
result = minimize(objective, x0, constraints=constraints, method='SLSQP')
print(result.x)
print(f"合計: {result.x[0] + result.x[1]:.6f}")
[0.3 0.7]
合計: 1.000000
制約を満たしながら目的関数が最小化されました。
不等式制約の例
x[0] + x[1] >= 1(合計が1以上)という条件を設けます。
from scipy.optimize import minimize
def objective(x):
return x[0] ** 2 + x[1] ** 2 # 原点に近づこうとする
x0 = [1.0, 1.0]
# x[0] + x[1] - 1 >= 0 と書く
constraints = {'type': 'ineq', 'fun': lambda x: x[0] + x[1] - 1}
result = minimize(objective, x0, constraints=constraints, method='SLSQP')
print(result.x)
print(f"合計: {result.x[0] + result.x[1]:.6f}")
[0.5 0.5]
合計: 1.000000
目的関数は原点(0,0)を目指しますが、制約 x[0] + x[1] >= 1 により [0.5, 0.5] が最適解になります。
boundsとconstraintsを同時に使う
from scipy.optimize import minimize
def objective(x):
return -x[0] * x[1] # x[0] * x[1] を最大化 = -x[0]*x[1] を最小化
x0 = [1.0, 1.0]
# 各変数は0以上
bounds = [(0, None), (0, None)]
# x[0] + x[1] <= 10(合計が10以下)
# 'ineq' は fun(x) >= 0 なので 10 - x[0] - x[1] >= 0 と書く
constraints = {'type': 'ineq', 'fun': lambda x: 10 - x[0] - x[1]}
result = minimize(objective, x0, bounds=bounds, constraints=constraints, method='SLSQP')
print(result.x)
print(f"最大積: {result.x[0] * result.x[1]:.4f}")
[5. 5.]
最大積: 25.0000
method(最適化手法)の選び方
method では、どのアルゴリズムで最適化を行うかを指定します。指定しない場合は問題の種類に応じて自動的に選ばれますが、bounds や constraints を使う場合は明示的に指定する方が安全です。
主なmethodと使い分け
| method | 特徴 | 向いているケース |
|---|---|---|
'SLSQP' | boundsとconstraints両方に対応 | 制約付き問題の標準的な選択 |
'L-BFGS-B' | boundsに対応、大規模問題に強い | 変数が多い、boundsのみの問題 |
'Nelder-Mead' | 微分不要(勾配情報不要) | 目的関数が複雑・不連続な場合 |
'BFGS' | 勾配ベース、高精度 | boundsなし・constraintsなしの滑らかな問題 |
'trust-constr' | boundsとconstraints両方に対応 | 大規模・高精度が必要な制約付き問題 |
boundsのみ → L-BFGS-Bの例
from scipy.optimize import minimize
def objective(x):
return (x[0] - 2) ** 2 + (x[1] - 4) ** 2
x0 = [0.0, 0.0]
bounds = [(0, 1.5), (0, 3.0)]
result = minimize(objective, x0, method='L-BFGS-B', bounds=bounds)
print(result.x)
print(result.message)
[1.5 3. ]
b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PRTOL'
constraints付き → SLSQPの例
bounds と constraints を同時に使う場合は SLSQP が基本の選択肢です。
from scipy.optimize import minimize
def objective(x):
return (x[0] - 1) ** 2 + (x[1] - 2) ** 2
x0 = [0.0, 0.0]
bounds = [(0, 3), (0, 3)]
constraints = {'type': 'eq', 'fun': lambda x: x[0] + x[1] - 2}
result = minimize(
objective, x0,
method='SLSQP',
bounds=bounds,
constraints=constraints
)
print(result.x)
print(result.success)
[1. 1.]
True
よくあるミスと注意点
初期値が結果に影響する
minimize は局所最小値を探すアルゴリズムです。初期値によっては、全体の最小値(大域最小値)ではなく、局所的な最小値に収束することがあります。
複数の初期値で試して結果を比較するのが安全です。
from scipy.optimize import minimize
import numpy as np
def objective(x):
# 複数の局所最小値を持つ関数
return np.sin(x[0]) + 0.1 * x[0] ** 2
results = []
for x0_val in [-5, 0, 5]:
res = minimize(objective, [x0_val])
results.append((x0_val, res.x[0], res.fun))
for x0, x_opt, f_opt in results:
print(f"初期値={x0:3d} → x={x_opt:.4f}, f(x)={f_opt:.4f}")
初期値= -5 → x=-4.6746, f(x)=-0.9988
初期値= 0 → x=-1.5708, f(x)=-1.0000
初期値= 5 → x=4.7124, f(x)=-0.9974
初期値によって異なる局所最小値に収束しています。
constraintsのineqの方向に注意
'ineq' は「fun(x) >= 0」という方向です。「以下(≦)」の制約を書くときは符号を反転します。
# x[0] + x[1] <= 10 を書く場合
# 10 - x[0] - x[1] >= 0 に変換する
constraints = {'type': 'ineq', 'fun': lambda x: 10 - x[0] - x[1]}
符号が逆だと意図とは逆の制約になるため注意してください。
result.successを必ず確認する
minimize は失敗しても例外を出さず、result.success = False で通知します。最適化後は必ず確認します。
result = minimize(objective, x0, bounds=bounds, constraints=constraints, method='SLSQP')
if not result.success:
print(f"最適化失敗: {result.message}")
else:
print(f"最適解: {result.x}")
boundsとmethodの組み合わせミス
bounds を指定しても、method='BFGS' など bounds に対応していないmethodを使うと、範囲制限が無視されます。bounds を使う場合は L-BFGS-B または SLSQP、constraints も使う場合は SLSQP を指定してください。
AI時代での補足
AIにコード生成を任せる場面でも、bounds の設定ミスや constraints の符号間違いは人間が確認しなければ気づきにくいエラーです。最適化の構造を理解しておくと、生成されたコードの意図を読み取って修正できます。
まとめ
この記事では scipy.optimize.minimize の基本的な使い方と、bounds・constraints・method の設定方法を解説しました。
- bounds:変数の上限・下限を設ける。タプルのリストまたは
Boundsクラスで指定 - constraints:変数間の関係に等式(
'eq')または不等式('ineq')の制約をかける - method:boundsのみなら
L-BFGS-B、constraintsも使うならSLSQPが基本の選択 - 注意点:初期値依存・
ineqの符号・result.successの確認を忘れずに
最適化問題は機械学習モデルのハイパーパラメータ探索とも関連します。scikit-learnのGridSearchCVによる探索方法に興味がある場合は、グリッドサーチ(GridSearchCV)の使い方も参考にしてください。