programing

NumPy 배열의 모든 셀에서 함수의 효율적인 평가

closeapi 2023. 7. 25. 20:59
반응형

NumPy 배열의 모든 셀에서 함수의 효율적인 평가

NumPy 배열 A가 주어지면, 모든 동일한 기능 f를 적용하는 가장 빠르고 효율적인 방법은 무엇입니까?

  1. A(i,j)에 f(A(i,j)를 할당한다고 가정합니다.

  2. 함수 f에는 이진 출력이 없으므로 마스크(ing) 작업이 도움이 되지 않습니다.

(모든 셀을 통해) "명백한" 이중 루프 반복이 최적의 솔루션입니까?

함수를 벡터화한 다음 필요할 때마다 Numpy 배열에 직접 적용할 수 있습니다.

import numpy as np

def f(x):
    return x * x + 3 * x - 2 if x > 0 else x * 5 + 8

f = np.vectorize(f)  # or use a different name if you want to keep the original f

result_array = f(A)  # if A is your Numpy array

벡터화할 때는 명시적 출력 유형을 직접 지정하는 것이 좋습니다.

f = np.vectorize(f, otypes=[np.float])

비슷한 질문은 NumPy 배열을 제자리에 매핑하는 입니다.f()에 대한 ufunc를 찾을 수 있으면 out 매개변수를 사용해야 합니다.

만약 당신이 숫자와 관련된 일을 하고 있다면 그리고.f(A(i,j)) = f(A(j,i))당신은 f를 사이의 거리로 정의하는 scipy.distance.cdist를 사용할 수 있습니다.A(i)그리고.A(j).

저는 제가 더 나은 해결책을 찾았다고 믿습니다.함수를 후드 아래에서 병렬 계산을 실행할 수 있는 파이썬 범용 함수(설명서 참조)로 변경하는 아이디어.

맞춤형으로 작성할 수 있습니다.ufunc에서, 확히더효에인적율서써, ▁invoking써▁in를 호출함으로써.np.frompyfunc내장된 공장 방식입니다.에, 은 트후스다, 은다더효다니입적율보보다 더 입니다.np.vectorize:

f = lambda x, y: x * y
f_arr = np.frompyfunc(f, 2, 1)
vf = np.vectorize(f)
arr = np.linspace(0, 1, 10000)

%timeit f_arr(arr, arr) # 307ms
%timeit f_arr(arr, arr) # 450ms

저는 더 큰 샘플도 테스트해 보았는데, 개선은 비례합니다.다른 방법의 성능 비교는 이 게시물을 참조하십시오.

2d 배열(ord-array)이 C- 또는 F-연속일 때, 함수를 2d 배열에 매핑하는 이 작업은 실질적으로 함수를 1d 배열에 매핑하는 작업과 같습니다. 예를 들어, 를 통해 이러한 방식으로 보기만 하면 됩니다.

1D 어레이에 대한 가능한 솔루션은 예를 들어 여기에서 논의되었습니다.

그러나 2D 어레이의 메모리가 연속되지 않으면 축이 잘못된 순서로 처리될 경우 발생할 수 있는 캐시 누락을 방지하기 위해 상황이 조금 더 복잡해집니다.

Numpy는 이미 최선의 순서로 축을 처리할 수 있는 기계를 갖추고 있습니다.이 기계를 사용할 수 있는 한 가지 가능성은 다음과 같습니다.np.vectorize그러나 Numpy의 문서에는 "성능이 아닌 편의를 위해 주로 제공된다"고 명시되어 있습니다. - 느린 파이썬 함수는 전체 관련 오버헤드와 함께 느린 파이썬 함수로 남아 있습니다!또 다른 문제는 메모리 소비가 크다는 것입니다. 예를 들어 이 SO 포스트를 참조하십시오.

C 함수의 성능을 원하지만 numpy의 기계를 사용하려면 다음과 같은 ufuncs 생성을 위해 numba를 사용하는 것이 좋습니다.

# runtime generated C-function as ufunc
import numba as nb
@nb.vectorize(target="cpu")
def nb_vf(x):
    return x+2*x*x+4*x*x*x

그것은 쉽게 뜁니다.np.vectorize또한 동일한 기능이 numpy-array 곱셈/함수로 수행될 때도 마찬가지입니다.

# numpy-functionality
def f(x):
    return x+2*x*x+4*x*x*x

# python-function as ufunc
import numpy as np
vf=np.vectorize(f)
vf.__name__="vf"

시간 측정 코드는 이 답변의 부록을 참조하십시오.

enter image description here

Numba 버녹 (색)은 python-function (즉, python-function)보다 약 보다 약 100배 빠릅니다.np.vectorize), 그러나 numbas 버전은 중간 어레이가 필요하지 않으므로 캐시를 더 효율적으로 사용하기 때문에 numpy 기능보다 약 10배 더 빠릅니다.


