加速最小二乘法

2024-05-16 09:44:49 发布

您现在位置:Python中文网/ 问答频道 /正文

如何加快函数least_square的速度?我们有六个变量(3个方向角和3个轴平移)需要优化。我们将两组3D点、平面上的两组点和投影矩阵应用于函数的输入

dSeed = np.zeros(6)
optRes = least_squares(minReproj, dSeed, method='lm', max_nfev=600,
    args=(points_prev, points_cur, d3d_prev, d3d_cur, Proj1))

此函数将点的前后投影误差降至最低

def minReproj(dof, d2dPoints1, d2dPoints2, d3dPoints1, d3dPoints2, w2cMatrix):
    Rmat = genEulerZXZ(dof[0], dof[1], dof[2]) # my function
    translationArray = np.array([[dof[3]], [dof[4]], [dof[5]]])
    temp = np.hstack((Rmat, translationArray))
    perspectiveProj = np.vstack((temp, [0, 0, 0, 1]))

    numPoints = d2dPoints1.shape[0]
    errorA = np.zeros((numPoints,3))
    errorB = np.zeros((numPoints,3))

    forwardProj = np.matmul(w2cMatrix, perspectiveProj)
    backwardProj = np.matmul(w2cMatrix, np.linalg.inv(perspectiveProj))

    for i in range(numPoints):
        Ja = np.ones((3))
        Jb = np.ones((3))
        Wa = np.ones((4))
        Wb = np.ones((4))

        Ja[0:2] = d2dPoints1[i,:]
        Jb[0:2] = d2dPoints2[i,:]
        Wa[0:3] = d3dPoints1[i,:]
        Wb[0:3] = d3dPoints2[i,:]

        JaPred = np.matmul(forwardProj, Wb)
        JaPred /= JaPred[-1]
        e1 = Ja - JaPred

        JbPred = np.matmul(backwardProj, Wa)
        JbPred /= JbPred[-1]
        e2 = Jb - JbPred

        errorA[i,:] = e1
        errorB[i,:] = e2

    residual = np.vstack((errorA,errorB))
    return residual.flatten()
def genEulerZXZ(psi, theta, sigma):
    c1 = cos(psi)
    s1 = sin(psi)
    c2 = cos(theta)
    s2 = sin(theta)
    c3 = cos(sigma)
    s3 = sin(sigma)

    mat = np.zeros((3,3))

    mat[0,0] = (c1 * c3) - (s1 * c2 * s3)
    mat[0,1] = (-c1 * s3) - (s1 * c2 * c3)
    mat[0,2] = (s1 * s2)

    mat[1,0] = (s1 * c3) + (c1 * c2 * s3)
    mat[1,1] = (-s1 * s3) + (c1 * c2 * c3)
    mat[1,2] = (-c1 * s2)

    mat[2,0] = (s2 * s3)
    mat[2,1] = (s2 * c3)
    mat[2,2] = c2

    return mat

这种优化需要0.2到0.4秒,这太多了。也许你知道如何加快这个过程? 或者也许有另一种方法可以找到两个点集的相对旋转和平移? 对于rpoleski:

       96    0.023    0.000   19.406    0.202 /usr/local/lib/python3.7/dist-packages/scipy/optimize/_lsq/least_squares.py:240(least_squares)
     4548    0.132    0.000   18.986    0.004 /usr/local/lib/python3.7/dist-packages/scipy/optimize/_lsq/least_squares.py:801(fun_wrapped)
       96    0.012    0.000   18.797    0.196 /usr/local/lib/python3.7/dist-packages/scipy/optimize/_lsq/least_squares.py:42(call_minpack)
     4548   11.102    0.002   18.789    0.004 /home/pi/helperFunctions.py:29(minimizeReprojection)

Tags: s3nponeszerosleastdofc2squares
1条回答
网友
1楼 · 发布于 2024-05-16 09:44:49

最有可能的是,在^{}期间,大部分时间都花在计算残差上,在您的例子中,残差的形式是minReproj()中的代码

但是,您在minReproj()中提供的代码似乎有一个次优内存管理,通过预分配可以大大改进。这将大大提高速度


例如,vstack()hstack()由于必须将其参数复制到其最终结果的内存中而遭受重大惩罚。考虑^ ^ {CD2}}的前几行,我在^ {CD7}}中重新分组。这些可以重写为gen_affine(),具有更好的计时(我还重写了gen_euler_zxz()以不分配新内存):

import numpy as np
from math import sin, cos


def gen_euler_zxz(result, psi, theta, sigma):
    c1 = cos(psi)
    s1 = sin(psi)
    c2 = cos(theta)
    s2 = sin(theta)
    c3 = cos(sigma)
    s3 = sin(sigma)
    result[0,0] = (c1 * c3) - (s1 * c2 * s3)
    result[0,1] = (-c1 * s3) - (s1 * c2 * c3)
    result[0,2] = (s1 * s2)
    result[1,0] = (s1 * c3) + (c1 * c2 * s3)
    result[1,1] = (-s1 * s3) + (c1 * c2 * c3)
    result[1,2] = (-c1 * s2)
    result[2,0] = (s2 * s3)
    result[2,1] = (s2 * c3)
    result[2,2] = c2
    return result


def gen_affine(dof):
    result = np.zeros((4, 4))
    gen_euler_zxz(result[:3, :3], dof[0], dof[1], dof[2])
    result[:3, 3] = dof[3:]
    result[3, 3] = 1
    return result


def gen_affine_OP(dof):
    Rmat = gen_euler_zxz(np.empty((3, 3)), dof[0], dof[1], dof[2])
    translationArray = np.array([[dof[3]], [dof[4]], [dof[5]]])
    temp = np.hstack((Rmat, translationArray))
    return np.vstack((temp, [0, 0, 0, 1]))


