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

14ifxucb  于 2023-04-06  发布在  Python
关注(0)|答案(2)|浏览(113)

我在一组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_matriximg_pix_mat都是42000 × 784矩阵,而img_total是标量。

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

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

以下论文的作者题为“NumPy数组:a structure for efficient numerical computation”(http://arxiv.org/pdf/1102.1523.pdf),在第4页的顶部指出,由于矢量化计算,他们观察到速度增加了500倍。我假设我看到的速度增加大部分是由于这一点。然而,我想更进一步,问为什么numpy矢量化计算比标准python循环快得多?
另外,也许你们知道第一种方法慢的其他原因。try/except结构的开销高吗?或者为每个循环创建一个新的np.array需要很长时间?
谢谢。

j13ufse2

j13ufse21#

由于numpy的内部工作原理,(据我所知,numpy内部使用C,所以你下推到numpy的所有东西实际上都要快得多,因为它是用不同的语言编写的)

编辑:

取出zip,并将其替换为vstack也会使其更快,(如果参数非常大,zip往往会变慢,而vstack更快;另外,vstack是numpy(因此是C),而zip是python)。
是的,如果我理解正确的话--不确定--你正在做42 k次的尝试/例外块。这肯定会对速度不利。

测试:

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

结果为(10,)
这意味着是的,如果你的矩阵是42 k乘784的矩阵,你正在尝试42 k次的try-except块。我假设这应该会对计算时间产生影响,以及每次执行zip,但不确定这是否是主要原因。
(So你运行你的东西的42 k次中的每一次,都需要0.17秒。我很确定try/except块不需要0.17秒,但也许它导致的开销或确实有助于它?)
尝试更改以下内容:

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语句是占用了你一分钟的时间还是将近两个小时的时间(我知道zipnumpy.vstack相比很慢,但不知道这是否会给你带来两个小时的时间增益。)

emeijp43

emeijp432#

你所看到的差异并不是因为SSE向量化之类的花哨。主要有两个原因。第一个是NumPy是用C编写的,C实现不必经历Python实现所经历的大量运行时方法调度和异常检查等。
第二个原因是,即使对于Python代码,基于循环的实现也是低效的。您在循环中使用vstack,每次调用vstack时,它都必须完全复制您传递给它的所有数组。这会为您的渐近复杂度增加len(data_matrix)的额外因素。

相关问题