15

我在一组 42000 张图像上实现了一个称为 TF-IDF 的加权系统,每张图像由 784 个像素组成。这基本上是一个 42000 x 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))

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

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

另外,也许你们知道第一种方法速度慢的其他原因。try/except 结构是否有高开销?或者也许为每个循环形成一个新的 np.array 需要很长时间?

谢谢。

4

2 回答 2

9

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

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

是的,如果我理解正确 - 不确定 - ,你正在做 42k 次 try/except 块,这肯定不利于速度,

测试:

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

结果 (10,)

这意味着是的,如果您的矩阵是 42kx784 矩阵,那么您正在尝试 42k 次 try-except 块,我假设这应该会影响计算时间,以及每次执行 zip 时,但不确定如果这是主要原因,

(所以你运行你的东西的每一次 42k 次,都需要 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 语句是否会占用你一分钟或近两个小时的时间(我知道 zip 与 numpy vstack 相比很慢,但不知道这是否会给你两个小时的时间增益)

于 2013-07-05T07:19:03.170 回答
8

您看到的差异不是由于像 SSE 矢量化这样的花哨的东西。有两个主要原因。首先是 NumPy 是用 C 编写的,C 实现不必像 Python 实现那样经历大量的运行时方法分派和异常检查等。

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

于 2013-07-05T07:18:33.697 回答