将结果与二次回归混淆

Confusing result with quadratic regression

所以,我正在尝试用二次回归拟合一些 x,y 数据对,可以在 http://polynomialregression.drque.net/math.html 找到示例公式。 以下是我的代码,它使用该显式公式并使用 numpy 内置函数进行回归,

import numpy as np 
x = [6.230825,6.248279,6.265732]
y = [0.312949,0.309886,0.306639472]
toCheck = x[2]


def evaluateValue(coeff,x):
    c,b,a = coeff
    val = np.around( a+b*x+c*x**2,9)
    act = 0.306639472
    error=  np.abs(act-val)*100/act
    print "Value = {:.9f} Error = {:.2f}%".format(val,error)



###### USing numpy######################
coeff = np.polyfit(x,y,2)
evaluateValue(coeff, toCheck)



################# Using explicit formula
def determinant(a,b,c,d,e,f,g,h,i):
    # the matrix is [[a,b,c],[d,e,f],[g,h,i]]
    return a*(e*i - f*h) - b*(d*i - g*f) + c*(d*h - e*g)

a = b = c = d = e = m = n = p = 0
a = len(x)
for i,j in zip(x,y):
        b += i
        c += i**2
        d += i**3
        e += i**4
        m += j
        n += j*i
        p += j*i**2
det = determinant(a,b,c,b,c,d,c,d,e)
c0 = determinant(m,b,c,n,c,d,p,d,e)/det
c1 = determinant(a,m,c,b,n,d,c,p,e)/det
c2 = determinant(a,b,m,b,c,n,c,d,p)/det

evaluateValue([c2,c1,c0], toCheck)




######Using another explicit alternative
def determinantAlt(a,b,c,d,e,f,g,h,i):
    return a*e*i - a*f*h - b*d*i +b*g*f + c*d*h - c*e*g # <- barckets removed

a = b = c = d = e = m = n = p = 0
a = len(x)
for i,j in zip(x,y):
        b += i
        c += i**2
        d += i**3
        e += i**4
        m += j
        n += j*i
        p += j*i**2
det = determinantAlt(a,b,c,b,c,d,c,d,e)
c0 = determinantAlt(m,b,c,n,c,d,p,d,e)/det
c1 = determinantAlt(a,m,c,b,n,d,c,p,e)/det
c2 = determinantAlt(a,b,m,b,c,n,c,d,p)/det

evaluateValue([c2,c1,c0], toCheck)

此代码给出此输出

Value = 0.306639472 Error = 0.00%
Value = 0.308333580 Error = 0.55%
Value = 0.585786477 Error = 91.03%

作为,你可以看到这些是彼此不同的,第三个是完全错误的。现在我的问题是:
1. 为什么显式公式给出的结果有点错误,如何改进?
2. numpy 如何给出如此准确的结果?
3.第三种情况只是打开括号,结果怎么变化这么大?

所以这里发生的一些事情不幸地困扰着你做事的方式。看看这段代码:

for i,j in zip(x,y):
        b += i
        c += i**2
        d += i**3
        e += i**4
        m += j
        n += j*i
        p += j*i**2

您构建的特征使得 x 值不仅是平方的,而且是立方和四次方的。

如果在将这些值放入 3 x 3 矩阵以求解之前打印出每个值:

In [35]: a = b = c = d = e = m = n = p = 0
    ...: a = len(x)
    ...: for i,j in zip(xx,y):
    ...:         b += i
    ...:         c += i**2
    ...:         d += i**3
    ...:         e += i**4
    ...:         m += j
    ...:         n += j*i
    ...:         p += j*i**2
    ...: print(a, b, c, d, e, m, n, p)
    ...:
    ...:
3 18.744836 117.12356813829001 731.8283056811686 4572.738547313946 0.9294744720000001 5.807505391292503 36.28641270376207

在处理 floating-point 算术时,尤其是对于小值,运算顺序确实很重要。这里发生的事情是侥幸,计算出的小值和大值的混合导致了一个非常小的值。因此,当您使用分解形式和扩展形式计算行列式时,请注意您如何获得略有不同的结果,但也要查看值的精度:

In [36]: det = determinant(a,b,c,b,c,d,c,d,e)

In [37]: det
Out[37]: 1.0913403514223319e-10

In [38]: det = determinantAlt(a,b,c,b,c,d,c,d,e)

In [39]: det
Out[39]: 2.3283064365386963e-10

