传感器标定必看!.NET8 手写多项式拟合,解决高阶病态过拟合

做工控 / 传感器标定经常需要曲线拟合,调用第三方库体积大、单片机移植受限;高阶多项式还容易矩阵奇异、曲线震荡。本文纯手写 C# 多项式拟合,完整数学推导 + 可直接复制工程,解决嵌入式轻量化拟合、过拟合判断两大工程痛点。

基本概念


多项式定义

m次多项式模型的一般形式为:

其中:

  • 多项式阶数m :决定多项式曲线的复杂度
    • m=1时为线性拟合:
    • m=2时为二次拟合:
    • m=3时为三次拟合,以此类推
  • 拟合系数a₀,a₁,...,aₘ:需要通过数据估计的参数
  • 拟合目标:通过最小化残差平方损失函数,找到使模型预测值与实际观测值差异最小化的系数组合

残差与最小二乘准则

对于N个样本点,i=0,1,...,N-1:

  • 预测值
  • 残差,反映预测误差
  • 损失函数 (残差平方和):

最小二乘法原理 :通过令损失函数S对各系数a_j的偏导数为零,建立方程组求解最优系数,实质上是寻找损失函数的极小值点。

正规方程组

通过求导整理得到的(m+1)阶线性方程组(正规方程):

矩阵形式表示为:

其中:

  • 设计矩阵X :N×(m+1)矩阵,元素 例:二次多项式(m=2)的设计矩阵为
  • :设计矩阵的转置
  • 系数向量a
  • 观测值向量y

关键指标

SSE(残差平方和)

  • 反映预测值与实际值的总差异
  • 值越小表示拟合效果越好
  • 比较时需考虑样本量N的影响

R²决定系数

其中(总平方和),为y的均值

  • 取值范围0,1
  • 表示模型解释的数据变异比例
  • 例:R²=0.85表示模型解释了85%的数据变异

过拟合与欠拟合

过拟合
  • 特征:多项式阶数m过大
  • 表现:
    • 模型过于复杂,拟合了训练数据中的噪声
    • 训练误差小但测试误差大
    • 泛化能力差
  • 示例:用10次多项式拟合仅有5个数据点的数据集
欠拟合
  • 特征:多项式阶数m过小
  • 表现:
    • 模型过于简单,无法捕捉数据规律
    • 训练误差和测试误差均较大
  • 示例:用线性模型拟合具有明显二次趋势的数据

模型选择建议:通过交叉验证等方法选择适当的m值,在欠拟合和过拟合之间取得平衡。通常可观察SSE和R²随m增加的变化趋势,选择改进不再显著的m值。

历史背景


1805年的重要突破

法国数学家阿德里安-马里·勒让德(Adrien-Marie Legendre)在1805年研究天体运动轨道时,发现天文观测数据存在显著误差。为此,他提出了最小二乘法(Least Squares Method),通过最小化误差平方和来寻找最优拟合曲线。这一方法首次系统性地解决了带噪声观测数据的数学处理问题,并发表在其著作《确定彗星轨道的新方法》中。勒让德的具体应用包括:

  • 利用离散观测点确定彗星椭圆轨道参数
  • 校正望远镜观测中由大气折射引起的系统性误差

1809年的理论奠基

德国数学家卡尔·弗里德里希·高斯(Carl Friedrich Gauss)在1809年发表的《天体运动论》中,从概率统计角度完善了最小二乘法理论。他的核心贡献包括:

  • 证明当观测噪声服从正态分布时,最小二乘估计等价于最大似然估计
  • 推导出正规方程组(Normal Equations)的解析解
  • 建立误差分布理论,证实最小二乘解具有最小方差特性
    高斯成功应用这一理论预测了谷神星轨道,充分展现了其实用价值。

19世纪中期的方法普及

随着实验科学的进步,最小二乘多项式拟合逐渐成为多领域的标准工具:

  • 物理学:光谱线波长测量、热膨胀系数测定
  • 大地测量:三角网平差计算、地图投影变换
  • 工程学 :桥梁应力测试数据校准
    典型应用场景:
  • 二次多项式拟合弹道轨迹
  • 三次多项式校正温度传感器非线性误差
  • 天文航海中的星历表编制

