C#中的非线性回归
c#
machine-learning
regression
5
0

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

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

普通最小二乘(OLS)公式

我看过Math.NET Numerics和其他一些库,但是它们要么提供插值功能 ,要么提供回归功能 (这对我没有用),或者代码无法正常工作。

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

参考资料:
Stack Overflow
收藏
评论
共 3 个回答
高赞 时间 活跃

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

收藏
评论

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

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,6}{1,9}", "x", "y");
        for (int i = 0; i < 10; i++)
        {
            double x = (i * 0.5);
            double y = poly.Fit(x);

            Console.WriteLine("{0,6:F2}{1,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的二次结果。 二次方程二次拟合

编辑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
收藏
评论

@ 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++)
            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上获得

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号