栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

numpy和matlab之间的性能差异

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

numpy和matlab之间的性能差异

说“ Matlab总是比NumPy更快”是错误的,反之亦然。通常他们的表现是可比的。使用NumPy时,要获得良好的性能,必须记住NumPy的速度来自调用用C
/ C ++ /
Fortran编写的基础函数。当您将这些函数应用于整个数组时,它的性能很好。通常,在Python循环中的较小数组或标量上调用那些NumPy函数时,性能会变差。

您问的Python循环有什么问题?Python循环中的每次迭代都是对

next
方法的调用。
[]
索引的每次使用都是对
__getitem__
方法的调用
。每个
+=
都是对的呼唤
__iadd__
。每个点分属性查找(例如like中的
np.dot
)都涉及函数调用。这些函数调用加起来大大阻碍了速度。这些钩子赋予Python强大的表达能力-
例如,为字符串建立索引与为dict建立索引的含义有所不同。相同的语法,不同的含义。通过为对象提供不同的
__getitem__
方法来实现魔术。

但是,这种表达能力的代价是速度。因此,当您不需要所有动态表达时,为了获得更好的性能,请尝试将自己限制为对整个数组进行NumPy函数调用。

因此,删除for循环;尽可能使用“向量化”公式。例如,代替

for i in range(m):    delta3 = -(x[i,:]-a3[i,:])*a3[i,:]* (1 - a3[i,:])

您可以一次

delta3
为每个计算
i

delta3 = -(x-a3)*a3*(1-a3)

而中的

for-loop
delta3
是向量,使用向量化方程式
delta3
是矩阵。


运算中的某些运算

for-loop
不依赖于运算,
i
因此应提升到循环之外。例如,
sum2
看起来像一个常量:

sum2 = sparse.beta*(-float(sparse.rho)/rhoest + float(1.0 - sparse.rho) / (1.0 - rhoest) )

这是一个可运行的示例,其中包含

alt
代码(
orig
)的替代实现()。

我的timeit基准测试显示 速度提高6.8倍

In [52]: %timeit orig()1 loops, best of 3: 495 ms per loopIn [53]: %timeit alt()10 loops, best of 3: 72.6 ms per loop

import numpy as npclass Bunch(object):    """ http://pre.activestate.com/recipes/52308 """    def __init__(self, **kwds):        self.__dict__.update(kwds)m, n, p = 10 ** 4, 64, 25sparse = Bunch(    theta1=np.random.random((p, n)),    theta2=np.random.random((n, p)),    b1=np.random.random((p, 1)),    b2=np.random.random((n, 1)),)x = np.random.random((m, n))a3 = np.random.random((m, n))a2 = np.random.random((m, p))a1 = np.random.random((m, n))sum2 = np.random.random((p, ))sum2 = sum2[:, np.newaxis]def orig():    partial_j1 = np.zeros(sparse.theta1.shape)    partial_j2 = np.zeros(sparse.theta2.shape)    partial_b1 = np.zeros(sparse.b1.shape)    partial_b2 = np.zeros(sparse.b2.shape)    delta3t = (-(x - a3) * a3 * (1 - a3)).T    for i in range(m):        delta3 = delta3t[:, i:(i + 1)]        sum1 = np.dot(sparse.theta2.T, delta3)        delta2 = (sum1 + sum2) * a2[i:(i + 1), :].T * (1 - a2[i:(i + 1), :].T)        partial_j1 += np.dot(delta2, a1[i:(i + 1), :])        partial_j2 += np.dot(delta3, a2[i:(i + 1), :])        partial_b1 += delta2        partial_b2 += delta3        # delta3: (64, 1)        # sum1: (25, 1)        # delta2: (25, 1)        # a1[i:(i+1),:]: (1, 64)        # partial_j1: (25, 64)        # partial_j2: (64, 25)        # partial_b1: (25, 1)        # partial_b2: (64, 1)        # a2[i:(i+1),:]: (1, 25)    return partial_j1, partial_j2, partial_b1, partial_b2def alt():    delta3 = (-(x - a3) * a3 * (1 - a3)).T    sum1 = np.dot(sparse.theta2.T, delta3)    delta2 = (sum1 + sum2) * a2.T * (1 - a2.T)    # delta3: (64, 10000)    # sum1: (25, 10000)    # delta2: (25, 10000)    # a1: (10000, 64)    # a2: (10000, 25)    partial_j1 = np.dot(delta2, a1)    partial_j2 = np.dot(delta3, a2)    partial_b1 = delta2.sum(axis=1)    partial_b2 = delta3.sum(axis=1)    return partial_j1, partial_j2, partial_b1, partial_b2answer = orig()result = alt()for a, r in zip(answer, result):    try:        assert np.allclose(np.squeeze(a), r)    except AssertionError:        print(a.shape)        print(r.shape)        raise

提示:
请注意,我在注释中保留了所有中间数组的形状。了解数组的形状有助于我理解您的代码在做什么。数组的形状可以帮助您指导正确的NumPy函数使用。或者至少,注意形状可以帮助您知道操作是否明智。例如,当您计算

np.dot(A, B)

A.shape = (n, m)
B.shape = (m, p)
,然后
np.dot(A, B)
将形状的阵列
(n, p)


它可以帮助以C_CONTIGUOUS顺序构建数组(至少,如果使用

np.dot
)。这样做最多可以将速度提高3倍:

下面,

x
是一样的
xf
,除了
x
是C_CONTIGUOUS并且
xf
是F_ConTIGUOUS -对于同样的关系
y
yf

import numpy as npm, n, p = 10 ** 4, 64, 25x = np.random.random((n, m))xf = np.asarray(x, order='F')y = np.random.random((m, n))yf = np.asarray(y, order='F')assert np.allclose(x, xf)assert np.allclose(y, yf)assert np.allclose(np.dot(x, y), np.dot(xf, y))assert np.allclose(np.dot(x, y), np.dot(xf, yf))

%timeit
基准显示速度差异:

In [50]: %timeit np.dot(x, y)100 loops, best of 3: 12.9 ms per loopIn [51]: %timeit np.dot(xf, y)10 loops, best of 3: 27.7 ms per loopIn [56]: %timeit np.dot(x, yf)10 loops, best of 3: 21.8 ms per loopIn [53]: %timeit np.dot(xf, yf)10 loops, best of 3: 33.3 ms per loop

关于Python基准测试:

在成对的

time.time()
调用中使用差异来基准化Python中的代码速度可能会产生误导。您需要多次重复测量。最好禁用自动垃圾收集器。测量较长的时间跨度(例如至少10秒的重复时间)也很重要,以避免由于时钟计时器的分辨率差而导致的错误,并减少
time.time
呼叫开销的重要性。Python为您提供了timeit模块,而不是您自己编写所有代码。我基本上是用它来计时代码片段,除了为方便起见,我是通过IPython终端调用它的。

我不确定这是否会影响您的基准测试,但请注意这可能会有所作为。在我链接到的问题中,根据

time.time
两段代码相差1.7倍,而使用
timeit
的基准测试表明,这两段代码的运行时间基本相同。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/639991.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号