20世纪的计算革命

计算机的诞生极大推动了数值算法的发展:

  • 高斯消元法改进:引入选主元技术(Partial Pivoting)提升稳定性
  • 矩阵分解技术:LU分解将运算复杂度降至O(n²)级
  • 迭代算法 :针对超大规模方程组的共轭梯度法
    重要里程碑:
  • 1947年冯·诺伊曼实现首个矩阵求逆电子计算
  • 1965年LINPACK线性代数库发布
  • 1980年代MATLAB集成多项式拟合工具箱

现代发展与衍生技术

当前多项式拟合技术主要朝两个方向发展:

基础应用领域

  • 工业传感器特性曲线标定(如压力-电压转换)
  • CNC机床运动轨迹平滑插值
  • 金融时间序列短期趋势预测
  • 医学影像灰度校正

改进算法

  • 正则化方法(岭回归)抑制高阶多项式过拟合
  • 鲁棒最小二乘(Huber损失函数)增强抗异常值能力
  • 稀疏多项式选择(LASSO回归)实现特征筛选

典型代码实现:

cs 复制代码
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Trainers;

// 创建ML上下文
var mlContext = new MLContext();

// 准备数据
var data = mlContext.Data.LoadFromEnumerable(trainingData);

// 创建数据处理管道
var pipeline = mlContext.Transforms.Concatenate("Features", featureColumns)
    .Append(mlContext.Transforms.NormalizeMinMax("Features"))
    .Append(mlContext.Transforms.Concatenate("PolynomialFeatures", 
        new PolynomialFeaturizingEstimator.Options
        {
            Degree = 5,
            OutputColumnName = "PolynomialFeatures",
            InputColumnName = "Features"
        }))
    .Append(mlContext.Regression.Trainers.LbfgsPoissonRegression(
        new LbfgsPoissonRegressionTrainer.Options
        {
            L2Regularization = 1.0f,
            FeatureColumnName = "PolynomialFeatures",
            LabelColumnName = "Label"
        }));

// 训练模型
var model = pipeline.Fit(data);

核心原理详解


损失函数极值推导

数学推导过程

最小二乘法的损失函数定义为:

对任意系数 求偏导(其中 ):

推导步骤

  • 消去常数项 (-2)
  • 展开求和项:
  • 形成正规方程组:共 个方程,对应 个未知系数

物理意义

该推导确保损失函数在系数空间达到极小值,此时模型预测值与实际值的平方误差最小。

矩阵构造逻辑

设样本数量为 ,多项式阶数为

设计矩阵构造

  • 维度
  • 元素定义 :第 ( i ) 行第 ( k ) 列元素为 从 0 到
  • 示例 :对于二次多项式,第 行为

矩阵运算

  • :得到的对称方阵
  • :得到长度为的列向量
  • 关键性质是对称正定矩阵(在样本点不共线时)
  • 方程求解

    解向量 即为最优多项式系数

线性方程组求解:高斯列主元消元

增广矩阵构造

将 ( A ) 和 ( B ) 合并为矩阵,最后一列存储向量的值。

列主元消元过程

cs 复制代码
// 假设 matrix 是一个二维 double 数组
int m = matrix.GetLength(0); // 行数
int n = matrix.GetLength(1); // 列数

for (int col = 0; col < m; col++)
{
    // 找主元行
    int pivot_row = col;
    double max_val = Math.Abs(matrix[col, col]);
    
    for (int i = col + 1; i < m; i++)
    {
        double current_val = Math.Abs(matrix[i, col]);
        if (current_val > max_val)
        {
            max_val = current_val;
            pivot_row = i;
        }
    }

    // 交换当前行和主元行
    if (pivot_row != col)
    {
        for (int j = 0; j < n; j++)
        {
            double temp = matrix[col, j];
            matrix[col, j] = matrix[pivot_row, j];
            matrix[pivot_row, j] = temp;
        }
    }

    // 归一化
    double pivot = matrix[col, col];
    if (pivot == 0) continue; // 避免除以零
    
    for (int j = 0; j < n; j++)
    {
        matrix[col, j] /= pivot;
    }

    // 消元
    for (int row = col + 1; row < m; row++)
    {
        double factor = matrix[row, col];
        for (int j = 0; j < n; j++)
        {
            matrix[row, j] -= factor * matrix[col, j];
        }
    }
}

