Julia 教程 / Julia 数值优化(Optim.jl 与 Convex.jl)
Julia 数值优化(Optim.jl 与 Convex.jl)
数值优化是机器学习、工程设计、金融建模等领域的核心工具。Julia 生态提供了 Optim.jl(通用优化)和 Convex.jl(凸优化建模)两套强大的优化框架。
1. Optim.jl 概述
using Optim
# 最简单的优化问题:最小化 f(x) = (x-2)²
f(x) = (x[1] - 2.0)^2
result = optimize(f, [0.0], BFGS())
println(result.minimizer) # [2.0]
println(result.minimum) # ≈ 0.0
| 方法 | 函数 | 需要梯度 | 需要 Hessian | 适用场景 |
|---|---|---|---|---|
| Nelder-Mead | NelderMead() | 否 | 否 | 无梯度、噪声目标 |
| Gradient Descent | GradientDescent() | 是 | 否 | 简单问题 |
| BFGS | BFGS() | 是 | 否 | 通用无约束优化(推荐) |
| L-BFGS | LBFGS() | 是 | 否 | 高维问题、内存有限 |
| Newton | Newton() | 是 | 是 | 快速二次收敛 |
| Conjugate Gradient | ConjugateGradient() | 是 | 否 | 大规模问题 |
| Particle Swarm | ParticleSwarm() | 否 | 否 | 全局优化 |
2. 无约束优化
2.1 BFGS 方法
using Optim
# Rosenbrock 函数(经典测试函数)
function rosenbrock(x)
return (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2
end
x0 = [-1.0, 1.0]
result = optimize(rosenbrock, x0, BFGS(); autodiff=:forward)
println("最小值点: $(result.minimizer)")
println("最小值: $(result.minimum)")
println("迭代次数: $(result.iterations)")
println("收敛状态: $(result.converged)")
2.2 Nelder-Mead(无梯度)
# 不需要梯度的场景
result = optimize(rosenbrock, x0, NelderMead())
# 适用于目标函数不可微、含有噪声的场景
noisy_func(x) = (x[1] - 3.0)^2 + randn() * 0.01
result = optimize(noisy_func, [0.0], NelderMead())
2.3 自定义梯度
function rosenbrock(x)
return (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2
end
function rosenbrock_grad!(g, x)
g[1] = -2.0 * (1.0 - x[1]) - 400.0 * x[1] * (x[2] - x[1]^2)
g[2] = 200.0 * (x[2] - x[1]^2)
end
# 手动提供梯度
result = optimize(rosenbrock, rosenbrock_grad!, x0, BFGS())
# 或使用自动微分(推荐)
result = optimize(rosenbrock, x0, BFGS(); autodiff=:forward)
⚠️ 注意: autodiff=:forward 使用 ForwardDiff.jl 进行自动微分,适用于标量返回值的函数。对于返回向量的函数需要手动提供 Jacobian。
3. 有约束优化
3.1 盒约束(Box Constraints)
using Optim
# 变量范围约束
f(x) = (x[1] - 1.0)^2 + (x[2] - 2.5)^2
# x[1] ∈ [0, 2], x[2] ∈ [0, 4]
lower = [0.0, 0.0]
upper = [2.0, 4.0]
x0 = [0.5, 0.5]
result = optimize(f, lower, upper, x0, Fminbox(GradientDescent()))
println("最优解: $(result.minimizer)")
3.2 非线性约束
using Optim
# 最小化 f(x,y) = x + y
# 约束: x² + y² ≤ 1, x ≥ 0
f(x) = x[1] + x[2]
# 使用 IPNewton(内点法)
function con_c!(c, x)
c[1] = x[1]^2 + x[2]^2 - 1.0 # 不等式约束 ≤ 0
end
lx = [0.0, -Inf] # 变量下界
ux = [Inf, Inf] # 变量上界
df = TwiceDifferentiable(f, [0.5, 0.5]; autodiff=:forward)
dc = TwiceDifferentiableConstraints(con_c!, lx, ux, [0.0], [0.0])
result = optimize(df, dc, [0.5, 0.5], IPNewton())
4. 收敛判据与选项
result = optimize(f, x0, BFGS();
autodiff = :forward,
iterations = 1000, # 最大迭代次数
f_tol = 1e-12, # 函数值容差
g_tol = 1e-8, # 梯度容差
x_tol = 1e-12, # 自变量容差
store_trace = true, # 保存迭代历史
show_trace = true, # 打印迭代过程
show_every = 10 # 每 10 步打印一次
)
# 检查结果
println("收敛: $(result.converged)")
println("迭代次数: $(result.iterations)")
println("函数调用次数: $(result.f_calls)")
| 参数 | 说明 | 默认值 |
|---|---|---|
iterations | 最大迭代次数 | 1000 |
f_tol | 函数值相对变化容差 | √eps |
g_tol | 梯度范数容差 | 1e-8 |
x_tol | 自变量变化容差 | √eps |
time_limit | 时间限制(秒) | Inf |
5. 行搜索与信赖域
using Optim, LineSearches
# 自定义行搜索策略
result = optimize(f, x0, BFGS();
linesearch = LineSearches.HagerZhang(), # Hager-Zhang 行搜索
autodiff = :forward
)
# 其他行搜索方法
# LineSearches.BackTracking() # 回溯法
# LineSearches.StrongWolfe() # 强 Wolfe 条件
# LineSearches.MoreThuente() # More-Thuente 方法
💡 提示: 对于大多数问题,默认的 HagerZhang 行搜索已经足够好。如果优化不收敛,可以尝试更换行搜索策略。
6. 多起点全局优化
using Optim
# 单一局部优化可能陷入局部最优
# 使用多起点策略提高找到全局最优的概率
function multi_start(f, bounds, n_starts=100; method=BFGS())
best_result = nothing
best_val = Inf
for _ in 1:n_starts
x0 = [bounds[i][1] + rand() * (bounds[i][2] - bounds[i][1])
for i in 1:length(bounds)]
try
result = optimize(f, x0, method; autodiff=:forward)
if result.minimum < best_val
best_val = result.minimum
best_result = result
end
catch
continue
end
end
return best_result
end
# 使用示例
bounds = [(-5.0, 5.0), (-5.0, 5.0)]
result = multi_start(rosenbrock, bounds)
7. 凸优化(Convex.jl)
using Convex, SCS
# 最小化 ||Ax - b||² + λ||x||₁(LASSO 回归)
n, m = 100, 50
A = randn(n, m)
x_true = sprandn(m, 1, 0.3) # 稀疏真实解
b = A * x_true + randn(n) * 0.1
x = Variable(m)
λ = 0.1
problem = minimize(sumsquares(A * x - b) + λ * norm(x, 1))
solve!(problem, SCS.Optimizer; silent=true)
println("最优值: $(problem.optval)")
println("解: $(x.value)")
常用凸优化原子
| 原子 | 说明 |
|---|---|
minimize / maximize | 最小化/最大化目标 |
sumsquares(x) | ‖x‖²₂ |
norm(x, p) | Lp 范数 |
quadform(x, P) | xᵀPx |
logsumexp(x) | log(Σeˣⁱ) |
logisticloss(x) | logistic 损失 |
x >= 0 | 逐元素非负约束 |
sum(x) == 1 | 和为 1 的约束 |
8. 非线性最小二乘
using Optim, LsqFit
# 拟合模型 y = a * exp(b * x)
model(x, p) = p[1] .* exp.(p[2] .* x)
# 生成数据
xdata = range(0, 5, length=50)
ydata = 2.5 .* exp.(-0.3 .* xdata) .+ randn(50) * 0.1
# 使用 LsqFit.jl
p0 = [1.0, -1.0] # 初始猜测
fit = curve_fit(model, xdata, ydata, p0)
println("拟合参数: $(fit.param)")
println("标准误: $(stderror(fit))")
# 使用 Optim.jl 的最小二乘
function least_squares(p)
pred = model(xdata, p)
return sum((pred - ydata) .^ 2)
end
```julia
result = optimize(least_squares, p0, LevenbergMarquardt())
💡 提示: 对于曲线拟合问题,LsqFit.jl 的 curve_fit 通常比手动构建优化问题更方便,它自动计算 Jacobian 并返回拟合标准误。
9. 实际案例
9.1 参数拟合:指数衰减模型
using Optim, Plots
# 实验数据:放射性衰减
t_data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]
N_data = [100.0, 74.1, 54.9, 40.7, 30.1, 22.3, 16.5, 12.2, 9.1]
# 模型: N(t) = N₀ * exp(-λt)
decay_model(t, p) = p[1] .* exp.(-p[2] .* t)
function loss(p)
pred = decay_model(t_data, p)
return sum((pred - N_data) .^ 2)
end
result = optimize(loss, [100.0, 0.3], BFGS(); autodiff=:forward)
N0, λ = result.minimizer
println("N₀ = $N0, λ = $λ, 半衰期 = $(log(2)/λ)")
# 绘图
t_plot = range(0, 10, length=100)
scatter(t_data, N_data, label="实验数据")
plot!(t_plot, decay_model(t_plot, [N0, λ]), label="拟合曲线")
9.2 机器学习损失优化
using Optim
# 逻辑回归的梯度下降
sigmoid(z) = 1.0 / (1.0 + exp(-z))
function logistic_loss(θ, X, y)
z = X * θ
probs = sigmoid.(z)
# 防止 log(0)
probs = clamp.(probs, 1e-10, 1 - 1e-10)
return -sum(y .* log.(probs) .+ (1 .- y) .* log.(1 .- probs))
end
# 生成二分类数据
n, d = 200, 3
X = hcat(ones(n), randn(n, d - 1))
θ_true = [0.5, -1.0, 2.0]
y = sigmoid.(X * θ_true) .> 0.5
y = Float64.(y)
# 优化
loss(θ) = logistic_loss(θ, X, y)
result = optimize(loss, zeros(d), BFGS(); autodiff=:forward)
println("真实参数: $θ_true")
println("估计参数: $(result.minimizer)")
10. 优化算法选择指南
graph TD
A[优化问题] --> B{目标函数可微?}
B -->|是| C{维度?}
B -->|否| D[Nelder-Mead / ParticleSwarm]
C -->|低维 <100| E[BFGS / Newton]
C -->|高维 >100| F[L-BFGS / ConjugateGradient]
A --> G{有约束?}
G -->|盒约束| H[Fminbox + BFGS]
G -->|非线性约束| I[IPNewton]
G -->|凸优化| J[Convex.jl + SCS]
扩展阅读
- Optim.jl 官方文档
- Convex.jl 文档
- LsqFit.jl 文档
- Nocedal & Wright, Numerical Optimization
- Boyd & Vandenberghe, Convex Optimization
- JuliaOpt — 优化包集合