Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Numpy 求两个矩阵之间的最小余弦距离_Numpy_Scipy_Vectorization_Linear Algebra - Fatal编程技术网

Numpy 求两个矩阵之间的最小余弦距离

Numpy 求两个矩阵之间的最小余弦距离,numpy,scipy,vectorization,linear-algebra,Numpy,Scipy,Vectorization,Linear Algebra,我有两个2Dnp。数组我们叫它们A和B,它们都有相同的形状。对于二维数组A中的每个向量,我需要在矩阵B中找到具有最小余弦距离的向量。要做到这一点,我只需要一个double for循环,在其中我试图找到最小值。因此,我基本上做了以下工作: from scipy.spatial.distance import cosine l, res = A.shape[0], [] for i in xrange(l): minimum = min((cosine(A[i], B[j]), j) for

我有两个2D
np。数组
我们叫它们
A
B
,它们都有相同的形状。对于二维数组
A
中的每个向量,我需要在矩阵
B
中找到具有最小余弦距离的向量。要做到这一点,我只需要一个double for循环,在其中我试图找到最小值。因此,我基本上做了以下工作:

from scipy.spatial.distance import cosine
l, res = A.shape[0], []
for i in xrange(l):
    minimum = min((cosine(A[i], B[j]), j) for j in xrange(l))
    res.append(minimum[1])
在上面的代码中,一个循环隐藏在理解后面。一切都很好,但是double for循环让它太慢了(我试着用双重理解重写它,这让事情快了一点,但仍然很慢)

我相信有一个numpy函数可以更快地实现以下目标(使用一些线性代数)

那么,有没有办法更快地实现我想要的呢?

从中我们得到了以下信息-

scipy.spatial.distance.cosine(u,v):计算一维阵列之间的余弦距离

u
v
之间的余弦距离定义为

其中
u⋅v
u
v
的点积

使用上面的公式,我们将得到一个向量化的解决方案,如下所示-

# Get the dot products, L2 norms and thus cosine distances
dots = np.dot(A,B.T)
l2norms = np.sqrt(((A**2).sum(1)[:,None])*((B**2).sum(1)))
cosine_dists = 1 - (dots/l2norms)

# Get min values (if needed) and corresponding indices along the rows for res.
# Take care of zero L2 norm values, by using nanmin and nanargmin  
minval = np.nanmin(cosine_dists,axis=1)
cosine_dists[np.isnan(cosine_dists).all(1),0] = 0
res = np.nanargmin(cosine_dists,axis=1)
运行时测试-

In [81]: def org_app(A,B):
    ...:    l, res, minval = A.shape[0], [], []
    ...:    for i in xrange(l):
    ...:        minimum = min((cosine(A[i], B[j]), j) for j in xrange(l))
    ...:        res.append(minimum[1])
    ...:        minval.append(minimum[0])
    ...:    return res, minval
    ...: 
    ...: def vectorized(A,B):
    ...:     dots = np.dot(A,B.T)
    ...:     l2norms = np.sqrt(((A**2).sum(1)[:,None])*((B**2).sum(1)))
    ...:     cosine_dists = 1 - (dots/l2norms)
    ...:     minval = np.nanmin(cosine_dists,axis=1)
    ...:     cosine_dists[np.isnan(cosine_dists).all(1),0] = 0
    ...:     res = np.nanargmin(cosine_dists,axis=1)
    ...:     return res, minval
    ...: 

In [82]: A = np.random.rand(400,500)
    ...: B = np.random.rand(400,500)
    ...: 

In [83]: %timeit org_app(A,B)
1 loops, best of 3: 10.8 s per loop

In [84]: %timeit vectorized(A,B)
10 loops, best of 3: 145 ms per loop
验证结果-

In [86]: x1, y1 = org_app(A, B)
    ...: x2, y2 = vectorized(A, B)
    ...: 

In [87]: np.allclose(np.asarray(x1),x2)
Out[87]: True

In [88]: np.allclose(np.asarray(y1)[~np.isnan(np.asarray(y1))],y2[~np.isnan(y2)])
Out[88]: True

使用
scipy.space.distance.cdist

from scipy.spatial.distance import cdist

def cdist_func(A, B):
    dists = cdist(A, B, 'cosine')
    return np.argmin(dists, axis=1), np.min(dists, axis=1)
它得到的结果与Divakar的答案相同:

x2, y2 = vectorized(A, B)
x3, y3 = cdist_func(A, B)

np.allclose(x2, x3) # True
np.allclose(y2, y3) # True
但它没有那么快:

%timeit vectorized(A, B) # 11.9 ms per loop
%timeit cdist_func(A, B) # 85.9 ms per loop

这里有一个例子:我所做的就是
A=np.load('A.npy')
B=np.load('B.npy')
x1,y1=org\u app(A,B)
x2,y2=矢量化(A,B)
,然后检查x1和list(x2)。我得到
x1=[459571526477309498504,4529,…
x2=[29,29,29,29,29,29,29,29,29,29,29,29,…
,还有一个用于计算单个输入中所有对之间的距离的方法。但是它看起来比这里的解决方案慢了一点。@Salvadodali我不认为
scipy.spatial.distance.cdist
会给你与余弦距离相同的结果。要验证,请查看mininum distances,而不仅仅是这两种方法的min参数。我发布了一个带有
scipy.spatial.distance.cdist
的版本,该版本对随机输入返回相同的结果,但速度较慢。我无法访问a.npy或B.npy,因此无法在那里检查结果。如果你使用我发布的内容,你可能需要修改它才能将其提交给dea我会按照你想要的方式和NaN在一起。