回代求解

cs 复制代码
for (int i = m; i >= 0; i--)
{
    a[i] = matrix[i, m + 1];
    for (int j = i + 1; j <= m; j++)
    {
        a[i] -= matrix[i, j] * a[j];
    }
}

数值稳定性措施

  • 主元阈值检查 :设定 ,若主元绝对值小于则判定为奇异矩阵
  • 动态行交换:每次消元前确保当前列的最大值位于对角线

拟合效果评估原理

基础统计量

  • 样本均值:
  • 总波动:

模型误差

  • 残差平方和:,其中 是多项式模型预测值
  • 决定系数:
    • 取值范围:( 0,1 )
    • 物理意义:模型解释的方差比例
    • 示例 表示模型能解释 85% 的数据波动

过拟合检测

当多项式阶数 ( m ) 接近样本量 ( N ) 时:

  • ( R^2 ) 会人为增大(甚至达到 1)
  • 需要通过交叉验证等方法来验证模型泛化能力

计算复杂度分析

  • 矩阵构造:
  • 高斯消元:
  • 总体复杂度
  • 适用性建议 :当 时,建议改用 QR 分解等更稳定的算法

算法执行流程详解


输入校验阶段

本阶段对输入数据进行严格验证,确保后续计算的可靠性:

样本数量校验

  • 必须满足 N > m+1(N为样本数量,m为多项式阶数)
  • 示例:当m=3(三次多项式)时,至少需要5个样本点(N=5),否则抛出"样本不足"异常
  • 数学原理:若N ≤ m+1,正规方程组将出现秩亏,导致解不唯一

数据质量校验

  • 检查x坐标重复值(同一x对应不同y会导致方程冲突)
  • 检测空值/NaN/±∞等非法数值
  • 实现方法:对x数组排序后检查相邻元素差是否为零

构建正规方程组

通过双重循环计算正规方程组的矩阵和向量:

cs 复制代码
// 初始化 (m+1)×(m+1) 矩阵 A
double[,] A = new double[m + 1, m + 1];
for (int j = 0; j <= m; j++)
{
    for (int k = 0; k <= m; k++)
    {
        double sum = 0;
        for (int i = 0; i < N; i++)
        {
            sum += Math.Pow(x[i], j + k);
        }
        A[j, k] = sum;
    }
}

// 初始化长度为 m+1 的向量 B
double[] B = new double[m + 1];
for (int j = 0; j <= m; j++)
{
    double sum = 0;
    for (int i = 0; i < N; i++)
    {
        sum += y[i] * Math.Pow(x[i], j);
    }
    B[j] = sum;
}

数学本质:计算设计矩阵的XᵀX和XᵀY

高斯消元法求解

解线性方程组A·coeffs = B的关键步骤:

消元过程

  1. 对于第k列(k=0→m),找出k行以下绝对值最大的元素作为主元
  2. 交换当前行与主元所在行
  3. 用当前行消去下方行的第k列元素

回代求解

  1. 从最后一行开始逆向求解
  2. 每行解对应一个多项式系数aₖ

数值稳定性:列主元选取可有效避免除零和减少舍入误差

多项式预测实现

多项式计算ŷ = Σaₖxᵏ的实现:

python 复制代码
public double Predict(double x, double[] coeffs)
{
    double yPred = 0.0;
    for (int k = 0; k < coeffs.Length; k++)
    {
        yPred += coeffs[k] * Math.Pow(x, k);
    }
    return yPred;
}

优化方案:采用霍纳法则减少乘法运算:

python 复制代码
double y_pred = coeffs[m];
for (int k = m - 1; k >= 0; k--)
{
    y_pred = y_pred * x + coeffs[k];
}

拟合质量评估

计算三个核心指标:

  • 总平方和(SST):Σ(yᵢ - ȳ)²,反映数据总变异
  • 残差平方和(SSE):Σ(yᵢ - ŷᵢ)²,衡量拟合误差
  • 决定系数(R²):1 - SSE/SST,取值0,1,越接近1拟合效果越好

