搜狐技术产品

Kaggle知识点:使用Numba加速代码

Numba介绍

https://numba.pydata.org/

Numba是一个用于优化Python函数的库,它利用行业标准的LLVM编译器库,在运行时将Python函数转换为优化的机器码。使用Numba编译的数值算法在Python中可以接近C或FORTRAN的速度。

Numba不需要替换Python解释器,运行额外的编译步骤,甚至安装C/C++编译器。只需将Numba的一个装饰器应用于Python函数,剩下的工作将由Numba完成。

Numba使用场景

  • 并行计算:Numba提供了一系列选项,可以将代码并行化在CPU和GPU上运行,通常只需要进行少量的代码更改。使用Numba的并行功能,可以将计算在多个CPU核心上自动执行,并且编写并行循环非常简单。

  • 分布式计算:Numba与分布式执行框架(例如Dask和Spark)非常兼容。通过结合Numba和这些框架,可以在分布式计算环境中高效地执行数值计算任务。

  • GPU加速:Numba支持NVIDIA CUDA,可以完全使用Python编写并行GPU算法。通过利用GPU的并行计算能力,可以加速复杂的计算任务,特别是涉及大规模数据处理和深度学习等领域。

Numba与Pandas案例

首先我们来创建一个模拟数据,数据存储了多列。接下来一些例子将使用这个数据来对比处理速度。

import pandas as pd
import numpy as np

data = np.random.rand(int(1e5), 5)

df = pd.DataFrame(data=data, columns=list("ABCDE"))
df["Type"] = np.random.choice(["Class1","Class2"], size=(len(df)))

案例1: 设置engine

在这个案例中,我们比较了使用不同的引擎(engine)进行计算所需要的时间。结果显示,使用Numba引擎进行计算只需要大约5.62毫秒的时间。与默认引擎相比,Numba引擎显著提高了性能。

%time out = rolling_df.mean()
# CPU times: user 14.1 ms, sys: 402 µs, total: 14.5 ms

%time out = rolling_df.mean(engine='cython')
# CPU times: user 12.6 ms, sys: 0 ns, total: 12.6 ms

%time out = rolling_df.mean(engine='numba')
# CPU times: user 5.62 ms, sys: 2.26 ms, total: 7.88 ms

案例2:自定义函数

我们可以将自定义函数编译为高效的机器码,以提高计算速度。在这个案例中,使用Numba引擎可以将计算时间缩短到原来的一半左右。

def custom_mean(x):
    return (x * x).mean()

    %time out = rolling_df.apply(custom_mean, raw=True)
# CPU times: user 2.89 s, sys: 392 µs, total: 2.89 s

%time out = rolling_df.apply(custom_mean, engine='cython', raw=True)
# CPU times: user 2.88 s, sys: 3.62 ms, total: 2.89 s

%time out = rolling_df.apply(custom_mean, engine='numba', raw=True)
# CPU times: user 1.23 s, sys: 117 µs, total: 1.23 s

案例3:Numba jit

通过使用jit装饰器,我们可以将普通的Python函数即时编译为优化的机器码,以提高计算速度。在这个案例中,使用Numba的即时编译可以将计算时间缩短到原来的一半左右,使得函数的执行更加高效。

from numba import jit, njit, vectorize, float64

def custom_mean(x):
    return (x * x).mean()

@jit(cache=True)
def custom_mean_jitted(x):
    return (x * x).mean()

    %time out = rolling_df.apply(custom_mean, raw=True)
# CPU times: user 2.9 s, sys: 143 µs, total: 2.9 s

%time out = rolling_df.apply(custom_mean_jitted, raw=True)
# CPU times: user 951 ms, sys: 0 ns, total: 951 ms

%time out = rolling_df.apply(custom_mean, engine='cython', raw=True)
# CPU times: user 2.87 s, sys: 3.87 ms, total: 2.87 s

%time out = rolling_df.apply(custom_mean_jitted, engine='cython', raw=True)
# CPU times: user 856 ms, sys: 0 ns, total: 856 ms

%time out = rolling_df.apply(custom_mean, engine='numba', raw=True)
# CPU times: user 1.57 s, sys: 48 µs, total: 1.57 s

%time out = rolling_df.apply(custom_mean_jitted, engine='numba', raw=True)
# CPU times: user 1.5 s, sys: 0 ns, total: 1.5 s

案例4:Python Loop

通过使用jit装饰器,我们可以将Python中的循环即时编译为优化的机器码,以提高计算速度。

from numba import jit, njit, vectorize, float64

def custom_mean(x):
    return (x * x).mean()

@jit(float64(float64[:]), nopython=True, cache=True)
def custom_mean_loops_jitted(x):
    out = 0.0
    for i in x:
        out += (i*i)
    return out / len(x)

    %time out = rolling_df.apply(custom_mean, raw=True)
# CPU times: user 2.9 s, sys: 7.83 ms, total: 2.9 s

%time out = rolling_df.apply(custom_mean, engine='numba', raw=True)
# CPU times: user 1.21 s, sys: 3.93 ms, total: 1.21 s

%time out = rolling_df.apply(custom_mean_loops_jitted, raw=True)
# CPU times: user 689 ms, sys: 0 ns, total: 689 ms