dof = 1, 2, 3, 4, 5, 6
%timeit gen_affine_OP(dof)
# 100000 loops, best of 3: 16.6 µs per loop
%timeit gen_affine(dof)
# 100000 loops, best of 3: 5.02 µs per loop

类似地,通过定义更大的残差并为errorAerrorB提供关于它的视图,可以避免residual = np.vstack((errorA,errorB))调用

另一个例子是在创建JaJbWaWb时:

def func_OP(numPoints):
    for i in range(numPoints):
        Ja = np.ones((3))
        Jb = np.ones((3))
        Wa = np.ones((4))
        Wb = np.ones((4))


def func(n):
    Ja = np.empty(3)
    Jb = np.empty(3)
    Wa = np.empty(3)
    Wb = np.empty(3)
    for i in range(n):
        Ja[:] = 1
        Jb[:] = 1
        Wa[:] = 1
        Wb[:] = 1


%timeit func_OP(1000)
# 100 loops, best of 3: 9.31 ms per loop
%timeit func(1000)
# 100 loops, best of 3: 2.2 ms per loop

另外,.flatten()正在制作一个您不需要的副本,而.ravel()将只返回一个视图,但这已经足够满足您的需要,并且速度更快:

a = np.ones((300, 300))
%timeit a.ravel()
# 1000000 loops, best of 3: 215 ns per loop
%timeit a.flatten()
# 10000 loops, best of 3: 113 µs per loop

最后一点是关于主回路的加速。 我并没有为此设计一个简单的矢量化重写,但您可以使用Numba加快速度(只要它在非对象模式下编译)

为此,还需要JIT修饰gen_euler_zxz(),并且需要用np.dot()替换np.matmul()调用(因为Numba不支持np.matmul()


最终,修订后的minReproj()将如下所示:

from math import sin, cos
import numpy as np
import numba as nb


matmul = np.dot


@nb.jit
def gen_euler_zxz(result, psi, theta, sigma):
    c1 = cos(psi)
    s1 = sin(psi)
    c2 = cos(theta)
    s2 = sin(theta)
    c3 = cos(sigma)
    s3 = sin(sigma)
    result[0, 0] = (c1 * c3) - (s1 * c2 * s3)
    result[0, 1] = (-c1 * s3) - (s1 * c2 * c3)
    result[0, 2] = (s1 * s2)
    result[1, 0] = (s1 * c3) + (c1 * c2 * s3)
    result[1, 1] = (-s1 * s3) + (c1 * c2 * c3)
    result[1, 2] = (-c1 * s2)
    result[2, 0] = (s2 * s3)
    result[2, 1] = (s2 * c3)
    result[2, 2] = c2
    return result


@nb.jit
def minReproj(dof, d2dPoints1, d2dPoints2, d3dPoints1, d3dPoints2, w2cMatrix):
    perspectiveProj = np.zeros((4, 4))
    gen_euler_zxz(perspectiveProj[:3, :3], dof[0], dof[1], dof[2])
    perspectiveProj[:3, 3] = dof[3:]
    perspectiveProj[3, 3] = 1

    numPoints = d2dPoints1.shape[0]
    residual = np.empty((2 * numPoints, 3))

    forwardProj = matmul(w2cMatrix, perspectiveProj)
    backwardProj = matmul(w2cMatrix, np.linalg.inv(perspectiveProj))

    Ja = np.empty(3)
    Jb = np.empty(3)
    Wa = np.empty(4)
    Wb = np.empty(4)
    for i in range(numPoints):
        j = i + numPoints

        Ja[2] = Jb[2] = 1.0
        Wa[3] = Wb[3] = 1.0
        Ja[0:2] = d2dPoints1[i, :]
        Jb[0:2] = d2dPoints2[i, :]
        Wa[0:3] = d3dPoints1[i, :]
        Wb[0:3] = d3dPoints2[i, :]

        JaPred = matmul(forwardProj, Wb)
        JaPred /= JaPred[-1]
        residual[i, :] = Ja - JaPred

        JbPred = matmul(backwardProj, Wa)
        JbPred /= JbPred[-1]
        residual[j, :] = Jb - JbPred
    return residual.ravel()

在一些玩具数据上进行测试,结果显示速度显著提高,当适当使用Numba时,速度变得非常快:

n = 10000
dof = 1, 2, 3, 4, 5, 6
d2dPoints1 = np.random.random((n, 2))
d2dPoints2 = np.random.random((n, 2))
d3dPoints1 = np.random.random((n, 3))
d3dPoints2 = np.random.random((n, 3))
w2cMatrix = np.random.random((3, 4))
x = minReproj_OP(dof, d2dPoints1, d2dPoints2, d3dPoints1, d3dPoints2, w2cMatrix)
y = minReproj(dof, d2dPoints1, d2dPoints2, d3dPoints1, d3dPoints2, w2cMatrix)
z = minReproj_nb(dof, d2dPoints1, d2dPoints2, d3dPoints1, d3dPoints2, w2cMatrix)
print(np.allclose(x, y))
# True
print(np.allclose(x, z))
# True


%timeit minReproj_OP(dof, d2dPoints1, d2dPoints2, d3dPoints1, d3dPoints2, w2cMatrix)
# 1 loop, best of 3: 277 ms per loop
%timeit minReproj(dof, d2dPoints1, d2dPoints2, d3dPoints1, d3dPoints2, w2cMatrix)
# 10 loops, best of 3: 177 ms per loop
%timeit minReproj_nb(dof, d2dPoints1, d2dPoints2, d3dPoints1, d3dPoints2, w2cMatrix)
# 100 loops, best of 3: 5.69 ms per loop

相关问题 更多 >