共计 2947 个字符,预计需要花费 8 分钟才能阅读完成。
问题背景
多项式回归是机器学习中常用的建模方法,特别适合描述非线性关系的数据。比如预测房价与面积的关系、销售额与广告投入的关系等,这些场景下简单的线性回归可能无法很好地拟合数据。

但多项式回归有个致命弱点:容易过拟合。过拟合是指模型在训练集上表现很好,但在新数据上表现糟糕的现象。我们通常可以通过观察训练集和验证集的表现差异来检测过拟合——如果训练集误差很低但验证集误差很高,基本可以确定发生了过拟合。
常规的训练方式是把所有数据一次性用于训练,然后用另一部分数据测试。而交叉验证(特别是 k 折交叉验证)则把数据分成 k 份,轮流用 k - 1 份训练,1 份验证,最后取平均值。这种方法能更好地评估模型的泛化能力。
技术实现
1. 环境准备
首先需要安装 MathNet.Numerics 库:
Install-Package MathNet.Numerics
2. 数据标准化
多项式拟合对数据尺度敏感,建议先做标准化:
public static Tuple<double[], double[]> Standardize(double[] x, double[] y)
{var meanX = x.Average();
var stdX = Math.Sqrt(x.Select(v => Math.Pow(v - meanX, 2)).Average());
var meanY = y.Average();
var stdY = Math.Sqrt(y.Select(v => Math.Pow(v - meanY, 2)).Average());
return Tuple.Create(x.Select(v => (v - meanX) / stdX).ToArray(),
y.Select(v => (v - meanY) / stdY).ToArray());
}
3. 多项式特征生成
public static double[][] GeneratePolynomialFeatures(double[] x, int degree)
{
return x.Select(v =>
Enumerable.Range(0, degree + 1)
.Select(p => Math.Pow(v, p))
.ToArray())
.ToArray();}
4. 最小二乘法拟合
public static double[] FitPolynomial(double[][] X, double[] y)
{var matrixX = Matrix<double>.Build.DenseOfRowArrays(X);
var vectorY = Vector<double>.Build.Dense(y);
// 使用伪逆求解最小二乘问题
return matrixX.PseudoInverse() * vectorY;}
5. k 折交叉验证实现
public static double[] CrossValidate(double[] x, double[] y,
int degree, int folds = 5)
{var indices = Enumerable.Range(0, x.Length).ToArray();
var rng = new Random();
indices = indices.OrderBy(i => rng.Next()).ToArray();
var foldSize = x.Length / folds;
var coefficients = new List<double[]>();
for (int i = 0; i < folds; i++)
{
// 划分训练集和验证集
var valIndices = indices.Skip(i * foldSize).Take(foldSize);
var trainIndices = indices.Except(valIndices);
var trainX = trainIndices.Select(idx => x[idx]).ToArray();
var trainY = trainIndices.Select(idx => y[idx]).ToArray();
var X = GeneratePolynomialFeatures(trainX, degree);
coefficients.Add(FitPolynomial(X, trainY));
}
// 取各折系数的平均值
return Enumerable.Range(0, degree + 1)
.Select(i => coefficients.Select(c => c[i]).Average())
.ToArray();}
评估优化
1. 误差对比
建议绘制训练集和验证集的 MSE 随多项式阶数变化的曲线:
var degrees = Enumerable.Range(1, 10);
var trainErrors = new List<double>();
var valErrors = new List<double>();
foreach (var degree in degrees)
{var coeffs = CrossValidate(x, y, degree);
// 计算训练集误差
var trainX = GeneratePolynomialFeatures(x, degree);
var predictedY = trainX.Select(f => coeffs.Zip(f, (c, v) => c * v).Sum()).ToArray();
var trainMse = y.Zip(predictedY, (a, b) => Math.Pow(a - b, 2)).Average();
// 计算验证集误差(使用交叉验证中的验证集)// ...
trainErrors.Add(trainMse);
valErrors.Add(valMse);
}
2. 多项式阶数选择
通常选择验证误差最小的阶数。实践中,当验证误差开始上升时就应该停止增加阶数。
3. 正则化补充
对于高阶多项式,建议加入 L2 正则化(岭回归):
public static double[] FitPolynomialWithRegularization(double[][] X, double[] y, double lambda)
{var matrixX = Matrix<double>.Build.DenseOfRowArrays(X);
var vectorY = Vector<double>.Build.Dense(y);
var identity = Matrix<double>.Build.DenseIdentity(X[0].Length);
return (matrixX.Transpose() * matrixX + lambda * identity).Inverse()
* matrixX.Transpose() * vectorY;}
生产建议
- 内存优化:对于大数据集,建议分批处理数据而不是一次性加载
- 性能陷阱:多项式阶数超过 20 时计算可能不稳定,建议限制最大阶数
- 监控指标:生产环境建议记录:
- 模型拟合时间
- 预测延迟
- 特征重要性
- 实时预测误差
延伸思考
当特征维度 >1000 时,可以考虑:
1. 使用主成分分析 (PCA) 降维
2. 改用随机森林等非线性模型
3. 使用梯度下降优化替代直接矩阵求逆
拓展阅读:
– ML.NET 官方文档
– MathNet.Numerics 文档
希望这篇指南能帮助你在 C# 中实现稳健的多项式回归模型。记住,模型复杂度不是越高越好,找到那个 ” 刚刚好 ” 的平衡点才是关键。
正文完
