自定义似然函数并最优化求解——基于python的scipy.optimize.minimize
minimize提供的方法能够解决无/有约束的线性或非线性的多个决策变量目标函数的最优化问题,是解决优化问题的利器。本篇文章介绍其如何使用,并提供了代码示例。
1. python的scipy.optimize.minimize 介绍
minimize提供的方法能够解决无/有约束的线性或非线性的多个决策变量目标函数的最优化问题,但是由于该模块是依据函数导数与梯度进行求解,不能够求解整数规划、01规划等问题。完整参数如下:
minimize(fun, x0, args=(), method=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None)
各个参数的含义如下:
| 参数 | 含义 | 使用说明 |
|---|---|---|
|
fun |
待最小化求解的目标函数(用户自定义) | 假设用户有一个自定义方程 fun(x, y, params), 但是使用时不直接传入 fun(x, y, params),而是将其先包装一下,使得fun变成一个仅依赖于params的函数,这样才能通过搜寻合适的params来最小化fun。例如,可以传递这样一个匿名函数(lambda表达式)lambda params: fun(x, y, params) 作为可调用对象 |
| x0 | 初始化(即目标函数中的参数的初始化值) | 一个Array,形状是(n,) n是待搜存参数个数 |
| args |
目标函数带参数时需要指定 |
元组,args 参数用于传递额外的参数给目标函数。它包含除了优化变量外的所有其他参数,这些参数会被直接传递给目标函数及其导数(包括fun、jac 和hess函数)的额外参数。 |
| method | 优化器类型 |
如果没有指定,则根据问题是否有约束或边界条件,默认选择 BFGS、L-BFGS-B 或 SLSQP 中的一种。 |
| jac | 雅可比矩阵或梯度函数 |
jac{callable, ‘2-point’, ‘3-point’, ‘cs’, bool} 计算梯度向量的方法,仅适用于CG、BFGS、Newton-CG、L-BFGS-B、TNC、SLSQP、dogleg、trust-ncg、trust-krylov、trust-exact和trust-constr。
|
| hess | hessian海塞矩阵 |
hess{callable, ‘2-point’, ‘3-point’, ‘cs’, HessianUpdateStrategy} 仅适用于Newton-CG, dogleg, trust-ncg, trust-krylov, trust-exact and trust-constr |
| hessp | 海塞向量积 |
目标函数的Hessian矩阵与任意向量 p 的乘积。仅适用于Newton-CG、trust-ncg、trust-krylov和trust-constr。 只需要提供 hessp 或 hess 其中之一即可。如果提供了 hess,则会忽略 hessp。hessp 必须能够计算Hessian矩阵与任意向量的乘积。 |
| bounds | 寻优范围 |
sequence or bounds,例如 变量的边界适用于Nelder-Mead、L-BFGS-B、TNC、SLSQP、Powell、trust-constr、COBYLA和COBYQA方法。有两种方式可以指定边界: (1)使用Bounds类的实例:Bounds(lb=-inf, ub=inf, keep_feasible=False) (2)为params中的每个元素指定(min, max)对的序列。使用None表示没有边界。 |
| constraints | 限制约束 |
仅适用于COBYLA、COBYQA、SLSQP和trust-constr方法。 (1)对于'trust-constr'和'COBYQA',约束被定义为一个对象或多个对象的列表。
|
| tol |
浮点数,误差容差值 | 算法终止时的容差值。 |
| callback | 回调函数 |
除了TNC、SLSQP和COBYLA之外,所有方法都支持调用回调函数: callback(intermediate_result: OptimizeResult) |
| options | 字典,不同方法有不同选项 | 通过一个字典来指定求解器的选项,包括最大迭代次数(maxiter)和是否显示收敛消息(disp)。需要注意的是,TNC方法使用maxfun而不是maxiter来限制函数评估的最大数量。这些选项允许用户更细致地控制优化过程的行为。 |
更多详细信息可以用show_options:
from scipy.optimize import show_options
show_options(solver="minimize")
# Specifying a method is possible:
show_options(solver="minimize", method="Nelder-Mead")
# We can also get the documentations as a string:
show_options(solver="minimize", method="Nelder-Mead", disp=False)
2. 示例1:用极大似然函数求解回归方程的系数
2.1 写出待求解函数的极大似然函数的数学表达式
假设我们有一个线性模型,其中,
。我们的目标是找到一组参数
,使得数据集下的对数似然函数最大。
对于每个观测值, 残差为
,那么所有观测值的联合概率密度由下式给出:
最大化似然函数等价于最小化负对数似然函数:
由于第一项与 无关,我们可以忽略它,从而简化问题为最小化第二项:
2.2 用scipy.optimize.minimize来求解极大似然函数
示例代码如下所示:
import pandas as pd
import numpy as np
from scipy.optimize import minimize
# Step 1: Define the function and the negative log-likelihood
def model_function(beta, x):
return beta[0] + beta[1] * x[:, 0] + beta[2] * x[:, 1]**2 + beta[3] * np.log(x[:, 2])
def neg_log_likelihood(beta, x, y):
residuals = y - model_function(beta, x)
mse = np.mean(residuals**2)
n = len(y)
return mse
# Step 2: Generate synthetic data
# step 2.1 generate x
np.random.seed(42)
n_samples = 10000
x1 = np.random.rand(n_samples)
x2 = np.random.rand(n_samples)
x3 = np.random.uniform(1, 10, n_samples) # Ensure x3 > 0 for log calculation
X = np.column_stack((x1, x2, x3))
# step 2.1 generate beta & y
true_beta = [1, 2, 3, 4] # True parameters for generating data
noise = np.random.normal(0, 1, n_samples)
y = model_function(true_beta, X) + noise
# Step 3: Use scipy.optimize.minimize to find the optimal beta
initial_guess = [0, 0, 0, 0]
result = minimize(neg_log_likelihood, initial_guess, args=(X, y), method='BFGS',options={'disp': True})
print(result)
estimated_beta = result.x
print("Estimated Beta:", estimated_beta)
# 另外一种方法,用于对比lambda和args的写法,二者都可以实现目的
result = minimize(lambda beta: neg_log_likelihood(beta, X, y), initial_guess, method='BFGS')
estimated_beta = result.x
print("Estimated Beta:", estimated_beta)
# Estimated Beta: [1.0117673 1.98887947 3.01406188 3.98902748]
# 再换一种优化器,Nelder-Mead单纯形法
result = minimize(lambda beta: neg_log_likelihood(beta, X, y), initial_guess, method='Nelder-Mead')
print("Estimated Beta:", result.x)
# Estimated Beta: [1.01095591 1.98947494 3.01293004 3.98956425]
3. 最优化算法的介绍
不同的优化器适用于不同类型的优化问题,主要取决于目标函数的特性(如是否平滑、是否存在约束等)。以下是对每种优化器适用场景的简要说明:
-
Nelder-Mead:适用于无约束优化问题。它不使用梯度信息,因此适合于不可微或难以计算导数的目标函数。
-
Powell:适用于无约束优化问题,尤其是当梯度难以计算时。它是一种共轭方向方法。
-
CG (Conjugate Gradient):适用于大规模无约束优化问题,特别是当目标函数是二次可微的时候。它比简单的梯度下降更高效。
-
BFGS (Broyden–Fletcher–Goldfarb–Shanno):一种拟牛顿法,适用于无约束优化问题。它利用近似Hessian矩阵来加速收敛,适用于平滑函数。
-
Newton-CG:结合了牛顿法和共轭梯度法的优点,适用于无约束优化问题,尤其是目标函数及其梯度和Hessian矩阵都容易计算的情况。
-
L-BFGS-B:有限内存BFGS算法的变体,支持边界约束条件下的优化问题。
-
TNC (Truncated Newton):适用于带有变量边界的大型无约束优化问题。它是为数值稳定性设计的一种截断牛顿算法。
-
COBYLA (Constrained Optimization BY Linear Approximations):适用于带约束的优化问题,特别是当约束条件是非线性的,且不需要精确的梯度信息时。
-
COBYQA:似乎是指“Constrained Optimization BY Quadratic Approximation”,这是一种基于二次逼近的方法,用于处理有约束的优化问题。但请注意,这并非标准命名,可能是对某些特定实现的误称。
-
SLSQP (Sequential Least Squares Programming):适用于带有等式和/或不等式约束的优化问题,能够有效地处理非线性约束。
-
trust-constr:信赖域算法,适用于具有复杂约束条件的问题,包括等式和不等式约束。
-
dogleg:信赖域方法的一种,适用于需要快速局部收敛的情况,通常用于解决非线性最小二乘问题。
-
trust-ncg (Newton Conjugate Gradient):信赖域牛顿共轭梯度法,适用于需要高精度解的大规模无约束优化问题。
-
trust-exact:精确信赖域方法,适用于需要高精度解的小到中等规模的优化问题。
-
trust-krylov:使用Krylov子空间方法的信赖域优化,适用于大规模问题,尤其是当求解Hessian矩阵与向量的乘积比较经济时。
选择合适的优化器取决于具体问题的特点,如目标函数的形式、是否有约束条件以及这些约束的性质等。
4. 示例2:待约束条件的最优化求解
假设我们要最小化以下函数:
同时,我们有以下约束条件:
此外,我们还有边界条件:
使用 scipy.optimize.minimize 求解
代码如下所示:
import numpy as np
from scipy.optimize import minimize
# Step 1: Define the objective function
def objective_function(x):
return (x[0] - 1)**2 + (x[1] - 2.5)**2
# Step 2: Define the constraints
constraints = [
{'type': 'ineq', 'fun': lambda x: x[0] + x[1] - 1}, # x1 + x2 >= 1
{'type': 'ineq', 'fun': lambda x: 4 - (x[0]**2 + x[1]**2)} # x1^2 + x2^2 <= 4
]
# Step 3: Define the bounds for each variable
bounds = [(0, 3), (0, 3)] # 0 <= x1 <= 3, 0 <= x2 <= 3
# Step 4: Initial guess
initial_guess = [0, 0]
# Step 5: Use scipy.optimize.minimize to find the optimal solution
result = minimize(objective_function, initial_guess, method='SLSQP', bounds=bounds, constraints=constraints)
# Print the results
print("Optimal Solution:", result.x)
print("Objective Function Value at Optimal Solution:", result.fun)
print("Success:", result.success)
print("Message:", result.message)
# Optimal Solution: [0.74278177 1.85695322]
# Objective Function Value at Optimal Solution: 0.4796703779910314
# Success: True
# Message: Optimization terminated successfully
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)