如何加速Python中的嵌套for?

如何加速Python中的嵌套for?,python,multithreading,numpy,scipy,multiprocessing,Python,Multithreading,Numpy,Scipy,Multiprocessing,我有一个包含描述符(数组)的列表,需要计算两者之间的余弦距离,这需要大量计算。对于带有n向量的列表,它执行n*(n-1)/2操作。我需要在一个大的n值中这样做,那么,如何使用多处理来加速这个过程呢?我发现了,但不清楚我该怎么办 我想加速的代码如下: 从scipy.spatial.distance导入余弦作为余弦距离 n=len(所有_向量) 所有_距离=[] 对于范围(n)中的i: 对于范围(i+1,n)内的j: x1=所有_向量[i] x2=所有_向量[j] 所有距离。追加(余弦距离(x1,x

我有一个包含描述符(数组)的列表,需要计算两者之间的余弦距离,这需要大量计算。对于带有
n
向量的列表,它执行
n*(n-1)/2
操作。我需要在一个大的
n
值中这样做,那么,如何使用多处理来加速这个过程呢?我发现了,但不清楚我该怎么办

我想加速的代码如下:

从scipy.spatial.distance导入余弦作为余弦距离
n=len(所有_向量)
所有_距离=[]
对于范围(n)中的i:
对于范围(i+1,n)内的j:
x1=所有_向量[i]
x2=所有_向量[j]
所有距离。追加(余弦距离(x1,x2))
[更新]

我还需要做一些标签检查,因此,原始代码如下所示:

Y = np.random.randn(100, 25)
从scipy.spatial.distance导入余弦作为余弦距离
n=len(所有_向量)
所有_距离=[]
所有标签检查=[]
对于范围(n)中的i:
对于范围(i+1,n)内的j:
x1=所有_向量[i]
x2=所有_向量[j]
所有距离。追加(余弦距离(x1,x2))
label_x1=所有_标签[i]#所有_标签和所有_距离都绑定
label_x2=所有_标签[j]
所有标签检查。追加(int(标签x1==标签x2))
我测试了一些建议的时间,到目前为止最好的答案是@DaniMesejo。下面是我用来测试每个案例的代码:

导入时间
将numpy作为np导入
从scipy.spatial.distance导入pdist,余弦作为余弦距离,平方形式
从itertools导入组合,星图
嵌入尺寸=128
N_嵌入=1024
def get_n_嵌入(n):
返回np.random.randn(n,嵌入大小)
嵌入=获取n_嵌入(n_嵌入)
###################
#scipy pdist
###################
开始=时间。时间()
pdist_距离=pdist(嵌入“余弦”)
end=time.time()
打印(f“使用scipy pdsit:{end-start}”)
###################
#嵌套循环
###################
嵌套的_距离=[]
开始=时间。时间()
对于范围内的i(N_嵌入):
对于范围内的j(i+1,N_嵌入):
x1=嵌入件[i]
x2=嵌入件[j]
嵌套的_距离。追加(余弦_距离(x1,x2))
end=time.time()
打印(f“使用嵌套循环:{end-start}”)
###################
#组合
###################
开始=时间。时间()
组合距离=[]
对于x1、x2组合(嵌入件,2):
组合距离。追加(余弦距离(x1,x2))
end=time.time()
打印(f“使用组合:{end-start}”)
###################
#星图
###################
开始=时间。时间()
星图距离=列表(星图(余弦距离,组合(嵌入,2)))
end=time.time()
打印(f“使用星图:{end-start}”)
打印(np.allclose(pdist_距离、嵌套_距离))
打印(np.allclose(pdist\U距离,pdist\U类距离))
打印(np.allclose(pdist_距离、组合_距离))
打印(np.allclose(pdist\u距离、星图\u距离))
结果是:

使用scipy pdsit:0.0303647518157959
使用pdsit和类:0.09841656684875488
使用嵌套循环:13.415924549102783
使用组合:13.093504428863525
使用星图:13.1774830817627
真的
真的
真的
真的
为了解决标签问题,我还可以使用
pdist
。我只需要将我的标签列表(告诉每个嵌入的标签)转换成一个2d列表,并计算成对距离。相同标签的成对距离为0:

###################
#pdist与类
###################
类别=[]
计数=0
id=0
对于范围内的i(N_嵌入):
class.append(id\ux)
计数+=1
如果计数%4==0:
计数=0
id+=1
标签_x=[(i,i)表示类中的i]
开始=时间。时间()
pdist_类_距离=pdist(嵌入“余弦”)
pdist_labels=pdist(labels_x,'euclidean')
pdist_标签=[1如果x==0.0,则为0,否则为pdist_标签中的x]
end=time.time()
打印(f“使用pdsit和类:{end-start}”)

我不能说这会有多大帮助,但您的嵌套循环基本上是一种尝试,使所有唯一的2个合法索引组合获得所有唯一的2个值组合。但在这里,使用itertools导入组合的
,这:

for i in range(n):
    for j in range(i + 1, n):
        x1 = all_vectors[i]
        x2 = all_vectors[j]
可以简化为:

for x1, x2 in combinations(all_vectors, 2):
您甚至可以对所有这些进行分层,将整个循环推到C层(假设余弦距离本身是用C实现的;如果不是,它只是限制了上行,但仍然可以正常工作),将所有发布的代码简化为:

from scipy.spatial.distance import cosine as cosine_distance
from itertools import combinations, starmap

all_distances = list(starmap(cosine_distance, combinations(all_vectors, 2)))
启动时运行更快(如果余弦距离是用C实现的成本相对较低的操作,则意味着更快)


如果这还不够快,那么很可能您必须使用
多处理
/
并发.futures.ProcessPoolExecutor
来并行化(如果
scipy
函数是在C中实现的,并且在运行时释放GIL,允许真正的线程并行,则可能是
multi-processing.dummy
/
concurrent.futures.ThreadPoolExecutor
,因为GIL,CPython上的CPU绑定线程通常不可用).

我不能说这会有多大帮助,但您的嵌套循环基本上是尝试创建所有唯一的2个合法索引组合,以获得所有唯一的2个值组合。但是,使用itertools导入组合的
,这:

for i in range(n):
    for j in range(i + 1, n):
        x1 = all_vectors[i]
        x2 = all_vectors[j]
可以简化为:

for x1, x2 in combinations(all_vectors, 2):
您甚至可以对所有这些进行分层,将整个循环推到C层(假设余弦距离本身是用C实现的;如果不是,它只是限制了上行,但仍然可以正常工作),将所有发布的代码简化为:

from scipy.spatial.distance import cosine as cosine_distance
from itertools import combinations, starmap

all_distances = list(starmap(cosine_distance, combinations(all_vectors, 2)))
启动时运行更快(如果余弦距离是用C实现的成本相对较低的操作,则意味着更快)

如果这还不够快,很可能您必须使用
多处理
/
并发.futures.ProcessPoolExecutor
True