共计 1913 个字符,预计需要花费 5 分钟才能阅读完成。
问题背景
多项式回归在工程领域应用广泛,比如处理传感器采集的非线性数据(温度 - 电阻特性曲线)、金融领域的趋势预测(股票价格波动)等场景。当数据呈现明显非线性特征时,简单线性回归无法满足精度需求,此时多项式回归能通过曲线拟合更好地描述数据关系。

但多项式回归存在一个典型陷阱:随着多项式阶数升高,模型会越来越贴合训练数据,甚至完美穿过每一个训练点。这看起来很美,实际却是过拟合——模型在训练集上误差极低,但在新数据(测试集)上表现急剧恶化。比如:
- 5 阶多项式在训练集上 R²=0.99,测试集仅 0.65
- 模型对噪声数据点过度敏感,预测曲线出现不合理震荡
技术方案
传统最小二乘法直接最小化训练误差,而带交叉验证的方案通过以下步骤控制过拟合:
- 数据分割:将原始数据分为 k 个子集(通常 k = 5 或 10)
- 轮换验证:每次用 k - 1 个子集训练,剩余 1 个验证,重复 k 次
- 误差评估:综合 k 次验证结果计算平均误差
MathNet.Numerics 库提供两大核心组件:
Polynomial.Fit:执行最小二乘多项式拟合CrossValidated:实现 k 折交叉验证框架
代码实现
数据生成与预处理
首先生成带噪声的模拟数据(正弦曲线 + 高斯噪声):
// 生成 100 个 0 -10 之间的均匀分布 x 值
var x = Generate.LinearRange(0, 10, 100);
// 真实模型:y=sin(x) + 噪声
var rng = new Random(42);
var y = x.Select(xi => Math.Sin(xi) + rng.NextDouble()*0.5).ToArray();
交叉验证实现
使用 5 折交叉验证评估 3 阶多项式模型:
// 定义评估函数 (均方误差)
double Metric(double[] actual, double[] predicted)
{return Distance.MSE(actual, predicted);
}
// 执行交叉验证
var cv = new CrossValidated<double[]>(k: 5);
var results = cv.Evaluate(samples: x.Zip(y, (xi, yi) => new[] { xi, yi}).ToArray(),
fit: data =>
{
var coeff = Polynomial.Fit(x: data.Select(d => d[0]).ToArray(),
y: data.Select(d => d[1]).ToArray(),
order: 3); // 3 阶多项式
return coeff;
},
predict: (model, test) =>
{return test.Select(t => Polynomial.Evaluate(t[0], model)).ToArray();},
metric: Metric
);
Console.WriteLine($"CV 平均误差: {results.AverageError}");
阶数选择策略
通过交叉验证寻找最优阶数(示例测试 1 - 6 阶):
var orders = Enumerable.Range(1, 6);
var cvErrors = new List<double>();
foreach(var order in orders)
{
// 同上交叉验证流程,调整 order 参数
cvErrors.Add(/* 获取当前阶数的 CV 误差 */);
}
// 选择误差最小的阶数
var bestOrder = orders.ElementAt(cvErrors.IndexOf(cvErrors.Min()));
生产建议
过拟合可视化
| 阶数 | 训练误差 | 验证误差 | 曲线形态 |
|---|---|---|---|
| 1 | 0.82 | 0.85 | 直线 |
| 3 | 0.12 | 0.15 | 平滑曲线 |
| 6 | 0.01 | 0.47 | 剧烈震荡 |
性能优化
- 内存管理 :对于超过 10,000 数据点,改用
Polynomial.Fit(x,y,order,DirectRegressionMethod.QR)避免直接求逆 - 并行计算 :使用
Task.WhenAll并行执行不同阶数的交叉验证
常见问题
- 矩阵奇异值错误:当阶数过高时,添加 L2 正则化
Polynomial.Fit(..., method: DirectRegressionMethod.Svd); - 数值不稳定:对 x 值进行归一化处理
var xScaled = x.Select(xi => (xi - xMean)/xStd).ToArray();
延伸思考
- 当数据采样不均匀(如 80% 数据集中在某区间)时,如何调整交叉验证策略?
- 对于周期性数据(如昼夜温度变化),多项式回归是否是最佳选择?
- 如何将本方案封装成可供 ASP.NET Core 调用的预测服务?
通过交叉验证机制,我们可以在保持模型简单性的同时获得可靠的泛化能力。实际项目中建议将最佳模型参数持久化,避免每次请求重复训练。
正文完
发表至: 未分类
近两天内