结果输出与异常处理

正常输出

  • 多项式表达式(如"y = 1.23 + 4.56x - 7.89x²")
  • 系数数组a₀, a₁,..., aₘ
  • 拟合指标:R²(保留4位小数)、SSE值

异常处理

  • 矩阵接近奇异时:
    • 抛出"MatrixSingularError"
    • 建议:降低多项式阶数或检查数据共线性
  • 数值溢出时提示"NumericalOverflow"

完整流程图

输入数据 → 设定阶数m → 数据校验 → 构建XᵀX和XᵀY → 高斯消元 → 求解系数 → 预测函数 → 计算R²/SSE → 输出拟合结果

算法性能分析


时间复杂度

多项式回归算法的时间复杂度主要取决于两个阶段:

构建正规方程组阶段

  • 计算复杂度为O(N·m²),其中N是样本量,m是多项式阶数
  • 主要耗时操作包括计算各幂次项的双重求和(从x⁰到x²ᵐ)
  • 示例:对于3阶多项式(m=3)和1000个样本(N=1000),需计算x⁰到x⁶的求和

高斯消元求解阶段

  • 求解(m+1)阶方阵的复杂度为O(m³)
  • 涉及(m+1)³/3次乘除法运算和(m+1)³/3次加减法运算

实际性能表现

  • 小样本量(N<100)和低阶多项式(m≤5):计算可在毫秒级完成
    • 例如:m=2,N=50时,现代CPU约需0.3ms
  • 高阶多项式(m>10):立方项m³导致计算量急剧增加
    • m=15时,仅消元阶段就需要约4000次运算
  • 大数据量(N>10000):Nm²项成为主要瓶颈
    • m=5,N=100000时,构建方程阶段需要约250万次运算

数值稳定性

多项式回归的数值稳定性与正规矩阵XᵀX的条件数密切相关:

低阶情况(m≤4)

  • 条件数通常较小(cond(XᵀX)<10⁴)
  • 高斯消元法能稳定求解,系数误差在10⁻⁶以内
  • 适合使用基本高斯消元法

高阶情况(m≥8)

  • 矩阵呈现高度病态性,条件数可达10¹⁰以上
  • 示例:当m=10时,微小扰动(如10⁻⁸)可能导致系数偏移超过100%
  • 常见问题:
    • 预测曲线剧烈振荡
    • 高阶项系数异常增大

稳定性优化方案

  • 数据归一化:将输入x线性映射到-1,1区间
    • 可降低条件数2~3个数量级
  • 降低多项式阶数:通过交叉验证选择最优m
  • 引入岭回归正则化:添加λI项改善矩阵条件
  • 采用列主元消元法:
    • 相比普通高斯消元,稳定性提升10~1000倍
    • 可有效处理条件数达10⁸的矩阵

空间复杂度

内存消耗主要来源

  • 正规矩阵存储:
    • 需要(m+1)×(m+1)的浮点数组
    • 占用空间:8(m+1)²字节(双精度浮点)
  • 样本数据存储:
    • 原始数据需O(N)空间
    • 典型实现需要存储x、y数组各N个元素
  • 总空间复杂度:O(N + m²)

示例

  • 当m=5,N=10000时:
    • 矩阵存储:8×6×6=288字节
    • 数据存储:8×10000×2=160KB
  • 大数据场景下(N>1M),可采用流式计算减少内存占用

收敛特性

多项式最小二乘拟合具有以下优良数学性质:

凸优化特性

  • 损失函数L(θ) = ||Xθ-y||²是严格凸函数
  • Hessian矩阵∇²L=2XᵀX正定(当X满秩时)
  • 保证仅存在唯一的全局最小值

解的唯一性

  • 当样本点足够多(N≥m+1)且分布合理时
  • 正规方程XᵀXθ=Xᵀy有唯一解
  • 高斯消元法必然收敛到最优参数θ*

与迭代法的比较

相比梯度下降等迭代算法,直接解法具有以下优势:

  • 无需考虑学习率选择
  • 无需设置迭代次数
  • 无需判定收敛条件
  • 计算确定性:相同输入总是得到相同输出

