Algorithm 三维线性回归

Algorithm 三维线性回归,algorithm,linear-regression,Algorithm,Linear Regression,我想写一个程序,给定三维空间中的点列表,用浮点表示为x,y,z坐标的数组,在这个空间中输出一条最佳拟合线。直线可以/应该是单位向量和直线上的点的形式 问题是我不知道该怎么做。我找到的最接近的东西是林克,尽管说实话,我不明白他是如何从一个方程到另一个方程的,当我们到达矩阵的时候,我已经迷路了 是否有一个简单2D线性回归的推广,我可以使用/有人可以(从数学上)解释上述方法的工作原理(以及如何使用它计算最佳拟合线)?线性回归 N维线性回归有一个标准公式,由 其中,结果是大小为n+1的向量,给出最适合

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

问题是我不知道该怎么做。我找到的最接近的东西是林克,尽管说实话,我不明白他是如何从一个方程到另一个方程的,当我们到达矩阵的时候,我已经迷路了

是否有一个简单2D线性回归的推广,我可以使用/有人可以(从数学上)解释上述方法的工作原理(以及如何使用它计算最佳拟合线)?

线性回归 N维线性回归有一个标准公式,由

其中,结果是大小为n+1的向量,给出最适合数据的函数系数

在你的情况下,n=3。而X是一个称为设计矩阵的mx(n+1)矩阵——在您的例子中是mx4。要构造设计矩阵,只需将每个数据点坐标值(x1,x2,…)复制到一行X中,此外,在每行的第1列中放置数字1。向量y具有与这些坐标关联的值。项和是“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。 那么,

然后,你只需将事情相乘,就可以直接得到。乘法矩阵很简单,虽然更复杂,但求矩阵的逆是相当简单的()。然而,对于像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和倍频程矩阵中,只需使用*即可相乘,而转置运算符为“”。请注意,我在这里使用了pinv(伪逆),当数据太冗余并导致不可逆的X-XTTranspose时,这是必要的(这次不是),如果您选择自己实现矩阵逆,请记住这一点

相反,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:计算协方差矩阵 协方差矩阵σ是简单的

其中m是数据点的数量

步骤3:执行奇异值分解 在这里,最好只找到一个库,它获取协方差矩阵并给出答案。有很多,这里有一些,当然,在八度音阶,Julia,Matlab(像R)中,这是另一个单线性svd

对协方差矩阵执行奇异值分解

(U,S,V) = svd((1/4)*Xm'*Xm);
第四步:找到线路 获取第一个组件(对于k个维度,您将获取第一个k个组件)

这是使投影误差最小化的线

额外学分:回去 您甚至可以恢复原始值的近似值,但它们将全部排列并投影在同一条线上。将这些点连接起来,得到一条线段

获得X中每个数据点的缩减维数(因为1-D将每个值为1):

往回走;原始值,但都位于同一条(最佳)线上


这可以通过NPM ml矩阵一衬板实现:

const { Matrix, solve } = require('ml-matrix');

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

伟大的回答@WaTeim

下面是我在python中为那些需要它的人所做的贡献。与提供的数值示例一起使用

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]]))
顺便问一下,有人能告诉我为什么numpy的
np.cov()
给出的结果与
1./X.shape[0])*np.matmul(Xm.T,Xm)

我在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 getLineByLeastSquares(常量QVector和点)
{

如果(points.size()在三维空间中为给定的点列表找到最佳拟合线是一项相当困难的任务。 可以使用两个向量定义三维空间中的直线:位于直线上的点a和直线(规格化)方向n。 它可以用下面的方程来描述,其中t是实数

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

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

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

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

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

#包括
#包括
使用arma;
int main()
{
std::向量点{{
{1, 1, 1}, 
{2, 2, 2}, 
{3, 3, 3} 
}};
int N=points.size();
vec3平均值={0,0,0};
mat33 corr(填充:零);
用于(自动p:点)
{
平均值+=p;
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]]))
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());
}
#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:");
}