C# 中的非线性回归

Posted

技术标签:

【中文标题】C# 中的非线性回归【英文标题】:Non-linear regression in C# 【发布时间】:2011-12-09 04:14:17 【问题描述】:

我正在寻找一种基于二维数据集生成非线性(最好是二次)曲线的方法,用于预测目的。现在我正在使用我自己的普通最小二乘法 (OLS) 实现来产生线性趋势,但我的趋势更适合曲线模型。我正在分析的数据是一段时间内的系统负载。

这是我用来产生线性系数的方程式:

我看过 Math.NET Numerics 和其他一些库,但它们要么提供 interpolation 而不是 regression(这对我没用),或者代码无法以某种方式工作。

任何人都知道任何可以产生这种曲线系数的免费开源库或代码示例?

【问题讨论】:

这正是我正在寻找的结果:www3.wolframalpha.com/input/… 另一个带有链接的 q:***.com/questions/882009/… 一个相关的代码项目:codeproject.com/KB/recipes/QuadraticRegression.aspx 前者是一组插值模型的链接,而后者给出了无法使用的不正确/不准确的结果,即使在将其转换为使用decimal而不是double之后也是如此。 标题错误。这是线性回归,因为多项式可以表示为参数的线性组合。公认的解决方案正是这样做的:将多项式分解为 Vandermonde 矩阵和参数向量的乘积。 【参考方案1】:

我会查看http://mathforum.org/library/drmath/view/53796.html 以尝试了解如何做到这一点。

那么this 有一个很好的实现,我认为它会对你有所帮助。

【讨论】:

那个实现对我不起作用。它开箱即用地给出了完全不正确的结果(系数为 0/1/0),经过一些调整后仍然完全不准确。与我之前发现的 Wolfram Alpha 结果完全不同。不幸的是,您提供的链接中的数学超出了我的范围。【参考方案2】:

我不认为你想要非线性回归。即使你使用的是二次函数,它仍然被称为线性回归。您想要的称为多元回归。如果您想要一个二次项,您只需将 x 平方项添加到您的因变量。

【讨论】:

线性回归给了我一条线。我不知道如何编辑我发布的方程式来做任何不同的事情。我想得到这样的结果:www3.wolframalpha.com/input/… 使用公式。但不是 x1 x2 使用你的 x 和 x 平方。如果您仍然有问题,我会尝试在 excel 或伪代码中制作一个示例 calc。【参考方案3】:

我使用了MathNet.Iridium 版本,因为它与.NET 3.5 和VS2008 兼容。该方法基于Vandermonde 矩阵。然后我创建了一个类来保存我的多项式回归

using MathNet.Numerics.LinearAlgebra;

public class PolynomialRegression

    Vector x_data, y_data, coef;
    int order;

    public PolynomialRegression(Vector x_data, Vector y_data, int order)
    
        if (x_data.Length != y_data.Length)
        
            throw new IndexOutOfRangeException();
        
        this.x_data = x_data;
        this.y_data = y_data;
        this.order = order;
        int N = x_data.Length;
        Matrix A = new Matrix(N, order + 1);
        for (int i = 0; i < N; i++)
        
            A.SetRowVector( VandermondeRow(x_data[i]) , i);
        

        // Least Squares of |y=A(x)*c| 
        //  tr(A)*y = tr(A)*A*c
        //  inv(tr(A)*A)*tr(A)*y = c
        Matrix At = Matrix.Transpose(A);
        Matrix y2 = new Matrix(y_data, N);
        coef = (At * A).Solve(At * y2).GetColumnVector(0);
    

    Vector VandermondeRow(double x)
    
        double[] row = new double[order + 1];
        for (int i = 0; i <= order; i++)
        
            row[i] = Math.Pow(x, i);
        
        return new Vector(row);
    

    public double Fit(double x)
    
        return Vector.ScalarProduct( VandermondeRow(x) , coef);
    

    public int Order  get  return order;  
    public Vector Coefficients  get  return coef;  
    public Vector XData  get  return x_data;  
    public Vector YData  get  return y_data;  

然后我像这样使用它:

using MathNet.Numerics.LinearAlgebra;