实践建议

当XᵀX接近奇异时,应检查:

  1. 样本量是否充足(N≥m+1)
  2. 输入特征是否线性相关
  3. 是否需要添加正则化项

完整代码


代码模块组成

  • 核心拟合类 - PolynomialFitter
  • 矩阵运算模块 - 高斯列主元消元法(实现原生矩阵求解)
  • 计算功能模块:
    • 多项式求值
    • 误差计算
    • R²指标计算
  • 测试程序:
    • 线性拟合演示
    • 二次拟合演示
    • 带噪声数据拟合演示
cs 复制代码
using System;
using System.Collections.Generic;

/// <summary>
/// 纯原生多项式最小二乘拟合工具类,无任何第三方数学库依赖
/// </summary>
public class PolynomialFitter
{
    private readonly double[] _xData;
    private readonly double[] _yData;
    private readonly int _sampleCount;

    /// <summary>
    /// 构造函数,载入观测数据
    /// </summary>
    /// <param name="x">自变量数组</param>
    /// <param name="y">因变量数组</param>
    public PolynomialFitter(double[] x, double[] y)
    {
        if (x == null || y == null || x.Length != y.Length)
            throw new ArgumentException("X与Y数组长度必须一致且不能为空");
        if (x.Length < 2)
            throw new ArgumentException("至少需要2组样本数据");

        _xData = x;
        _yData = y;
        _sampleCount = x.Length;
    }

    /// <summary>
    /// 执行m阶多项式拟合,返回系数数组 [a0,a1,a2...am]
    /// P(x) = a0 + a1*x + a2*x^2 + ... + am*x^m
    /// </summary>
    /// <param name="m">多项式阶数</param>
    /// <returns>拟合系数数组</returns>
    public double[] Fit(int m)
    {
        int eqCount = m + 1;
        // 校验样本数量:样本数必须大于方程数
        if (_sampleCount <= eqCount)
            throw new InvalidOperationException($"样本数量{_sampleCount}必须大于阶数+1({eqCount}),否则矩阵奇异");

        // 1. 构建正规方程组 A * a = B
        double[,] A = new double[eqCount, eqCount];
        double[] B = new double[eqCount];

        // 填充矩阵A和向量B
        for (int j = 0; j < eqCount; j++)
        {
            for (int i = 0; i < _sampleCount; i++)
            {
                double xi = _xData[i];
                double yi = _yData[i];
                double xj = Math.Pow(xi, j);
                B[j] += yi * xj;

                for (int k = 0; k < eqCount; k++)
                {
                    double xk = Math.Pow(xi, k);
                    A[j, k] += xj * xk;
                }
            }
        }

        // 2. 高斯列主元消元求解线性方程组
        double[] coeffs = GaussianElimination(A, B);
        return coeffs;
    }

    /// <summary>
    /// 高斯列主元消元法 求解 Ax = B
    /// 原生实现,无矩阵库依赖
    /// </summary>
    private double[] GaussianElimination(double[,] matA, double[] vecB)
    {
        int n = vecB.Length;
        double[,] aug = new double[n, n + 1];

        // 构建增广矩阵 [A|B]
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
                aug[i, j] = matA[i, j];
            aug[i, n] = vecB[i];
        }

        // 前向消元
        for (int col = 0; col < n; col++)
        {
            // 列主元选取:找到当前列绝对值最大行
            int pivotRow = col;
            double maxVal = Math.Abs(aug[col, col]);
            for (int r = col + 1; r < n; r++)
            {
                double val = Math.Abs(aug[r, col]);
                if (val > maxVal)
                {
                    maxVal = val;
                    pivotRow = r;
                }
            }

            // 交换主元行与当前行
            if (pivotRow != col)
            {
                for (int c = col; c <= n; c++)
                {
                    double temp = aug[col, c];
                    aug[col, c] = aug[pivotRow, c];
                    aug[pivotRow, c] = temp;
                }
            }

            // 判断奇异矩阵(主元接近0)
            double pivot = aug[col, col];
            if (Math.Abs(pivot) < 1e-12)
                throw new ArithmeticException("正规矩阵奇异,无法求解,请降低多项式阶数或增加样本");

            // 归一化主元行
            for (int c = col; c <= n; c++)
                aug[col, c] /= pivot;

            // 消去下方所有行
            for (int r = col + 1; r < n; r++)
            {
                double factor = aug[r, col];
                for (int c = col; c <= n; c++)
                    aug[r, c] -= factor * aug[col, c];
            }
        }

