SciPy - 优化
SciPy - 优化 (scipy.optimize)
Section titled “SciPy - 优化 (scipy.optimize)”scipy.optimize 包提供了丰富的函数最小化(优化)、求解方程根和曲线拟合算法集。优化是寻找导致给定函数取得最小值(或最大值)的输入值(参数)的过程。
此模块包含以下工具:
- 单变量函数最小化: 寻找单变量函数的最小值 (
minimize_scalar)。 - 多变量函数最小化: 寻找多变量函数的最小值,包括无约束和有约束的 (
minimize)。提供了多种算法(例如,BFGS, Nelder-Mead, SLSQP)。 - 全局优化: 旨在找到全局最小值的例程,可能避免局部最小值(例如,
basinhopping,differential_evolution)。 - 最小二乘最小化: 最小化残差平方和,常用于曲线拟合(
least_squares,curve_fit)。 - 求解方程: 求解 $f(x) = 0$ 形式的方程。用于单变量函数(
newton,brentq)和多变量系统(root)。
使用 minimize() 最小化多变量标量函数
Section titled “使用 minimize() 最小化多变量标量函数”minimize() 函数为最小化一个或多个变量的标量函数提供了统一的接口。让我们考虑 Rosenbrock 函数,它是优化算法的常见基准。对于 N 个变量,它定义为:
$$f(\mathbf{x}) = \sum_{i=0}^{N-2} [100(x_{i+1} - x_i^2)^2 + (1 - x_i)^2]$$
全局最小值是 $f(\mathbf{x}) = 0$,出现在 $\mathbf{x} = (1, 1, \ldots, 1)$ 处。
Nelder-Mead 单纯形算法
Section titled “Nelder-Mead 单纯形算法”Nelder-Mead 算法是一种直接搜索方法,不需要梯度信息。它对于非光滑函数通常很鲁棒,但可能比基于梯度的算法慢。
import numpy as npfrom scipy.optimize import minimize
# 定义 N 变量的 Rosenbrock 函数def rosenbrock_func(x): """Rosenbrock 函数。""" return sum(100.0 * (x[i+1] - x[i]**2.0)**2.0 + (1 - x[i])**2.0 for i in range(len(x)-1))
# 初始猜测(例如,对于 5 维 Rosenbrock 函数)x_initial = np.array([1.5, 0.8, 1.2, 0.7, 1.1])
print(f"Initial guess x0: {x_initial}")print(f"Rosenbrock function value at initial guess: {rosenbrock_func(x_initial):.4f}")
# 使用 Nelder-Mead 方法进行最小化# `method='Nelder-Mead'`result_nelder_mead = minimize(rosenbrock_func, x_initial, method='Nelder-Mead', options={'xatol': 1e-8, 'disp': True})
print("\nResult from Nelder-Mead:")print(f"Optimal x: {result_nelder_mead.x}")print(f"Function value at optimum: {result_nelder_mead.fun}")print(f"Success: {result_nelder_mead.success}")# print(f"Message: {result_nelder_mead.message}")输出将显示优化过程(如果 disp=True)和最终结果:
Initial guess x0: [1.5 0.8 1.2 0.7 1.1]Rosenbrock function value at initial guess: 145.1000Optimization terminated successfully. Current function value: 0.000000 Iterations: 378 Function evaluations: 662
Result from Nelder-Mead:Optimal x: [1. 1. 1. 1. 0.99999999]Function value at optimum: 5.959159901745691e-17Success: True单纯形算法简单且不需要梯度,因此适用于某些问题。然而,对于更光滑的函数,基于梯度的算法如 BFGS (Broyden-Fletcher-Goldfarb-Shanno),可通过 method='BFGS' 使用,通常更快、更准确。要使用 BFGS,你可能需要提供函数的 Jacobian(梯度),或者算法可以对其进行近似。
使用 least_squares 进行最小二乘最小化
Section titled “使用 least_squares 进行最小二乘最小化”最小二乘优化旨在找到参数 $\mathbf{x}$,使 $m$ 个残差函数 $r_i(\mathbf{x})$ 的平方和最小:$Cost(\mathbf{x}) = \sum_{i=0}^{m-1} r_i(\mathbf{x})^2$。这在数据拟合(曲线拟合)中非常常见。
让我们再次使用 Rosenbrock 函数,但将其构建为一个最小二乘问题。Rosenbrock 函数可以写成 $f(x_0, x_1) = r_0(x_0, x_1)^2 + r_1(x_0, x_1)^2$,其中 $r_0 = 10(x_1 - x_0^2)$ 且 $r_1 = (1 - x_0)$。
import numpy as npfrom scipy.optimize import least_squares
# 定义 Rosenbrock 的残差函数(此处为简化起见使用 2D 情况)def rosenbrock_residuals(x): # x 预期是一个数组 [x0, x1] r0 = 10 * (x[1] - x[0]**2) r1 = (1 - x[0]) return np.array([r0, r1])
# x = [x0, x1] 的初始猜测x_initial_ls = np.array([2.0, 2.0])
print(f"Initial guess for least_squares: {x_initial_ls}")print(f"Residuals at initial guess: {rosenbrock_residuals(x_initial_ls)}")
# 求解最小二乘问题result_least_squares = least_squares(rosenbrock_residuals, x_initial_ls)
print("\nResult from least_squares:")print(f"Optimal x: {result_least_squares.x}")print(f"Value of residuals at optimum: {result_least_squares.fun}") # These are the r_i valuesprint(f"Cost function (sum of squares of residuals): {result_least_squares.cost}")print(f"Success: {result_least_squares.success}")# print(f"Message: {result_least_squares.message}")least_squares 函数需要一个返回残差向量的函数。然后它最小化这些残差的平方和。2D Rosenbrock 函数的精确最小值在 $x = [1.0, 1.0]$ 处。
输出显示收敛到已知最小值:
Initial guess for least_squares: [2. 2.]Residuals at initial guess: [-20. -1.]
Result from least_squares:Optimal x: [1. 1.]Value of residuals at optimum: [4.4408921e-15 0.0000000e+00]Cost function (sum of squares of residuals): 9.866924291084687e-30Success: True对于曲线拟合,scipy.optimize.curve_fit 是一个专门且通常更方便的函数,它使用最小二乘法将用户定义的函数拟合到数据。
求解方程根(Root finding)涉及求解 $f(x) = 0$(对于标量函数)或 $\mathbf{f}(\mathbf{x}) = \mathbf{0}$(对于方程组)形式的方程。
单变量函数求解方程根
Section titled “单变量函数求解方程根”对于单变量方程,scipy.optimize 提供了几种算法,如 brentq(如果已知包含根的区间,这通常是最佳选择)、newton(需要导数或对其进行近似)和 bisect。
使用 root() 求解方程组
Section titled “使用 root() 求解方程组”要找到一组非线性方程 $\mathbf{f}(\mathbf{x}) = \mathbf{0}$ 的根,请使用 root() 函数。它支持多种方法,包括 ‘hybr’ (Powell 的混合方法,默认) 和 ‘lm’ (Levenberg-Marquardt)。
考虑求解单变量超越方程 $2x + 2\cos(x) = 0$。我们正在寻找满足 $f(x) = 2x + 2\cos(x)$ 为零的 $x$。
import numpy as npfrom scipy.optimize import root
# 定义我们要找到根的函数def func_for_root(x): # 注意:即使是单变量问题,使用 root() 时 x 也需要是一个数组 return 2 * x[0] + 2 * np.cos(x[0])
# 方程根的初始猜测initial_guess_root = np.array([0.0]) # 对于 root() 必须是一个数组
solution = root(func_for_root, initial_guess_root)
print("Solution from root():")print(f"Root x: {solution.x}")print(f"Function value at root f(x): {solution.fun}") # 应该接近 0print(f"Success: {solution.success}")# print(f"Message: {solution.message}")输出将显示找到的根:
Solution from root():Root x: [-0.73908513]Function value at root f(x): [0.]Success: True这意味着 $x approx -0.739$ 是方程 $2x + 2\cos(x) = 0$ 的一个根。
优化的实际应用
Section titled “优化的实际应用”优化技术在以下领域至关重要:
- 机器学习: 通过最小化损失函数来训练模型。
- 工程: 设计结构、流程或系统以实现最大效率或最小成本。
- 金融: 投资组合优化、风险管理。
- 物流: 路径规划、资源分配。
- 科学研究: 模型中的参数估计,以拟合实验数据。
选择合适的优化算法取决于问题的特征:是约束问题还是无约束问题,是否可获得导数,函数的平滑度,以及是寻求全局最小值还是局部最小值。始终查阅 SciPy 文档,获取关于每个优化器的优势和劣势的详细指导。