Numba의 ufunc 접근 방식은 사용성과 성능 사이의 좋은 균형이지만, 여전히 우리가 할 수 있는 최선은 아닙니다.그러나 어떤 작업에도 최선의 방법이나 방법은 없습니다. 한계가 무엇인지, 한계를 어떻게 완화할 수 있는지 이해해야 합니다.

를 들어, 함수 예를들어예, 함수월초예(함수▁(:exp,sin,cosnumpy's) numba보다 .np.exp(일시적 배열이 생성되지 않음 - 속도 향상의 주요 원인).그러나 내 아나콘다 설치는 8192보다 벡터에 Intel의 VML을 사용합니다. 메모리가 연속되지 않으면 할 수 없습니다.따라서 Intel의 VML을 사용하려면 요소를 연속 메모리에 복사하는 것이 더 나을 수 있습니다.

import numba as nb
@nb.vectorize(target="cpu")
def nb_vexp(x):
    return np.exp(x)

def np_copy_exp(x):
    copy = np.ravel(x, 'K')
    return np.exp(copy).reshape(x.shape) 

비교의 공정성을 위해 VML의 병렬화 기능을 해제했습니다(부록의 코드 참조).

enter image description here

VML이 시작되면 복사 오버헤드가 보상 이상이 됩니다.그러나 데이터가 L3 캐시에 비해 너무 커지면 작업이 다시 메모리 대역폭에 바인딩되므로 이점은 미미합니다.

반면에 Numba는 Intel의 SVML도 사용할 수 있습니다.

from llvmlite import binding
# set before import
binding.set_option('SVML', '-vector-library=SVML')

import numba as nb

@nb.vectorize(target="cpu")
def nb_vexp_svml(x):
    return np.exp(x)

병렬화를 통해 VML을 사용하면 다음과 같은 이점을 얻을 수 있습니다.

enter image description here

Numba의 버전은 오버헤드가 적지만 일부 크기에서는 VML이 추가 복사 오버헤드에도 불구하고 SVML을 능가합니다. Numba의 ufuncs가 병렬화되지 않았기 때문에 조금도 놀랍지 않습니다.


목록:

A. 다항식 함수의 비교:

import perfplot
perfplot.show(
    setup=lambda n: np.random.rand(n,n)[::2,::2],
    n_range=[2**k for k in range(0,12)],
    kernels=[
        f,
        vf, 
        nb_vf
        ],
    logx=True,
    logy=True,
    xlabel='len(x)'
    ) 

의 비교exp:

import perfplot
import numexpr as ne # using ne is the easiest way to set vml_num_threads
ne.set_vml_num_threads(1)
perfplot.show(
    setup=lambda n: np.random.rand(n,n)[::2,::2],
    n_range=[2**k for k in range(0,12)],
    kernels=[
        nb_vexp, 
        np.exp,
        np_copy_exp,
        ],
    logx=True,
    logy=True,
    xlabel='len(x)',
    )

은 모두 잘 매핑을 사용자 해야 할 에는 사용자 정의 를 사용해야 합니다.numpy.ndarray배열의 모양을 유지해야 합니다.

나는 단지 두 개를 비교했지만, 그것은 그것의 모양을 유지할 것입니다.ndarray나는 비교를 위해 100만 개의 항목이 있는 배열을 사용했습니다.여기서 저는 제곱함수를 사용합니다.저는 n차원 배열에 대한 일반적인 사례를 발표합니다.2차원을 위해서 그냥 만들어 보세요.iter2차원으로

import numpy, time

def A(e):
    return e * e

def timeit():
    y = numpy.arange(1000000)
    now = time.time()
    numpy.array([A(x) for x in y.reshape(-1)]).reshape(y.shape)        
    print(time.time() - now)
    now = time.time()
    numpy.fromiter((A(x) for x in y.reshape(-1)), y.dtype).reshape(y.shape)
    print(time.time() - now)
    now = time.time()
    numpy.square(y)  
    print(time.time() - now)

산출량

>>> timeit()
1.162431240081787    # list comprehension and then building numpy array
1.0775556564331055   # from numpy.fromiter
0.002948284149169922 # using inbuilt function

여기서 당신은 분명히 볼 수 있습니다.numpy.fromiter사용자 제곱 함수를 사용합니다.기능이 다음에 종속된 경우i, j이며, 반복합니다. 배열 크기에 대해 반복합니다.for ind in range(arr.size),사용하다numpy.unravel_index갖기 위해i, j, ..1D 인덱스와 배열 numpy.unravel_index의 모양을 기반으로 합니다.

이 답변은 여기에 있는 다른 질문에 대한 제 답변에서 영감을 받았습니다.

언급URL : https://stackoverflow.com/questions/7701429/efficient-evaluation-of-a-function-at-every-cell-of-a-numpy-array

반응형