使用最小二乘法进行曲线逼近
本例演示了如何使用面向问题的优化 工作流程执行非线性曲线逼近(近似)问题。
我们将使用JuMP.jl库的函数来提出优化问题,非线性优化库Ipopt.jl、随机数生成库Random.jl、用于处理概率分布的库Distributions.jl和库Plots.jl来可视化结果。
安装库
如果您的环境中没有安装最新版本的JuMP
软件包,请取消注释并运行下面的方框:
Pkg.add(["Ipopt", "Distributions", "JuMP"])
#Pkg.add("JuMP");
要在安装完成后启动新版本的程序库,请单击 "我的账户 "按钮:


按下 "Start Engee "按钮重新启动会话:

任务描述
优化问题是对一条曲线进行非线性最小二乘逼近。
该任务的模型依赖方程:
其中, 、 、 和 为未知参数, 为响应,t 为时间。
问题的目标是找到能使目标函数最小化的值 和 :
连接图书馆
连接图书馆JuMP
:
using JuMP;
连接非线性求解器库Ipopt
:
using Ipopt;
连接随机数生成库Random
:
using Random;
连接用于处理概率分布的库Distributions
:
using Distributions;
连接图形库Plots
:
using Plots;
数据生成
在这项任务中,我们将生成人工噪声数据。
指定随机数发生器的任意粒度:
Random.seed!(0);
创建包含模型参数真实值的变量A_true
和r_true
。这些变量用于生成人工数据ydata
。
使用,, 和 作为真值:
A_true = [1, 2];
r_true = [-1, -3];
A_true
和r_true
变量是优化算法应尽量还原的基准值。将优化结果与真实值进行比较,可以评估曲线逼近过程的准确性和效率。
生成 200 个从 0 到 3 的tdata
随机值作为时间数据。同时创建一个包含噪声的变量noisedata
,我们将把它添加到最终数据ydata
中。
tdata = sort(3 * rand(200));
noisedata = 0.05 * randn(length(tdata));
ydata = A_true[1] * exp.(r_true[1] * tdata) + A_true[2] * exp.(r_true[2] * tdata) + noisedata;
将生成的点绘制在图表上:
plot(tdata, ydata, seriestype=:scatter, color=:red, legend=:right, label="Исходные данные")
xlabel!("t")
ylabel!("Отклик")
从图中可以看出,数据中含有噪声。因此,问题的解很可能与真实参数 和 不完全一致。
创建优化问题
使用函数Model()
创建一个优化问题,并在括号中指定求解器的名称:
model = Model(Ipopt.Optimizer)
创建变量 和 ,每个变量包含两个值, -,, 和 :
@variable(model, A[1:2]);
@variable(model, r[1:2]);
创建一个最小化平方和的非线性目标函数:
@NLobjective(model, Min, sum((A[1]*exp(r[1]*t) + A[2]*exp(r[2]*t) - y)^2 for (t,y) in zip(tdata, ydata)));
设置适当的初始值是高效、准确地解决优化问题的重要步骤,对优化过程的速度、质量和可靠性有重大影响。
指定初始值,, 和 。
set_start_value.(A, [1/2, 3/2]);
set_start_value.(r, [-1/2, -3/2]);
输出初始值 和 :
println("Начальные значения A: ", start_value.(A))
println("Начальные значения r: ", start_value.(r))
问题的解决方案
解决优化问题:
optimize!(model)
将 和 的值存储在变量中:
A_sol = value.(A);
r_sol = value.(r);
输出优化结果
println("Оптимизированные значения A: ", A_sol)
println("Оптимизированные значения r: ", r_sol)
可视化并分析结果
计算曲线近似的结果:
fitted_response = A_sol[1] * exp.(r_sol[1] * tdata) + A_sol[2] * exp.(r_sol[2] * tdata);
将结果绘制在图表上:
plot!(tdata, fitted_response, color=:blue, label="Приближённая кривая")
title!("Приближённый отклик")
您可以将获得的结果与最初设置的值进行比较,, 和 。四舍五入功能可以对过于精确的数值进行四舍五入。
将 的结果与原始值进行比较:
percent_diffs = ((A_sol .- A_true) ./ A_true) .* 100
avg_percent_diff = mean(percent_diffs)
println("Процентная разница: ", round.(percent_diffs, digits=2))
println("Средняя процентная разница: ", round.(avg_percent_diff, digits=2))
将 的结果与原始值进行比较:
percent_diffs = ((r_sol .- r_true) ./ r_true) .* 100
avg_percent_diff = mean(percent_diffs)
println("Процентная разница: ", round.(percent_diffs, digits=2))
println("Средняя процентная разница: ", round.(avg_percent_diff, digits=2))
因此, 的结果与原始值平均相差 ,而 的结果与原始值平均相差 。
结论
在本例中,我们采用以问题为导向的方法,使用最小二乘法 (LSM) 解决了一个非线性曲线逼近问题。我们还将结果可视化,并与真实的模型参数进行了比较。