питон, угол между векторами: потребность в вычислительной эффективности - PullRequest
1 голос
/ 12 июня 2019

У меня более 5 миллионов пар двух 3D векторов, и мне нужно вычислить угол между каждой парой.Я пытался с:

# calculate angle 
def unit_vector(vector):
    return vector / np.linalg.norm(vector)

def angle_between(v1, v2):
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)
    return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))

как в это сообщение

Например:

a=[[1,1,1],[1,2,1],[6,4,5]]
b=[[1,0,0],[6,2,2],[1,9,2]]

anglevec=np.zeros(len(a))

for i in range(len(a)):
    anglevec[i]=angle_between(a[i], b[i])

print(anglevec)

, но реализация с циклом слишком медленная.

Может кто-нибудь помочь?

Ответы [ 2 ]

1 голос
/ 14 июня 2019

Подход Numba

Задача довольно проста для реализации, например.https://stackoverflow.com/a/16544330/4045774 Единственная проблема в том, что циклы Python чрезвычайно медленные.Этого можно избежать, используя Numba или Cython.

Пример

import numba as nb
import numpy as np

#You can disable parallelization with parallel=False
@nb.njit(fastmath=True,error_model="numpy",parallel=True)
def angle(v1,v2):
    #Check the dimensions, this may also have an effect on SIMD-vectorization
    assert v1.shape[1]==3
    assert v2.shape[1]==3
    res=np.empty(v1.shape[0])

    for i in nb.prange(v1.shape[0]):
        dot=0.
        a=0.
        b=0.
        for j in range(3):
            dot+=v1[i,j]*v2[i,j]
            a+=v1[i,j]**2
            b+=v2[i,j]**2
        res[i]=np.arccos(dot/(np.sqrt(a*b)))
    return res

Сроки

#Use numpy-arrays when working with arrays
a=np.random.rand(500_000,3)
b=np.random.rand(500_000,3)

%timeit res=your_func(a,b)
5.65 s ± 45.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res=angle(a,b) #without using Numba 
3.15 s ± 13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res=angle(a,b) #with Numba
2.13 ms ± 441 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
0 голосов
/ 12 июня 2019

Вот решение с использованием Pool и Pool.map.

from multiprocessing import Pool, cpu_count
import numpy as np

def unit_vector(vector):
    return vector / np.linalg.norm(vector)

def angle_between(v1, v2):
    #if you want to maximize speed, avoid making variables
    #v1_u = unit_vector(v1)
    #v2_u = unit_vector(v2)
    return np.arccos(np.clip(np.dot(unit_vector(v1_u),unit_vector(v2_u)),-1.0,1.0))

def calc_angle(_list):
    #use list unpacking instead of instantiate variables
    return angle_between(*_list) 


a=[[1,1,1],[1,2,1],[6,4,5]]
b=[[1,0,0],[6,2,2],[1,9,2]]

with Pool(cpu_count()) as p: #use the context manager
    angles = p.map(calc_angle, zip(a,b))

Выход:

>>> angles
[0.9553166181245092, 0.7398807743787404, 0.8775836986593762]
...