2017-05-31 30 views
5

Co jest najbardziej szybki sposób obliczyć funkcję jakszybki sposób obliczyć funkcję warunkowego

# here x is just a number 
def f(x): 
    if x >= 0: 
     return np.log(x+1) 
    else: 
     return -np.log(-x+1) 

Jednym z możliwych sposobów jest:

# here x is an array 
def loga(x) 
    cond = [x >= 0, x < 0] 
    choice = [np.log(x+1), -np.log(-x+1) 
    return np.select(cond, choice) 

Ale wydaje numpy przechodzi elementu tablicy przez elementu. Czy istnieje sposób użycia czegoś koncepcyjnie podobnego do np.exp (x) w celu uzyskania lepszej wydajności?

+0

Czy którekolwiek z zamieszczonych rozwiązań działają dla Ciebie? – Divakar

+0

@Divakar przepraszam, nie mam możliwości sprawdzenia, czy jeszcze. Spróbuję w najbliższym czasie i oznaczyć odpowiedź: – ichernob

Odpowiedz

6
def f(x): 
     return (x/abs(x)) * np.log(1+abs(x)) 
+0

Nice! To działa. Dobra robota na matematyce. Wraz z 'numexpr' i/lub ponownym użyciem' abs (x) 'powinien być zwycięzcą. – Divakar

+2

Proponuję użyć 'numpy.sign (x)' zamiast 'x/abs (x)', aby uniknąć możliwego podziału przez zero. 'np.copysign (np.log (1 + abs (x)), x)' byłby inny sposób. –

+0

Wszystko to jest całkiem niezłe. Ale funkcje takie jak abs(), np.sign() mogą zwracać 0. Rozwiązanie Marks jest naprawdę fajne, jednak chciałbym znaleźć rozwiązanie szybciej niż np.tanh (x). Czy to możliwe? – ichernob

3

W takich przypadkach, masking pomaga -

def mask_vectorized_app(x): 
    out = np.empty_like(x) 
    mask = x>=0 
    mask_rev = ~mask 
    out[mask] = np.log(x[mask]+1) 
    out[mask_rev] = -np.log(-x[mask_rev]+1) 
    return out 

Przedstawiamy numexpr module pomaga nam dalej.

import numexpr as ne 

def mask_vectorized_numexpr_app(x): 
    out = np.empty_like(x) 
    mask = x>=0 
    mask_rev = ~mask 

    x_masked = x[mask] 
    x_rev_masked = x[mask_rev] 
    out[mask] = ne.evaluate('log(x_masked+1)') 
    out[mask_rev] = ne.evaluate('-log(-x_rev_masked+1)') 
    return out 

Zainspirowany @user2685079's post a następnie za pomocą właściwości logarithmetic: log(A**B) = B*log(A) możemy wcisnąć Zapisz się do obliczeń dziennika, a to pozwala nam wykonać więcej pracy z numexpr „s oceny ekspresji, tak jak -

s = (-2*(x<0))+1 # np.sign(x) 
out = ne.evaluate('log((abs(x)+1)**s)') 

Computing sign stosując porównanie daje nam s w inny sposób -

s = (-2*(x<0))+1 

Wreszcie możemy pchnąć to do numexpr oceny ekspresji -

def mask_vectorized_numexpr_app2(x): 
    return ne.evaluate('log((abs(x)+1)**((-2*(x<0))+1))') 

testu Runtime

Loopy podejście do porównania -

def loopy_app(x): 
    out = np.empty_like(x) 
    for i in range(len(out)): 
     out[i] = f(x[i]) 
    return out 

synchronizacją czasową i weryfikacji -

In [141]: x = np.random.randn(100000) 
    ...: print np.allclose(loopy_app(x), mask_vectorized_app(x)) 
    ...: print np.allclose(loopy_app(x), mask_vectorized_numexpr_app(x)) 
    ...: print np.allclose(loopy_app(x), mask_vectorized_numexpr_app2(x)) 
    ...: 
True 
True 
True 

In [142]: %timeit loopy_app(x) 
    ...: %timeit mask_vectorized_numexpr_app(x) 
    ...: %timeit mask_vectorized_numexpr_app2(x) 
    ...: 
10 loops, best of 3: 108 ms per loop 
100 loops, best of 3: 3.6 ms per loop 
1000 loops, best of 3: 942 µs per loop 

Korzystanie @user2685079's solution użyciu np.sign zastąpić pierwszą część, a następnie bez numexpr oceny -

In [143]: %timeit np.sign(x) * np.log(1+abs(x)) 
100 loops, best of 3: 3.26 ms per loop 

In [144]: %timeit np.sign(x) * ne.evaluate('log(1+abs(x))') 
1000 loops, best of 3: 1.66 ms per loop 
+0

Czy możesz dodać moje do czasu? – piRSquared

+1

@piRSquared Nie mam 'numba'. Umysł dodając moje do twojego? – Divakar

+0

Dodano najnowszy 'app2' – piRSquared

1

można nieznacznie zwiększyć szybkość swojego drugiego roztworu za pomocą np.where zamiast np.select:

def loga(x): 
    cond = [x >= 0, x < 0] 
    choice = [np.log(x+1), -np.log(-x+1)] 
    return np.select(cond, choice) 

def logb(x): 
    return np.where(x>=0, np.log(x+1), -np.log(-x+1)) 

In [16]: %timeit loga(arange(-1000,1000)) 
10000 loops, best of 3: 169 µs per loop 

In [17]: %timeit logb(arange(-1000,1000)) 
10000 loops, best of 3: 98.3 µs per loop 

In [18]: np.all(loga(arange(-1000,1000)) == logb(arange(-1000,1000))) 
Out[18]: True 
2

Using numba

Numba daje Ci możliwość przyspieszenia działania aplikacji h wysokiej wydajności funkcje napisane bezpośrednio w Pythonie. Z kilkoma adnotacjami, zorientowany na tablicę i ciężki w matematyce kod Pythona może być dokładnie na czas skompilowany do natywnych instrukcji maszynowych, podobnych do C, C++ i Fortran, bez konieczności przełączania języków i interpreterów Pythona.

Program Numba generuje zoptymalizowany kod maszyny, korzystając z infrastruktury kompilatora LLVM w czasie importu, w czasie wykonywania lub statycznie (przy użyciu dołączonego narzędzia Pycc).Numba wspiera kompilację Pythona, aby działał na sprzęcie CPU lub GPU i został zaprojektowany do integracji ze stosem oprogramowania naukowego Python.

Projekt Numba jest obsługiwany przez Continuum Analytics oraz Fundację Gordona i Betty Moore (Grant GBMF5423).

from numba import njit 
import numpy as np 

@njit 
def pir(x): 
    a = np.empty_like(x) 
    for i in range(a.size): 
     x_ = x[i] 
     _x = abs(x_) 
     a[i] = np.sign(x_) * np.log(1 + _x) 
    return a 

Dokładność definicje

np.isclose(pir(x), f(x)).all() 

True 

Timing

x = np.random.randn(100000) 

# My proposal 
%timeit pir(x) 
1000 loops, best of 3: 881 µs per loop 

# OP test 
%timeit f(x) 
1000 loops, best of 3: 1.26 ms per loop 

# Divakar-1 
%timeit mask_vectorized_numexpr_app(x) 
100 loops, best of 3: 2.97 ms per loop 

# Divakar-2 
%timeit mask_vectorized_numexpr_app2(x) 
1000 loops, best of 3: 621 µs per loop 

Funkcja

from numba import njit 
import numpy as np 

@njit 
def pir(x): 
    a = np.empty_like(x) 
    for i in range(a.size): 
     x_ = x[i] 
     _x = abs(x_) 
     a[i] = np.sign(x_) * np.log(1 + _x) 
    return a 

import numexpr as ne 

def mask_vectorized_numexpr_app(x): 
    out = np.empty_like(x) 
    mask = x>=0 
    mask_rev = ~mask 

    x_masked = x[mask] 
    x_rev_masked = x[mask_rev] 
    out[mask] = ne.evaluate('log(x_masked+1)') 
    out[mask_rev] = ne.evaluate('-log(-x_rev_masked+1)') 
    return out 

def mask_vectorized_numexpr_app2(x): 
    return ne.evaluate('log((abs(x)+1)**((-2*(x<0))+1))') 


def f(x): 
    return (x/abs(x)) * np.log(1+abs(x)) 
+0

Moja ostatnia aplikacja2 jest nieco inna :) Przepraszamy za kłopot. Czy mógłbyś zaktualizować? Nie myśl, że musisz ponownie je wymienić. – Divakar

+1

@Divakar bez problemu :-). Ten ostatni jest naprawdę szybki. – piRSquared