如何加快NumPy配置文件的代码-Numba的矢量化? [英] How do I speed up profiled NumPy code - vectorizing, Numba?

查看:132
本文介绍了如何加快NumPy配置文件的代码-Numba的矢量化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在运行一个大型Python程序,以优化金融中(Markowitz)投资组合优化的投资组合权重.当我分析代码时,要花费90%的运行时间来计算投资组合的回报,这要进行数百万次.我该怎么做才能加快代码执行速度?我尝试过:

I am running a large Python program to optimize portfolio weights for (Markowitz) portfolio optimization in finance. When I Profile the code, 90% of the run time is spent calculating the portfolio return, which is done millions of times. What can I do to speed up my code? I have tried:

  • 向量化收益计算:使代码 slower 从1.5毫秒变为3毫秒
  • 使用了Numba的autojit函数来加速代码:无需更改
  • vectorizing the calculation of returns: made the code slower, from 1.5 ms to 3 ms
  • used the function autojit from Numba to speed up the code: no change

请参见下面的示例-有什么建议吗?

See example below - any suggestions?

import numpy as np


def get_pf_returns(weights, asset_returns, horizon=60):
    '''
    Get portfolio returns: Calculates portfolio return for N simulations,
    assuming monthly rebalancing.

    Input
    -----
    weights: Portfolio weight for each asset
    asset_returns: Monthly returns for each asset, potentially many simulations
    horizon: 60 months (hard-coded)

    Returns
    -------
    Avg. annual portfolio return for each simulation at the end of 5 years
    '''
    pf = np.ones(asset_returns.shape[1])
    for t in np.arange(horizon):
        pf *= (1 + asset_returns[t, :, :].dot(weights))
    return pf ** (12.0 / horizon) - 1


def get_pf_returns2(weights, asset_returns):
    ''' Alternative '''
    return np.prod(1 + asset_returns.dot(weights), axis=0) ** (12.0 / 60) - 1

# Example
N, T, sims = 12, 60, 1000  # Settings
weights = np.random.rand(N)
weights *= 1 / np.sum(weights)  # Sample weights
asset_returns = np.random.randn(T, sims, N) / 100  # Sample returns

# Calculate portfolio risk/return
pf_returns = get_pf_returns(weights, asset_returns)
print np.mean(pf_returns), np.std(pf_returns)

# Timer
%timeit get_pf_returns(weights, asset_returns)
%timeit get_pf_returns2(weights, asset_returns)

编辑

解决方案:Matmul在我的机器上最快:

Solution: Matmul was fastest on my machine:

def get_pf_returns(weights, asset_returns):
    return np.prod(1 + np.matmul(asset_returns, weights), axis=0) ** (12.0 / 60) - 1

推荐答案

在我的环境中,与einsumdot相比,mutmul(@)具有适度的时间优势:

In my environment, mutmul (@) has a modest time advantage over einsum and dot:

In [27]: np.allclose(np.einsum('ijk,k',asset_returns,weights),asset_returns@weig
    ...: hts)
Out[27]: True
In [28]: %timeit asset_returns@weights
100 loops, best of 3: 3.91 ms per loop
In [29]: %timeit np.einsum('ijk,k',asset_returns,weights)
100 loops, best of 3: 4.73 ms per loop
In [30]: %timeit np.dot(asset_returns,weights)
100 loops, best of 3: 6.8 ms per loop

我认为时间受计算总数的限制,而不是编码细节.所有这些都将计算结果传递给已编译的numpy代码.您的原始循环版本相对较快的事实可能与循环数量少(只有60个循环)有关,以及更完整的dot中的内存管理问题.

I think times are limited by the total number of calculations, more than the coding details. All of these pass the calculation to compiled numpy code. The fact that your original looped version is relatively fast probably has to do with the small number of loops (only 60), and memory management issues in the fuller dot.

numba可能不会替换dot代码.

因此,在此处或此处进行的调整可能会将您的代码速度提高2倍,但不要指望数量级的改进.

So a tweak here or there might speed up your code by a factor of 2, but don't expect an order of magnitude improvement.

这篇关于如何加快NumPy配置文件的代码-Numba的矢量化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