三维线性回归

时间:2014-07-14 23:27:55

标签: algorithm linear-regression

我想编写一个程序,给定3D空间中的点列表,表示为浮点的x,y,z坐标数组,在此空间中输出最佳拟合线。该线可以/应该是单位矢量和线上的点的形式。

问题在于我不知道如何做到这一点。我发现最接近的是this链接,但老实说,我不明白他是如何从等式到等式的,当我们得到矩阵时,我很丢失。

是否可以使用简单的二维线性回归的推广/可以有人解释(数学上)上述链接方法的工作原理(以及如何使用它来计算最佳拟合线) )?

5 个答案:

答案 0 :(得分:21)

线性回归

给出了N维线性回归的标准公式

Normal Equation for N-dimensional linear Regression

在结果中,enter image description here是大小为n + 1的向量,给出最适合数据的函数系数。

在你的情况下n = 3.而X是一个mx(n + 1)矩阵,称为设计矩阵 - 在你的情况下是mx4。要构造设计矩阵,只需将每个数据点坐标值(x1,x2,...)复制到X行中,此外,将每行第1列中的数字1放在一行。向量y具有与那些坐标相关联的值。术语enter image description hereenter image description here是“X的转置”和“X和X的转置乘积的倒数”。最后一个术语可以计算密集,因为反转矩阵是O(n ^ 3),但对于你n = 4,只要n小于5000,没问题。

一个例子

假设您有数据点(6,4,11)= 20,(8,5,15)= 30,(12,9,25)= 50和(2,1,3)= 7。 在那种情况下,

enter image description here

然后你只需要将事物相乘,你就可以直接获得。乘法矩阵很简单,虽然更复杂,但采用矩阵的逆矩阵是相当简单的(see here for example)。然而,对于像Matlab,Octave和Julia这样的科学计算语言(我将用它来说明),它是一个单行的。

julia> X = [1 6 4 11; 1 8 5 15; 1 12 9 25; 1 2 1 3]
4x4 Array{Int64,2}:
 1   6  4  11
 1   8  5  15
 1  12  9  25
 1   2  1   3

julia> y = [20;30;50;7]
4-element Array{Int64,1}:
 20
 30
 50
  7

