在我的研究中,我必须实现两组向量之间的成对距离L1距离计算,每一组向量都表示为NumPy矩阵(向量是行)。这必须使用两个循环、一个循环和无循环来完成。我预计,由于NumPy在向量化方面非常出色,所以算法的排序必须是两个循环慢于一个循环比没有循环慢。在
我写了函数:
def f_cdist_2(X1, X2):
res = np.zeros(shape=(X1.shape[0], X2.shape[0]), dtype=np.float64)
for ix1 in range(X1.shape[0]):
for ix2 in range(X2.shape[0]):
res[ix1, ix2] = np.abs(X1[ix1, :] - X2[ix2, :]).sum()
return res
def f_cdist_1(X1, X2):
res = np.zeros(shape=(X1.shape[0], X2.shape[0]), dtype=np.float64)
for ix1 in range(X1.shape[0]):
res[ix1, :] = np.abs(np.tile(X1[ix1, :], (X2.shape[0], 1)) - X2).sum(axis=1)
return res
def f_cdist_0(X1, X2):
res = np.abs(
np.tile(X1[:, :, np.newaxis], (1, 1, X2.shape[0])) - \
np.tile(X2.T[np.newaxis, :, :], (X1.shape[0], 1, 1))
).sum(axis=1)
return res
然后我用128 x 512和256 x 512形状的两个随机矩阵测试了性能,基于100次运行,我得到了以下结果:
两个循环:156毫秒
一个循环:32毫秒
无循环:135毫秒
我还尝试了cdist
中的scipy.spatial.distance
,得到了最好的性能:9毫秒。在
现在,有没有更好的方法来实现无循环功能?我希望它的性能至少和一个循环一样好,但现在我不知道如何改进它。在
更新
使用kwinkunks的no-loops实现方法,我在矩阵1024x1024上重新运行了测试(又进行了100次测试),结果如下:
两个循环:5.7秒
一个循环:6.6秒
无循环:3.9秒
scipy.spatial.distance.cdist
:0.6秒
所以在更大的矩阵上,无循环实现确实更好。scipy
创造了奇迹,但如果我理解正确的话,它是用C编写的,因此性能非常好。在
更新
尝试使用4096 x 1024个矩阵np.float64
,相同的设置:
两个循环:88秒
一个循环:66秒
无循环:内存不足(目前有大约18 Gb的可用RAM)
scipy.spatial.distance.cdist
:13秒
您可以使用Pythran从矢量化版本获得额外的加速
f_距离py公司名称:
在我的笔记本电脑上,原始版本运行在:
^{pr2}$编译内核后:
您可以对其进行基准测试:
使用SIMD指令可进一步加快计算速度:
免责声明:我是pythran项目的核心开发人员。在
使用Numba的解决方案
Exmaple公司
性能
^{pr2}$编辑:手工优化的Numba版本
计时
您可以避免使用NumPy的广播进行平铺等:
但是,令人惊讶的是(不管怎样)它并没有比循环快(我的机器上大约90毫秒,而你的^{cd1>}函数的24毫秒)。
那个广播技巧通常很有用。这意味着你可以做这样的事情:
^{pr2}$相关问题 更多 >
编程相关推荐