解释numpy的矢量化函数应用程序VS python for for循环之间的速度差异

时间:2013-07-05 07:07:45

标签: python performance loops numpy vectorization

我在一组42000张图像上实现了一个名为TF-IDF的加权系统,每个图像包含784个像素。这基本上是42000乘784矩阵。

我尝试的第一种方法是使用显式循环并且超过2小时

def tfidf(color,img_pix,img_total):
    if img_pix==0:
        return 0
    else:
        return color * np.log(img_total/img_pix)

...

result = np.array([])
for img_vec in data_matrix:
    double_vec = zip(img_vec,img_pix_vec)
    result_row = np.array([tfidf(x[0],x[1],img_total) for x in double_vec])
    try:
        result = np.vstack((result,result_row))
    # first row will throw a ValueError since vstack accepts rows of same len
    except ValueError:
        result = result_row

我试图使用numpy矩阵和的第二种方法花了不到5分钟。请注意,data_matrix,img_pix_mat都是42000 x 784矩阵,而img_total是标量。

result = data_matrix * np.log(np.divide(img_total,img_pix_mat))

我希望有人能解释速度上的巨大差异

以下论文的作者题为“NumPy数组:高效数值计算的结构”(http://arxiv.org/pdf/1102.1523.pdf),在第4页的顶部说明,由于矢量化计算,它们观察到500倍的速度增加。我假设我看到的速度增加很大程度上是由于这个原因。但是,我想更进一步,问为什么numpy向量化计算比标准python循环快得多?

另外,也许你们可能知道为什么第一种方法很慢的其他原因。尝试/除结构有高开销吗?或者为每个循环形成一个新的np.array需要很长时间?

感谢。

2 个答案:

答案 0 :(得分:8)

由于numpy的内部工作原理,(据我所知,numpy在内部使用C语言,所以你所有的numpy实际上要快得多,因为它使用不同的语言)

编辑: 取出zip,并用vstack替换它也应该更快,(如果参数非常大,拉链往往会变慢,而且vstack更快),(但这也是把它变成numpy的东西(因此进入C),而zip是python)

并且是的,如果我理解正确 - 不确定 - 你正在尝试42k次尝试/除了块,这肯定会对速度不利,

试验:

T=numpy.ndarray((5,10))
for t in T:
print t.shape

结果为(10,)

这意味着是的,如果您的矩阵是42kx784矩阵,那么您尝试42k次尝试除外块,我假设应该在计算时间内产生效果,以及每次都做一个拉链,但不确定这是否是主要原因,

(所以每运行42k次你的东西,需要0.17秒,我很确定try / except块不需要0.17秒,但是它可能造成的开销大约是否有所帮助?< / p>

尝试更改以下内容:

double_vec = zip(img_vec,img_pix_vec)
result_row = np.array([tfidf(x[0],x[1],img_total) for x in double_vec])

result_row=np.array([tfidf(img_vec[i],img_pix_vec[i],img_total) for i in xrange(len(img_vec))])

至少摆脱了zip语句,但不确定zip语句是否会缩短你的时间一分钟,或者差不多两个小时(我知道zip很慢,与numpy vstack相比,但是没有任何线索,如果那样会给你两个小时的时间收益)

答案 1 :(得分:7)

您所看到的差异不是由于像SSE矢量化这样的任何幻想。主要原因有两个。第一个是NumPy是用C语言编写的,而C实现不需要经历大量的运行时方法调度和异常检查等等,Python实现就会完成。

第二个原因是即使对于Python代码,基于循环的实现也是低效的。您在循环中使用vstack,并且每次调用vstack时,它都必须完全复制您传递给它的所有数组。这为您的渐近复杂性增加了len(data_matrix)的额外因子。