在不到一秒内计算巨大矩阵的范数:NUMPY,PYTHON

2 投票
2 回答
967 浏览
提问于 2025-04-18 15:40

我有一个很大的矩阵,大小是(1,000,000,100),也就是有一百万行和一百列。我想计算每一行的“范数”,也就是每一行的长度。目前我使用的方法是:

import numpy
a =  numpy.random.rand((1000000,100))
b = numpy.linalg.norm(a, axis =1) # this takes 2.78 seconds.

有没有什么办法可以让计算这些范数的时间更短呢?

2 个回答

2

简单来说,普通的规范化是一个受内存限制的问题,用numpy处理起来效率不高。要提高效率,使用numpy的办法就是进行一些数据分块,避免多次读取同样的数据:

import numpy as np
a =  np.random.rand(1000000,100)
print np.linalg.norm(a, axis =1).shape

def g(d, out=None):
    bs = 2000
    if out is None:
        r = np.empty(d.shape[0])
    else:
        r = out
    for i in range(0, d.shape[0], bs):
        u = min(i + bs, d.shape[0])
        r[i:u] = np.linalg.norm(d[i:u], axis=1)
    return r


print  (g(a) == numpy.linalg.norm(a, axis =1)).all()
print "blocked"
%timeit -n 10 g(a)
print "normal"
%timeit -n 10 numpy.linalg.norm(a, axis =1) 

在我的机器上(DDR2内存)使用numpy 1.9,这样做能带来一些小的提升:

blocked
10 loops, best of 3: 294 ms per loop
normal
10 loops, best of 3: 561 ms per loop

理论上,增加一层使用线程的分块应该能进一步提高性能,但在我的机器上(glibc 2.19),这会导致过多的页面错误,因为glibc在多线程模式下频繁调整内存,这样反而没有帮助。

strace -f  -e madvise ipython test_threaded.ipy 2>&1  | grep MADV_DONTNEED -c
14228
2

使用更小的 dtype 类型;

In [8]: a = numpy.random.rand(1000,1000)

In [9]: %timeit numpy.linalg.norm(a, axis=1)
100 loops, best of 3: 10.4 ms per loop

In [11]: b = numpy.asarray(a, numpy.float32)

In [12]: %timeit numpy.linalg.norm(b, axis=1)
100 loops, best of 3: 3.5 ms per loop

根据你使用的numpy版本,使用一个优化过的或并行化的LAPACK库可能也会有所帮助。

在一台运行FreeBSD 10 amd64的Intel Core2 Quad (Q9300)上使用numpy,我得到了:

In [14]: a =  numpy.random.rand(1000000,100)

In [15]: %timeit -n 10 numpy.linalg.norm(a, axis =1)
10 loops, best of 3: 1.08 s per loop

还有一种加速的方法,前提是你有一台多核的机器;

  • 把数组写入一个文件
  • 写一个函数,从这个文件中读取某一行,计算所有数字的平方和,并返回结果。
  • 使用 multiprocessing.Pool.map() 在多个进程中运行这个函数。
  • map() 返回时,把结果列表中的数字加起来,然后对这个和取平方根。

加速效果大约等于可用核心的数量。

撰写回答