        // 回代求解
        double[] res = new double[n];
        for (int row = n - 1; row >= 0; row--)
        {
            double sum = aug[row, n];
            for (int c = row + 1; c < n; c++)
                sum -= aug[row, c] * res[c];
            res[row] = sum;
        }
        return res;
    }

    /// <summary>
    /// 根据系数计算多项式预测值 P(x)
    /// </summary>
    /// <param name="x">输入自变量</param>
    /// <param name="coeffs">拟合系数 [a0,a1,a2...am]</param>
    /// <returns>预测y值</returns>
    public static double CalcPolyValue(double x, double[] coeffs)
    {
        double y = 0;
        for (int k = 0; k < coeffs.Length; k++)
        {
            y += coeffs[k] * Math.Pow(x, k);
        }
        return y;
    }

    /// <summary>
    /// 计算拟合评价指标:SSE残差平方和、SST总平方和、R²决定系数
    /// </summary>
    /// <param name="coeffs">拟合系数</param>
    /// <returns>(SSE, SST, R2)</returns>
    public (double SSE, double SST, double R2) CalcFitMetrics(double[] coeffs)
    {
        double yMean = 0;
        foreach (var y in _yData) yMean += y;
        yMean /= _sampleCount;

        double sse = 0;
        double sst = 0;
        for (int i = 0; i < _sampleCount; i++)
        {
            double yPred = CalcPolyValue(_xData[i], coeffs);
            double e = _yData[i] - yPred;
            sse += e * e;

            double dy = _yData[i] - yMean;
            sst += dy * dy;
        }

        double r2 = sst < 1e-12 ? 1.0 : 1 - sse / sst;
        return (sse, sst, r2);
    }

    /// <summary>
    /// 格式化输出多项式表达式字符串
    /// </summary>
    public static string GetPolyExpression(double[] coeffs)
    {
        List<string> terms = new List<string>();
        int m = coeffs.Length - 1;
        for (int k = 0; k < coeffs.Length; k++)
        {
            double a = coeffs[k];
            if (Math.Abs(a) < 1e-10) continue;

            string term;
            int power = k;
            if (power == 0)
                term = $"{a:F4}";
            else if (power == 1)
                term = $"{a:F4}*x";
            else
                term = $"{a:F4}*x^{power}";

            terms.Add(term);
        }
        if (terms.Count == 0) return "0";
        return string.Join(" + ", terms);
    }
}

/// <summary>
/// 测试主程序
/// </summary>
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("===== 纯C#原生多项式拟合测试 =====");
        // 测试1:二次函数 y = 1 + 2x + 0.5x² 叠加少量噪声
        double[] x = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        double[] y = {
            1.05, 3.48, 7.02, 11.47, 16.93,
            23.41, 30.89, 39.36, 48.82, 59.27, 70.75
        };

        PolynomialFitter fitter = new PolynomialFitter(x, y);
        int fitOrder = 2; // 二次多项式拟合

        try
        {
            double[] coeffs = fitter.Fit(fitOrder);
            var metrics = fitter.CalcFitMetrics(coeffs);

            Console.WriteLine($"拟合阶数:{fitOrder}");
            Console.WriteLine($"多项式表达式:P(x) = {PolynomialFitter.GetPolyExpression(coeffs)}");
            Console.WriteLine($"拟合系数 [a0,a1,a2]:{string.Join(", ", coeffs)}");
            Console.WriteLine($"残差平方和 SSE = {metrics.SSE:F6}");
            Console.WriteLine($"决定系数 R² = {metrics.R2:F6}");
            Console.WriteLine("\n单点预测演示 x=5 预测值:" + PolynomialFitter.CalcPolyValue(5, coeffs).ToString("F4"));
        }
        catch (Exception ex)
        {
            Console.WriteLine("拟合失败:" + ex.Message);
        }

        Console.ReadLine();
    }
}

