Python 向量化函数的麻烦
对于一个项目,我需要从函数生成样本。我希望能够尽快生成这些样本 我有这个例子(在最终版本中,参数中将提供Python 向量化函数的麻烦,python,numpy,vectorization,Python,Numpy,Vectorization,对于一个项目,我需要从函数生成样本。我希望能够尽快生成这些样本 我有这个例子(在最终版本中,参数中将提供函数lambda),目标是使用lambda函数在开始和停止之间生成n个点的ys def get_ys(系数,num_输出=20,开始=0,停止=1): 函数=lambda x,args:args[0]*(x-args[1])**2+args[2]*(x-args[3])+args[4] linspace(开始、停止、num=num_输出、端点=True) ys=[x中x的函数(x,系数)] 返
函数
lambda),目标是使用lambda函数
在开始
和停止
之间生成n个点的ys
def get_ys(系数,num_输出=20,开始=0,停止=1):
函数=lambda x,args:args[0]*(x-args[1])**2+args[2]*(x-args[3])+args[4]
linspace(开始、停止、num=num_输出、端点=True)
ys=[x中x的函数(x,系数)]
返回Y
%%次
n=1000
xs=np.random.random((n,5))
ys=np。沿_轴应用_(获取_ys,1,xs)
墙壁时间:616毫秒
我正在尝试将其矢量化,并找到numpy.apply\u沿\u轴
%%次
对于范围(1000)内的i:
xs=np.random.random(5)
ys=获取_ys(xs)
墙壁时间:622毫秒
不幸的是,它仍然相当缓慢:/
我不太熟悉函数矢量化,有人可以指导我如何提高脚本的速度吗
谢谢
编辑:
输入/输出示例:
xs = np.ones(5)
ys = get_ys(xs)
[1.0, 0.9501385041551247, 0.9058171745152355, 0.8670360110803323, 0.8337950138504155,0.8060941828254848, 0.7839335180055402, 0.7673130193905817, 0.7562326869806094, 0.7506925207756232, 0.7506925207756232, 0.7562326869806094, 0.7673130193905817, 0.7839335180055401, 0.8060941828254847, 0.8337950138504155, 0.8670360110803323, 0.9058171745152354, 0.9501385041551246, 1.0]
您试图绕过调用get_ys
1000次,每行xs
一次
要将xs
作为一个整体传递到get_ys
需要什么?换句话说,如果系数是(n,5)而不是(5,)呢
xs
是(20,),而ys
将是相同的(对吗)
lambda的写入要求标量x
和(5,)args。可以将其更改为与(20,)x和(n,5)args一起使用吗
作为第一步,如果给定xs
,那么函数会产生什么?而不是
ys = [function(x, coefficients) for x in xs]
ys = function(xs, coefficients)
编写时,您的代码(以较慢的Python速度)迭代n(1000)行和20linspace
。因此,函数
被调用20000次。这就是你的代码变慢的原因
让我们试试改变吧
函数的示例运行:
In [126]: np.array(get_ys(np.arange(5)))
Out[126]:
array([-2. , -1.89473684, -1.78947368, -1.68421053, -1.57894737,
-1.47368421, -1.36842105, -1.26315789, -1.15789474, -1.05263158,
-0.94736842, -0.84210526, -0.73684211, -0.63157895, -0.52631579,
-0.42105263, -0.31578947, -0.21052632, -0.10526316, 0. ])
只需调用一次函数
,即可替换列表理解:
In [127]: def get_ys1(coefficients, num_outputs=20, start=0., stop=1.):
...: function = lambda x, args: args[0]*(x-args[1])**2 + args[2]*(x-args[3]) + args[4]
...:
...: xs = np.linspace(start, stop, num=num_outputs, endpoint=True)
...: ys = function(xs, coefficients)
...: return ys
...:
...:
相同值:
In [128]: get_ys1(np.arange(5))
Out[128]:
array([-2. , -1.89473684, -1.78947368, -1.68421053, -1.57894737,
-1.47368421, -1.36842105, -1.26315789, -1.15789474, -1.05263158,
-0.94736842, -0.84210526, -0.73684211, -0.63157895, -0.52631579,
-0.42105263, -0.31578947, -0.21052632, -0.10526316, 0. ])
比较时间:
In [129]: timeit np.array(get_ys(np.arange(5)))
345 µs ± 16.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [130]: timeit get_ys1(np.arange(5))
89.2 µs ± 162 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
这就是我们所说的“矢量化”——用一个等价物取代python级迭代(列表理解),使numpy
数组方法的用户更充分
我想我们可以继续使用(n,5)系数,但这应该足以让您开始
完全矢量化
通过广播
将(n,5)与(20,)进行对比,我可以得到一个没有任何python循环的函数:
def get_ys2(coefficients, num_outputs=20, start=0., stop=1.):
function = lambda x, args: args[:,0]*(x-args[:,1])**2 + args[:,2]*(x-args[:,3]) + args[:,4]
xs = np.linspace(start, stop, num=num_outputs, endpoint=True)
ys = function(xs[:,None], coefficients)
return ys.T
并使用(1,5)输入:
使用您的测试用例:
In [146]: n = 1000
...: xs = np.random.random((n,5))
...: ys = np.apply_along_axis(get_ys, 1, xs)
In [147]: ys.shape
Out[147]: (1000, 20)
两个时间安排:
In [148]: timeit ys = np.apply_along_axis(get_ys, 1, xs)
...:
106 ms ± 303 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [149]: timeit ys = np.apply_along_axis(get_ys1, 1, xs)
...:
88 ms ± 98.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
并对此进行了测试
In [150]: ys2 = get_ys2(xs)
In [151]: ys2.shape
Out[151]: (1000, 20)
In [152]: np.allclose(ys, ys2)
Out[152]: True
In [153]: timeit ys2 = get_ys2(xs)
424 µs ± 484 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
它匹配值,并大大提高了速度
在新函数中,args
现在可以是(n,5)。如果x
为(20,1),则结果为(20,n),我在返回时将其转置。沿_轴应用
是一种方便的方法,可以将只需要1d数组的函数应用于3d(或更大)数组。对于2d,只在一维上迭代更简单。无论如何,它不是一个速度工具,因此也不是我们通常所说的“矢量化”。“矢量化”通常意味着重写函数,使其与二维数组一起工作,使用与多维数组一起工作的numpy
方法。没有捷径可走。你必须学习numpy
基础知识。另一种说法是-apply
仍然调用get_ys
1000次。它不会编译它或以其他方式使它更快。@Gulzar,它可以用给定的代码运行。e、 g.get_ys(np.arange(5))
返回20个值的列表。这不是一个调试问题,而是一个重写问题。@hpaulj哦,我不知道,我以为它会矢量化,我的错。我尝试在函数上使用numpy.vectorize
,但它每次插入一个元素而不是一行,因此我认为需要使用此函数。np.vectorize
传递标量值,而不是行。它有一个性能免责声明<代码>应用…
也应该有一个。这非常令人印象深刻,非常感谢!我仍在努力理解你在回答中所做的一切,包括广播、转置和创建新的axis
In [148]: timeit ys = np.apply_along_axis(get_ys, 1, xs)
...:
106 ms ± 303 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [149]: timeit ys = np.apply_along_axis(get_ys1, 1, xs)
...:
88 ms ± 98.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [150]: ys2 = get_ys2(xs)
In [151]: ys2.shape
Out[151]: (1000, 20)
In [152]: np.allclose(ys, ys2)
Out[152]: True
In [153]: timeit ys2 = get_ys2(xs)
424 µs ± 484 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)