julia> T = pinv(X'*X)*X'*y
4-element Array{Float64,1}:
  4.0
 -5.5
 -7.0
  7.0

...验证

julia> 12*(-5.5) + 9*(-7.0) + 25*(7) + 4
50.0

在Julia中,Matlab和Octave矩阵可以简单地乘以*,而转置运算符是'。注意这里我使用了pinv(伪逆),这是必要的(不是这次),当数据太冗余并产生不可逆的X-Xtranspose时,如果你选择自己实现矩阵求逆,请记住这一点。 / p>

取而代之的是PCA

主成分分析(PCA)是一种用于降维的技术,其目的是从n维空间中找到k维空间,使得投影误差最小化。在一般情况下,n和k是任意的,但在这种情况下n = 3且k = 1.有4个主要步骤。

第1步:数据预处理

要使标准方法起作用,必须首先执行均值归一化,并且还可能缩放数据,以使算法不会因浮点错误而失败。在后一种情况下,这意味着如果一个维度的值的范围相对于另一个维度是巨大的,则可能存在问题(例如,在一维中-1000到1000对比-0.1到0.2)。通常它们足够接近。均衡化只是意味着每个维度,减去每个数据点的平均值,以便结果数据集以原点为中心。获取结果并将每个数据点(x1,x2,... xn)作为一行存储在一个大矩阵X中。

X = [ 6 4 11; 8 5 15; 12 9 25; 2 1 3]
4x3 Array{Int64,2}:
  6  4  11
  8  5  15
 12  9  25
  2  1   3

找到平均值

y = convert(Array{Float64,1},([sum(X[1:4,x]) for x = 1:3])/4')
3-element Array{Float64,1}:
  7.0 
 4.75
 13.5 

...正常化

julia> Xm = X .- y'
4x3 Array{Float64,2}:
 -1.0  -0.75   -2.5
  1.0   0.25    1.5
  5.0   4.25   11.5
 -5.0  -3.75  -10.5

步骤2:计算协方差矩阵

协方差矩阵sigma就是

enter image description here

其中m是数据点的数量。

步骤3:执行奇异值分解

这里最好找到一个采用协方差矩阵并吐出答案的库。有很多,这里有一些; in python in Rin Java,当然还有Octave,Julia,Matlab(如R),这是另一个 svd

在协方差矩阵上执行SVD

(U,S,V) = svd((1/4)*Xm'*Xm);

第4步:找到

取第一个组件(对于k维度,您将采用前k个组件)

Ureduce = U[:,1]
3-element Array{Float64,1}:
 -0.393041
 -0.311878
 -0.865015

这是最小化投影误差的线

额外信用:回去

您甚至可以恢复原始值的近似值,但它们将排成一行并投影在同一条线上。连接点以获得线段。

获取X中每个数据点的缩小尺寸(因为1-D将各自为1个值):

z= Ureduce' * Xm'
1x4 Array{Float64,2}:
2.78949  -1.76853  -13.2384  12.2174

走回去;原始值,但都位于相同(最佳)线

julia> (Ureduce .* z .+ y)'
4x3 Array{Float64,2}:
  5.90362  3.88002   11.0871                         6  4  11
  7.69511  5.30157   15.0298      versus             8  5  15
 12.2032   8.87875   24.9514                        12  9  25
  2.19806  0.939664   2.93176                        2  1   3

答案 1 :(得分:1)

为给定的 3D 空间点列表找到最佳拟合线是一项非常困难的任务。 可以使用 2 个向量在 3D 空间中定义一条线:位于线上的点 a 和线(归一化)方向 n 。 可以用下面的等式来描述,其中t是实数

enter image description here

假设有一个点列表{(xᵢ, yᵢ, zᵢ)},点a可以用所有点的平均值表示,即

enter image description here

而方向n可以通过求解协方差矩阵的特征问题来找到

enter image description here

求解特征方程后,可以取最大特征值对应的特征向量,对应于解n

这是我使用 Armadillo library (C++) 对点集 {(1,1,1), (2,2,2), (3,3,3)} 进行的演示:

#include<armadillo>
#include<vector>
using namespace arma;

int main()
{
    std::vector<vec3> points {{ 
        {1, 1, 1}, 
        {2, 2, 2}, 
        {3, 3, 3} 
    }};
    int N = points.size();

    vec3 mean = {0, 0, 0};
    mat33 corr(fill::zeros);
    for(auto p : points)
    {
        mean += p;
        for(int i = 0; i < 3; i++)
            for(int j = i; j < 3; j++)
                corr(i, j) += p(i) * p(j);
    }

    corr /= N;
    mean /= N;

    mat33 cov {{
        {corr(0, 0) - mean(0) * mean(0), corr(0, 1) - mean(0) * mean(1), corr(0, 2) - mean(0) * mean(2)},
        {corr(0, 1) - mean(0) * mean(1), corr(1, 1) - mean(1) * mean(1), corr(1, 2) - mean(1) * mean(2)},
        {corr(0, 2) - mean(0) * mean(2), corr(1, 2) - mean(2) * mean(1), corr(2, 2) - mean(2) * mean(2)}
    }};

    vec3 eigval; mat33 eigvec;
    eig_sym(eigval, eigvec, cov);

    mean.print("\nPoint: ");

    eigvec.col(eigval.index_max())
          .print("\nDirection:");
}

答案 2 :(得分:0)

这可以通过require('ml-matrix')单线实现:

solve(this.DataX, Matrix.columnVector(this.DataY[0]));

答案 3 :(得分:0)

@WaTeim的答案很好

这是我在python中为需要它的人的贡献。与提供的数值示例一起使用

import React from 'react';
import Presentation from "../Presentation";

const data = () => import("../assets/sample.json");

const Finder = (props) => {

    const {prop1} = props;

    return ( <Presentation data={data} selection={prop1} /> );
};

export default Presentation;

顺便说一句。谁能告诉我为什么numpy的def regr(X): y= np.average(X, axis=0) Xm = X-y u, s, v = np.linalg.svd((1./X.shape[0])*np.matmul(Xm.T,Xm)) # Extra Credit: Going back z= np.matmul(u[:,0].T, Xm.T) c = np.array([z*n for n in u[:,0]]) d = np.array(y.tolist()*c.shape[1]).reshape(c.shape[1],-1).T e = (c+d).T return u,s,v regr(np.array([[6, 4, 11],[8,5,15],[12,9,25],[2,1,3]])) np.cov() ???

答案 4 :(得分:0)

我在 QT 代码中使用了这种简单的方法:

QPair<QVector3D, QVector3D> getLineByLeastSquares(const QVector<QVector3D>& points)
{
    if (points.size() <= 1)
        return QPair<QVector3D, QVector3D>();
    QVector3D avg;
    for (const QVector3D& p : points)
        avg += p;
    avg /= static_cast<float>(points.size());
    float nX = 0.0F, nY = 0.0F, nZ = 0.0F;
    for (const QVector3D& p : points)
    {
        const QVector3D tmp = p - avg;
        nX += tmp.x() * tmp.x();
        nY += tmp.x() * tmp.y();
        nZ += tmp.x() * tmp.z();
    }
    return QPair<QVector3D, QVector3D>(avg, QVector3D(nX, nY, nZ).normalized());
}

结果QPair<QVector3D, QVector3D>的第一个成分是线点,第二个成分是线法线。