代码说明

  • 纯手写实现:所有浮点运算和矩阵消元操作均为原生实现,未使用 NuGet 或第三方数学库
  • 数值稳定性保障:内置极小主元检测机制,有效避免奇异矩阵导致的运算崩溃
  • 完整计算流程:支持从数据加载→拟合求解→结果预测→拟合评估→多项式表达式输出的完整链路
  • 验证案例:使用带噪声的二次曲线数据进行测试,拟合结果接近真实系数(a0=1, a1=2, a2=0.5),R²值趋近于1
  • 适用性说明:支持任意低阶多项式拟合(推荐m≤6阶,高阶易出现矩阵奇异问题)

算法优缺点


优点

理论简单易实现

基于最小二乘法原理,数学推导清晰直观(通过求导求极值直接得到解析解)。可使用原生代码(如 Python 基础库、C 语言标准库)实现,无需依赖第三方机器学习框架(如 TensorFlow、PyTorch)。例如,使用 NumPy 仅需 10 行代码即可完成核心计算。

全局最优解保证

损失函数为严格凸函数(二次型),其 Hessian 矩阵正定,不存在局部最优问题。通过正规方程 单次矩阵运算即可获得全局最优系数,无需像梯度下降等迭代算法担心收敛问题。

计算效率高(低阶情况)

当多项式阶数时,矩阵运算复杂度为 为样本数),现代 CPU 可在毫秒级完成万级样本计算,适用于嵌入式系统(如 STM32)、工业控制器(PLC)等资源受限设备的实时运行场景。

输出函数性质优良

拟合结果为处处连续且无限可导的多项式函数,便于后续数学操作,例如:

  • 插值计算(任意点的函数值预测)
  • 求导分析(获取变化率、极值点)
  • 积分运算(计算曲线下面积)

评估体系完善

提供多种统计指标量化拟合质量:

  • 决定系数(0-1 区间,越接近 1 拟合越好)
  • SSE 误差平方和(绝对误差量级)
    可通过交叉验证简单选择最优阶数。

数据适应性强

适用于多种离散采样场景:

  • 传感器周期性采样数据(温度、压力等)
  • 物理/化学实验观测数据
  • 短时间序列(如 24 小时温度变化)
  • 非均匀采样数据(只要满足最小样本量)

缺点

高阶数值不稳定性

时,正规方程中的 矩阵条件数呈指数增长,导致:

  • 矩阵求逆出现严重数值误差
  • 系数值异常波动(如 量级系数)
  • 预测值偏离真实趋势数个数量级

过拟合风险极高

随着阶数增加会出现:

  • 训练误差持续下降但测试误差呈 U 型反转
  • 拟合曲线出现不合理震荡(Runge 现象)
    实际应用中需严格限制(经验值)。

外推可靠性差

预测行为呈现典型多项式特征:

  • 区间内拟合可能很精确
  • 超出训练数据范围后快速发散
    例如,拟合温度变化曲线后,预测明天温度可能合理,但预测下月温度会严重偏离。

非线性表达能力有限

无法处理复杂模式:

  • 分段函数(如不同区间的不同物理规律)
  • 周期性波动(三角函数特征)
  • 突变点(如阶跃变化)
    需改用样条回归或分段回归。

异常值敏感度高

平方损失函数会放大离群点的影响:

  • 单个异常点可使系数偏移 50% 以上
  • 实际应用需配合 RANSAC 或 Huber 损失
    例如,传感器偶发错误数据会完全破坏拟合效果。

样本量硬性限制

拟合 阶多项式需要满足:

  • 最少 个样本(否则矩阵不可逆)
  • 实际建议 以保证稳定性
    小样本下只能选择低阶模型。

适用场景


推荐使用场景

传感器标定

  • 温湿度、压力、电压传感器输入输出曲线校正
  • 方法:采用 1~3 阶多项式拟合传感器输出与真实值的映射关系
    • 线性(1 阶)校正:y = a0 + a1 * x
    • 二次(2 阶)校正:y = a0 + a1 * x + a2 * x²
    • 三次(3 阶)校正:y = a0 + a1 * x + a2 * x² + a3 * x³
  • 适用场景:低非线性误差的传感器标定(如热电偶线性化、ADC 电压校准等)

