通过缩小误差来拟合曲线
很多模型是带少数参数的曲线,拟合就是选出使曲线贴近数据的参数。scipy.optimize.least_squares(fun, x0) 做的就是这件事。你写 fun(p),它返回残差向量,每个数据点一个,残差是模型值减去数据值。函数随后最小化代价 21∑ri2。它从你的猜测 x0 出发,借助雅可比矩阵 J(每个参数变化时每个残差如何变化的表)前进。高斯–牛顿的一步解 J⊤Jδ=−J⊤r,再把参数移动 δ。对参数是线性的模型,一步就能到达答案。bounds=(下界, 上界) 把参数保持在合理范围。curve_fit 是更友好的包装,直接接收模型 f(x,参数)。
平方误差会让一个离群点占主导:残差 10 算作 100。稳健损失对大残差增长得更慢。取 loss='soft_l1' 和 f_scale=1,惩罚为 ρ(z)=2(1+z−1),其中 z 是残差的平方。拟合收益率曲线是经典用法。纳尔逊–西格尔曲线是 y(τ)=β0+β1τ/λ1−e−τ/λ+β2(τ/λ1−e−τ/λ−e−τ/λ)。它有长期水平 β0、斜率 β1、驼峰 β2 和以年计的衰减长度 λ,四个数就能描述整条曲线。拟合之后,噪声水平用 s=SSE/(n−p) 估计,其中 n 是点数,p 是参数个数。
δ=−(J⊤J)−1J⊤r,cost=21∑ri2,ρ(z)=2(1+z−1),s=n−pSSE(1) 过原点的直线用
y=ax 拟合点
(1,2.1)、
(2,3.9)、
(3,6.2),从
a=1 出发。残差
r=ax−y=(−1.1,−1.9,−3.2),雅可比
J=x=(1,2,3)。于是
J⊤r=−14.5,
J⊤J=14,
δ=14.5/14=1.036,新值
a=2.036。因为模型对
a 是线性的,这就已经是答案了。答案处的残差为
0.064、
−0.171、
0.093,代价为
0.0211。残差
10 用平方算作
100,用
soft_l1 只有
ρ=18.10。在真实数据上,用
least_squares 对 2026 年 10 月 9 日十个美债收益率(3 个月到 30 年)拟合纳尔逊–西格尔曲线,水平
5.75%,斜率
−1.52(曲线随期限上升),
λ=2.7 年,与报价收益率平均只差
7.3 个基点,最差
13 个基点。用
4 个参数拟合
10 个点、
SSE=0.54,自由度为
n−p=6,
s=0.54/6=0.30。
- 输入残差,输出参数:
fun(p) 返回模型减数据,least_squares 缩小平方和。 - 用稳健损失对付离群点:
soft_l1 防止一个离群点拖偏整条曲线。 - 四个数描述一条曲线:纳尔逊–西格尔用水平、斜率、驼峰和衰减长度概括收益率曲线。
实验:评分内容
- 过原点的斜率10 分
- 高斯–牛顿一步15 分
- 稳健惩罚15 分
- 拟合后的噪声水平10 分
- least_squares 的参数10 分
- 曲线拟合的流程10 分
- 为什么用稳健损失30 分