行列式在10-10量级!存在差异的原因是因为使用 floating-point 算术,理论上两种行列式方法应该产生相同的结果,但不幸的是,实际上它们给出的结果略有不同,这是由于称为 错误传播 。因为可以表示 floating-point 数字的位数是有限的,所以运算顺序会改变错误传播的方式,因此即使您删除了括号并且公式基本匹配,运算顺序也会得到结果现在不同了。对于经常处理 floating-point 算术的任何软件开发人员来说,这篇文章都是必读的:What Every Computer Scientist Should Know About Floating-Point Arithmetic.

因此,当您尝试使用 Cramer 规则求解系统时,不可避免地要除以代码中的主要行列式,即使变化大约为 10-10,这两种方法之间的变化可以忽略不计,但是您会得到非常不同的结果,因为您在求解系数时除以这个数字。

NumPy 没有这个问题的原因是他们通过 least-squares 和 pseudo-inverse 解决了系统,而不是使用 Cramer 规则。我不建议使用 Cramer 规则来查找回归系数,这主要是由于经验,而且还有更可靠的方法。

然而,要解决您的特定问题,最好对数据进行 标准化 以便动态范围现在以 0 为中心。因此,您用来构建系数矩阵的特征更明智,因此计算过程更容易处理数据。在您的情况下,像用 x 值的平均值减去数据这样简单的事情应该可行。因此,如果您有要预测的新数据点,您 必须 在进行预测之前首先减去 x 数据的平均值。

因此,在您的代码开头,对该数据执行均值减法和回归。我已经向您展示了我在哪里修改了上面给出的源代码:

import numpy as np 
x = [6.230825,6.248279,6.265732]
y = [0.312949,0.309886,0.306639472]

# Calculate mean
me = sum(x) / len(x)
# Make new dataset that is mean subtracted
xx = [pt - me for pt in x]

#toCheck = x[2]

# Data point to check is now mean subtracted
toCheck = x[2] - me



def evaluateValue(coeff,x):
    c,b,a = coeff
    val = np.around( a+b*x+c*x**2,9)
    act = 0.306639472
    error=  np.abs(act-val)*100/act
    print("Value = {:.9f} Error = {:.2f}%".format(val,error))



###### USing numpy######################
coeff = np.polyfit(xx,y,2) # Change
evaluateValue(coeff, toCheck)



################# Using explicit formula
def determinant(a,b,c,d,e,f,g,h,i):
    # the matrix is [[a,b,c],[d,e,f],[g,h,i]]
    return a*(e*i - f*h) - b*(d*i - g*f) + c*(d*h - e*g)

a = b = c = d = e = m = n = p = 0
a = len(x)
for i,j in zip(xx,y): # Change
        b += i
        c += i**2
        d += i**3
        e += i**4
        m += j
        n += j*i
        p += j*i**2
det = determinant(a,b,c,b,c,d,c,d,e)
c0 = determinant(m,b,c,n,c,d,p,d,e)/det
c1 = determinant(a,m,c,b,n,d,c,p,e)/det
c2 = determinant(a,b,m,b,c,n,c,d,p)/det

evaluateValue([c2,c1,c0], toCheck)




######Using another explicit alternative
def determinantAlt(a,b,c,d,e,f,g,h,i):
    return a*e*i - a*f*h - b*d*i +b*g*f + c*d*h - c*e*g # <- barckets removed

a = b = c = d = e = m = n = p = 0
a = len(x)
for i,j in zip(xx,y): # Change
        b += i
        c += i**2
        d += i**3
        e += i**4
        m += j
        n += j*i
        p += j*i**2
det = determinantAlt(a,b,c,b,c,d,c,d,e)
c0 = determinantAlt(m,b,c,n,c,d,p,d,e)/det
c1 = determinantAlt(a,m,c,b,n,d,c,p,e)/det
c2 = determinantAlt(a,b,m,b,c,n,c,d,p)/det
evaluateValue([c2,c1,c0], toCheck)

当我运行这个时,我们现在得到:

In [41]: run interp_test
Value = 0.306639472 Error = 0.00%
Value = 0.306639472 Error = 0.00%
Value = 0.306639472 Error = 0.00%

作为您的最终读物,这是我在他们的问题中解决的其他人遇到的类似问题:。总结就是我建议他们不要使用 Cramer 规则,而是使用 least-squares 到 pseudo-inverse。我向他们展示了如何在不使用 numpy.polyfit 的情况下获得完全相同的结果。此外,使用 least-squares 概括了如果你有超过 3 个点,你仍然可以通过你的点拟合二次方,以便模型具有尽可能小的误差。