最小二乘法:正规方程与 SVD
Least square methods: normal equation vs svd
我尝试按照 beta = inv(X'X)X'Y
的正规方程编写自己的线性回归代码。但是,平方误差比numpy.linalg
中的lstsq
函数大很多。谁能向我解释为什么 SVD 方法(lstsq 使用的)比正规方程更准确?谢谢
我怀疑您数据的矩阵 X'X
具有较高的 condition number. Trying to compute a numerical inverse of such a matrix can result in large errors. It is usually a bad idea to explicitly compute an inverse matrix (see, for example, http://www.johndcook.com/blog/2010/01/19/dont-invert-that-matrix/ or http://epubs.siam.org/doi/abs/10.1137/1.9780898718027.ch14)。
您可以使用 numpy.linalg.cond
查看条件编号。
这是一个例子。首先创建 X
和 Y
:
In [186]: X = np.random.randn(500, 30)
In [187]: Y = np.linspace(0, 1, len(X))
这个随机X
,条件数不大:
In [188]: np.linalg.cond(X.T.dot(X))
Out[188]: 2.4456380658308148
正规方程和 lstsq
给出相同的结果(根据 numpy.allclose
使用该函数的默认参数时):
In [189]: betan = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(Y)
In [190]: betal, res, rnk, s = np.linalg.lstsq(X, Y)
In [191]: np.allclose(betan, betal)
Out[191]: True
现在通过使两列几乎相同来调整 X
。这使得 X'X
几乎是单数,并赋予它一个很大的条件数:
In [192]: X[:,0] = X[:,1] + 1e-8*np.random.randn(len(X))
In [193]: np.linalg.cond(X.T.dot(X))
Out[193]: 3954529794300611.5
现在正规方程给出的结果不同于 lstsq
:
In [194]: betan = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(Y)
In [195]: betal, res, rnk, s = np.linalg.lstsq(X, Y)
In [196]: np.allclose(betan, betal)
Out[196]: False
我尝试按照 beta = inv(X'X)X'Y
的正规方程编写自己的线性回归代码。但是,平方误差比numpy.linalg
中的lstsq
函数大很多。谁能向我解释为什么 SVD 方法(lstsq 使用的)比正规方程更准确?谢谢
我怀疑您数据的矩阵 X'X
具有较高的 condition number. Trying to compute a numerical inverse of such a matrix can result in large errors. It is usually a bad idea to explicitly compute an inverse matrix (see, for example, http://www.johndcook.com/blog/2010/01/19/dont-invert-that-matrix/ or http://epubs.siam.org/doi/abs/10.1137/1.9780898718027.ch14)。
您可以使用 numpy.linalg.cond
查看条件编号。
这是一个例子。首先创建 X
和 Y
:
In [186]: X = np.random.randn(500, 30)
In [187]: Y = np.linspace(0, 1, len(X))
这个随机X
,条件数不大:
In [188]: np.linalg.cond(X.T.dot(X))
Out[188]: 2.4456380658308148
正规方程和 lstsq
给出相同的结果(根据 numpy.allclose
使用该函数的默认参数时):
In [189]: betan = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(Y)
In [190]: betal, res, rnk, s = np.linalg.lstsq(X, Y)
In [191]: np.allclose(betan, betal)
Out[191]: True
现在通过使两列几乎相同来调整 X
。这使得 X'X
几乎是单数,并赋予它一个很大的条件数:
In [192]: X[:,0] = X[:,1] + 1e-8*np.random.randn(len(X))
In [193]: np.linalg.cond(X.T.dot(X))
Out[193]: 3954529794300611.5
现在正规方程给出的结果不同于 lstsq
:
In [194]: betan = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(Y)
In [195]: betal, res, rnk, s = np.linalg.lstsq(X, Y)
In [196]: np.allclose(betan, betal)
Out[196]: False