class Program

    static void Main(string[] args)
    
        Vector x_data = new Vector(new double[]  0, 1, 2, 3, 4 );
        Vector y_data = new Vector(new double[]  1.0, 1.4, 1.6, 1.3, 0.9 );

        var poly = new PolynomialRegression(x_data, y_data, 2);

        Console.WriteLine("0,61,9", "x", "y");
        for (int i = 0; i < 10; i++)
        
            double x = (i * 0.5);
            double y = poly.Fit(x);

            Console.WriteLine("0,6:F21,9:F4", x, y);
        
    

[1,0.57,-0.15] 的计算系数与输出:

    x        y
 0.00   1.0000
 0.50   1.2475
 1.00   1.4200
 1.50   1.5175
 2.00   1.5400
 2.50   1.4875
 3.00   1.3600
 3.50   1.1575
 4.00   0.8800
 4.50   0.5275

与 Wolfram Alpha 的 quadratic 结果匹配。

编辑 1 为了达到您想要的效果,请尝试对 x_datay_data 进行以下初始化:

Matrix points = new Matrix( new double[,]     1, 82.96 , 
                 2, 86.23 ,   3, 87.09 ,   4, 84.28 , 
                 5, 83.69 ,   6, 89.18 ,   7, 85.71 , 
                 8, 85.05 ,   9, 85.58 ,  10, 86.95 , 
                11, 87.95 ,  12, 89.44 ,  13, 93.47   );
Vector x_data = points.GetColumnVector(0);
Vector y_data = points.GetColumnVector(1);

产生以下系数(从最低功率到最高)

Coef=[85.892,-0.5542,0.074990]
     x        y
  0.00  85.8920
  1.00  85.4127
  2.00  85.0835
  3.00  84.9043
  4.00  84.8750
  5.00  84.9957
  6.00  85.2664
  7.00  85.6871
  8.00  86.2577
  9.00  86.9783
 10.00  87.8490
 11.00  88.8695
 12.00  90.0401
 13.00  91.3607
 14.00  92.8312

【讨论】:

完美解决了我的问题。谢谢:) 我优化升级了你的答案:) ***.com/a/12770686/1046374【参考方案4】:

@ja72 代码很不错。但我将它移植到当前版本的Math.NET(据我了解,目前不支持MathNet.Iridium)并优化了代码大小和性能(例如,Math.Pow 函数由于性能缓慢而未在我的解决方案中使用) .

public class PolynomialRegression

    private int _order;
    private Vector<double> _coefs;

    public PolynomialRegression(DenseVector xData, DenseVector yData, int order)
    
        _order = order;
        int n = xData.Count;

        var vandMatrix = new DenseMatrix(xData.Count, order + 1);
        for (int i = 0; i < n; i++)
            vandMatrix.SetRow(i, VandermondeRow(xData[i]));
        
        // var vandMatrixT = vandMatrix.Transpose();
        // 1 variant:
        //_coefs = (vandMatrixT * vandMatrix).Inverse() * vandMatrixT * yData;
        // 2 variant:
        //_coefs = (vandMatrixT * vandMatrix).LU().Solve(vandMatrixT * yData);
        // 3 variant (most fast I think. Possible LU decomposion also can be replaced with one triangular matrix):
        _coefs = vandMatrix.TransposeThisAndMultiply(vandMatrix).LU().Solve(TransposeAndMult(vandMatrix, yData));
    

    private Vector<double> VandermondeRow(double x)
    
        double[] result = new double[_order + 1];
        double mult = 1;
        for (int i = 0; i <= _order; i++)
        
            result[i] = mult;
            mult *= x;
        
        return new DenseVector(result);
    

    private static DenseVector TransposeAndMult(Matrix m, Vector v)
    
        var result = new DenseVector(m.ColumnCount);
        for (int j = 0; j < m.RowCount; j++)
        
            double v_j = v[j];
            for (int i = 0; i < m.ColumnCount; i++)
                result[i] += m[j, i] * v_j;
        
        return result;
    

    public double Calculate(double x)
    
        return VandermondeRow(x) * _coefs;
    

也可以在 github:gist 上找到。

【讨论】:

以上是关于C# 中的非线性回归的主要内容,如果未能解决你的问题,请参考以下文章

什么是线性回归模型

spss非线性回归分析步骤

一元线性回归模型和一元线性回归方程之间的区别

回归分析 R语言 -- 多元线性回归

如何搞懂机器学习中的线性回归模型?机器学习系列之线性回归基础篇

多元线性回归多重共线性检验及避免方法,简单点的