实验数据平滑

  • 物理、化学实验离散观测点拟合平滑曲线
  • 作用:消除随机测量噪声,生成连续可微的平滑曲线
  • 示例:自由落体实验的位移-时间数据拟合,避免离散点直接连接导致的锯齿状曲线
  • 适用条件:数据点较少且噪声较小的实验场景

短区间插值

  • 在已知离散点之间填充连续数值,生成平滑过渡曲线
  • 示例:温度随时间变化的平滑曲线绘制,补充未采样的时间点数据
  • 注意:仅适用于短区间内插值,避免长区间外推误差累积

简单趋势预测

  • 基于近期数据点拟合多项式,预测未来短时间内的趋势(如 3~5 个数据点)
  • 示例:股票价格短时波动趋势预测(不适用于长期预测)
  • 限制:仅限局部趋势估算,长期外推误差较大

嵌入式轻量化计算

  • 适用于单片机、工控软件等无第三方库环境的曲线拟合
  • 优势:多项式拟合计算量小,适合资源受限的嵌入式设备(如 STM32、Arduino)
  • 示例:PLC 工控系统实时校正传感器数据,无需依赖复杂数学库

数学教学演示

  • 最小二乘法、线性方程组教学案例
  • 作用:通过多项式拟合直观展示最小二乘法原理
  • 示例:用 2 阶多项式拟合散点图,演示残差平方和最小化过程

不推荐使用场景

长时序预测与大范围外推

  • 问题:高阶多项式外推易过拟合,导致数值爆炸或振荡
  • 替代方案:线性回归(全局趋势)、样条插值(分段平滑)、LSTM(时序依赖建模)

强周期数据(正弦、震荡信号)

  • 问题:多项式难以准确拟合周期性波动
  • 替代方案:傅里叶拟合(频域分解)、三角函数回归(如 y = a*sin(bx) + c*cos(dx)

存在大量异常噪声或离群点

  • 问题:最小二乘法对异常值敏感,拟合曲线易偏离真实趋势
  • 替代方案:鲁棒最小二乘(RANSAC)、中位数拟合(Theil-Sen)

高维度特征拟合

  • 问题:标准多项式回归仅支持单自变量(y=f(x)),无法处理多变量(如 z=f(x,y)
  • 替代方案:多元多项式回归(如 z = a0 + a1x + a2y + a3xy)或机器学习模型(随机森林、神经网络)

需要高精度高阶拟合

  • 问题:高阶多项式矩阵求逆易出现病态问题,导致数值不稳定
  • 替代方案:B 样条(局部低阶拼接)、正交多项式(如切比雪夫多项式,降低条件数)

:以上场景划分基于多项式拟合的数学特性,实际应用中需结合数据分布和需求选择合适方法。

总结


  • 多项式拟合是基于最小二乘准则的基础回归算法,通过构建正规方程组 + 高斯消元求解最优多项式系数,纯 C# 原生可完整实现,无需第三方数学库;
  • 算法核心约束来自多项式阶数:低阶稳定可靠,高阶存在严重数值病态与过拟合问题,工程上优先使用 1~4 阶;
  • 性能由样本量 N 与阶数 m 共同决定,时间复杂度O(Nm2+m3),适合小样本、局部区间、低非线性程度数据;
  • 优势是轻量化、全局最优、实现简单;短板是外推弱、高阶不稳定、对噪声敏感;
  • 工程使用规范:优先从低阶开始拟合,通过 R² 判断是否提升阶数,禁止无限制提高阶数;如需大范围高精度拟合,可采用分段多项式拟合替代单一高阶多项式。

本文完整源码:多项式拟合算法C#源码仓库

本文完整覆盖多项式拟合数学推导、原生 C# 实现、误差评估与工程避坑,源码无第三方依赖,可直接移植 PLC/STM32。觉得干货有用可以点赞收藏,专栏持续更新 LMS 自适应滤波、卡尔曼滤波、各类曲线拟合原生实现,关注不迷路。