如何改进Python代码中的循环



我正在将这段代码从Matlab翻译成Python。代码运行良好,但在python中速度慢得令人痛苦。在Matlab中,代码以不到一分钟的方式运行,在python中,它花了30分钟!!!有python模式经验的人可以帮我吗?

# P({ai})
somai = 0
for i in range(1, n):
somaj = 0
for j in range(1, n):
exponencial = math.exp(-((a[i] - a[j]) * (a[i] - a[j])) / dev_a2 - ((b[i] - b[j]) * (b[i] - b[j])) / dev_b2)
somaj = somaj + exponencial
somai = somai + somaj

与MATLAB一样,我建议您对代码进行矢量化。循环的迭代可能比MATLAB和numpy的低级别实现慢得多。


您的操作(a[i] - a[j])*(a[i] - a[j])是所有N数据点的成对平方欧几里得距离。您可以使用scipy的pdistsquareform函数计算成对距离矩阵——pdist,squareform。

然后计算成对距离矩阵AB之间的差,并对指数衰减求和。所以你可以得到一个矢量化的代码,比如:

import numpy as np
from scipy.spatial.distance import pdist
from scipy.spatial.distance import squareform
# Example data
N = 1000
a = np.random.rand(N,1)
b = np.random.rand(N,1)
dev_a2 = np.random.rand()
dev_b2 = np.random.rand()
# `a` is an [N,1] matrix (i.e. column vector)
A = pdist(a, 'sqeuclidean')
# Change to pairwise distance matrix
A = squareform(A)
# Divide all elements by same divisor
A = A / dev_a2
# Then do the same for `b`'s
# `b` is an [N,1] matrix (i.e. column vector)
B = pdist(b, 'sqeuclidean')
B = squareform(B)
B = B / dev_b2
# Calculate exponential decay
expo = np.exp(-(A-B))
# Sum all elements
total = np.sum(expo)

下面是迭代方法和矢量化代码之间的快速时序比较。

N: 1000 | Iter Output: 2729989.851117 | Vect Output: 2732194.924364
Iter time: 6.759 secs | Vect time: 0.031 secs
N: 5000 | Iter Output: 24855530.997400 | Vect Output: 24864471.007726
Iter time: 171.795 secs | Vect time: 0.784 secs

请注意,最终结果并不完全相同我不知道为什么会这样,可能是我的舍入错误或数学错误,但我将留给你。

TLDR

使用numpy

为什么麻木

默认情况下,Python速度较慢。python的强大之处之一是它能很好地使用C语言,并且拥有大量的库。能帮你听的是numpy。Numpy主要是用C语言实现的,如果使用得当,速度会很快。诀窍是用这样一种方式表达代码,即保持numpy内部和python外部的执行。

代码和结果

import math
import numpy as np
n = 1000
np_a = np.random.rand(n)
a = list(np_a)
np_b = np.random.rand(n)
b = list(np_b)
dev_a2, dev_b2 = (1, 1)
def old():
somai = 0.0
for i in range(0, n):
somaj = 0.0
for j in range(0, n):
tmp_1 = -((a[i] - a[j]) * (a[i] - a[j])) / dev_a2
tmp_2 = -((b[i] - b[j]) * (b[i] - b[j])) / dev_b2
exponencial = math.exp(tmp_1 + tmp_2)
somaj += exponencial
somai += somaj
return somai

def new():
tmp_1 = -np.square(np.subtract.outer(np_a, np_a)) / dev_a2
tmp_2 = -np.square(np.subtract.outer(np_b, np_b)) / dev_a2
exponential = np.exp(tmp_1 + tmp_2)
somai = np.sum(exponential)
return somai

旧=每个环路1.76 s±48.3 ms(7次运行的平均值±标准偏差,每次1个环路(
新=每个环路24.6 ms±66.1µs这大约是70倍的改进
old产量为740919.6020840995
new产量为7409-19.602084099

解释

您会注意到,为了清晰起见,我用tmp_1tmp_2对您的代码进行了一些分解。

np.random.rand(n):这创建了一个长度为n的数组,该数组具有从0到1(不包括1(的随机浮点值(此处记录(。

np.subtract.outer(a, b):Numpy为所有运算符提供了模块,允许您使用它们执行各种操作。假设你有np_a = [1, 2, 3]np.subtract.outer(np_a, np_a)会产生

array([[ 0, -1, -2],
[ 1,  0, -1],
[ 2,  1,  0]])

如果你想更深入地了解这一点,这里有一个stackoverflow链接。("外"一词也来自"外积",类似于线性代数(

np.square:简单地对矩阵中的每个元素求平方。

/:在numpy中,当你在标量和矩阵之间进行算术运算时,它会做适当的事情,并将该运算应用于矩阵中的每个元素。

np.exp:类似np.square

np.sum:将每个元素相加,返回一个标量。

相关内容

  • 没有找到相关文章

最新更新