2012-05-24 25 views
19

La funzione sigmoidale è definita comealgoritmo sigma veloce

enter image description here

ho trovato che utilizzando il C funzione incorporata exp() per calcolare il valore di f(x) è lento. Esiste un algoritmo più veloce per calcolare il valore di f(x)?

risposta

15

non è necessario utilizzare la funzione sigmoide effettiva, esatta in un algoritmo di rete neurale, ma può sostituirlo con una versione approssimativa che ha proprietà simili ma è più veloce il calcolo.

Ad esempio, è possibile utilizzare la funzione "fast sigma"

f(x) = x/(1 + abs(x)) 

Utilizzando primi termini di sviluppo in serie per exp (x) non aiuterà troppo se gli argomenti di f (x) sono non vicino allo zero e si ha lo stesso problema con una espansione in serie della funzione sigmoid se gli argomenti sono "grandi".

Un'alternativa è utilizzare la ricerca tabella. Cioè, si precalcolano i valori della funzione sigmoid per un dato numero di punti dati, e quindi si interpola rapidamente (lineare) tra loro, se lo si desidera.

-1

Non penso che si possa fare meglio del built-in exp() ma se si desidera un altro approccio, è possibile utilizzare l'espansione in serie. WolframAlpha puoi calcolarlo per te.

13

È consigliabile misurare prima l'hardware. Solo un rapido benchmark script mostra che sulla mia macchina 1/(1+|x|) è il più veloce, e tanh(x) è il secondo più vicino. Anche la funzione di errore erf è abbastanza veloce.

% gcc -Wall -O2 -lm -o sigmoid-bench{,.c} -std=c99 && ./sigmoid-bench 
atan(pi*x/2)*2/pi 24.1 ns 
atan(x)    23.0 ns 
1/(1+exp(-x))  20.4 ns 
1/sqrt(1+x^2)  13.4 ns 
erf(sqrt(pi)*x/2) 6.7 ns 
tanh(x)    5.5 ns 
x/(1+|x|)   5.5 ns 

mi aspetto che i risultati possono variare a seconda dell'architettura e del compilatore utilizzato, ma erf(x) (dal C99), tanh(x) e x/(1.0+fabs(x)) sono suscettibili di essere gli esecutori veloci.

+1

Supponiamo anche che tu intenda dire 'x/sqrt (1 + x^2)' invece di '1/sqrt (1 + x^2)'. – pqn

6

Per fare NN più flessibile di solito usato un certo tasso alfa per modificare l'angolo di grafico di circa 0.

La funzione sigmoide assomiglia:

f(x) = 1/(1+exp(-x*alpha)) 

La funzione quasi equivalente, (ma più veloce) è:

f(x) = 0.5 * (x * alpha/(1 + abs(x*alpha))) + 0.5 

È possibile controllare i grafici here

Quando uso la funzione abs la rete diventa più veloce più di 100 volte.

+0

Dove si suppone che la prima parentesi rotonda si chiuda nella seconda equazione? –

+0

Risolto, vedere in linea. – Nosyara

4

Questa risposta probabilmente non è rilevante per la maggior parte dei casi, ma volevo solo buttar fuori che per CUDA computing ho trovato x/sqrt(1+x^2) la funzione più veloce di gran lunga.

Per esempio, fatto con intrinseche galleggiante singola precisione:

__device__ void fooCudaKernel(/* some arguments */) { 
    float foo, sigmoid; 
    // some code defining foo 
    sigmoid = __fmul_rz(rsqrtf(__fmaf_rz(foo,foo,1)),foo); 
} 
+0

Buono. Anche se sarebbe solo se si calcolano i neuroni come una Matrice completamente connessa, non un vettore per una singola riga/matrice sparsa. – user1496062

1

Utilizzando Eureqa per cercare approssimazioni a sigma ho trovato 1/(1 + 0.3678749025^x) approssima esso.È abbastanza vicino, basta sbarazzarsi di un'operazione con la negazione di x.

Alcune delle altre funzioni mostrate qui sono interessanti, ma l'operazione di alimentazione è davvero così lenta? L'ho testato e in realtà è più veloce dell'aggiunta, ma potrebbe essere solo un colpo di fortuna. Se è così dovrebbe essere altrettanto veloce o più veloce di tutti gli altri.

EDIT: 0.5 + 0.5*tanh(0.5*x) e meno preciso, 0.5 + 0.5*tanh(n) funziona anche. E potresti semplicemente sbarazzarti delle costanti se non ti interessa prenderlo tra il range [0,1] come sigmoid. Ma presume che tanh sia più veloce.

+0

Il termine power è generalmente lento per eseguire yes, quindi questa approssimazione non evita quell'aspetto della domanda originale, dal momento che pow() sarà spesso implementato nei circuiti della CPU come adattamento a un'esecuzione/valutazione di exp(). – redcalx

8

Le persone qui sono principalmente preoccupate della velocità con cui una funzione è relativa a un'altra e creano micro benchmark per vedere se f1(x) esegue 0,0001 ms più veloce di f2(x). Il grosso problema è che questo è per lo più irrilevante, perché ciò che conta è la velocità con cui la rete impara con la funzione di attivazione cercando di ridurre al minimo la funzione di costo.

Al teoria corrente, rectifier function and softplusenter image description here

rispetto a funzione sigmoide o simili funzioni di attivazione, consentono più veloce e formazione efficace di profonde architetture neurali su grandi e complessi set di dati.

Quindi suggerisco di eliminare la micro-ottimizzazione e dare un'occhiata a quale funzione consente un apprendimento più rapido (anche considerando le varie altre funzioni di costo).

3

Inoltre è possibile utilizzare la versione grezza di sigma (IT differenze non superiore a 0,2% da originale):

inline float RoughSigmoid(float value) 
    { 
     float x = ::abs(value); 
     float x2 = x*x; 
     float e = 1.0f + x + x2*0.555f + x2*x2*0.143f; 
     return 1.0f/(1.0f + (value > 0 ? 1.0f/e : e)); 
    } 

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst) 
    { 
     float s = slope[0]; 
     for (size_t i = 0; i < size; ++i) 
      dst[i] = RoughSigmoid(src[i] * s); 
    } 

Ottimizzazione della funzione RoughSigmoid con l'utilizzo di SSE:

#include <xmmintrin.h> 

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst) 
    { 
     size_t alignedSize = size/4*4; 
     __m128 _slope = _mm_set1_ps(*slope); 
     __m128 _0 = _mm_set1_ps(-0.0f); 
     __m128 _1 = _mm_set1_ps(1.0f); 
     __m128 _0555 = _mm_set1_ps(0.555f); 
     __m128 _0143 = _mm_set1_ps(0.143f); 
     size_t i = 0; 
     for (; i < alignedSize; i += 4) 
     { 
      __m128 _src = _mm_loadu_ps(src + i); 
      __m128 x = _mm_andnot_ps(_0, _mm_mul_ps(_src, _slope)); 
      __m128 x2 = _mm_mul_ps(x, x); 
      __m128 x4 = _mm_mul_ps(x2, x2); 
      __m128 series = _mm_add_ps(_mm_add_ps(_1, x), _mm_add_ps(_mm_mul_ps(x2, _0555), _mm_mul_ps(x4, _0143))); 
      __m128 mask = _mm_cmpgt_ps(_src, _0); 
      __m128 exp = _mm_or_ps(_mm_and_ps(_mm_rcp_ps(series), mask), _mm_andnot_ps(mask, series)); 
      __m128 sigmoid = _mm_rcp_ps(_mm_add_ps(_1, exp)); 
      _mm_storeu_ps(dst + i, sigmoid); 
     } 
     for (; i < size; ++i) 
      dst[i] = RoughSigmoid(src[i] * slope[0]); 
    } 

Ottimizzazione della funzione RoughSigmoid con l'utilizzo di AVX:

#include <immintrin.h> 

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst) 
    { 
     size_t alignedSize = size/8*8; 
     __m256 _slope = _mm256_set1_ps(*slope); 
     __m256 _0 = _mm256_set1_ps(-0.0f); 
     __m256 _1 = _mm256_set1_ps(1.0f); 
     __m256 _0555 = _mm256_set1_ps(0.555f); 
     __m256 _0143 = _mm256_set1_ps(0.143f); 
     size_t i = 0; 
     for (; i < alignedSize; i += 8) 
     { 
      __m256 _src = _mm256_loadu_ps(src + i); 
      __m256 x = _mm256_andnot_ps(_0, _mm256_mul_ps(_src, _slope)); 
      __m256 x2 = _mm256_mul_ps(x, x); 
      __m256 x4 = _mm256_mul_ps(x2, x2); 
      __m256 series = _mm256_add_ps(_mm256_add_ps(_1, x), _mm256_add_ps(_mm256_mul_ps(x2, _0555), _mm256_mul_ps(x4, _0143))); 
      __m256 mask = _mm256_cmp_ps(_src, _0, _CMP_GT_OS); 
      __m256 exp = _mm256_or_ps(_mm256_and_ps(_mm256_rcp_ps(series), mask), _mm256_andnot_ps(mask, series)); 
      __m256 sigmoid = _mm256_rcp_ps(_mm256_add_ps(_1, exp)); 
      _mm256_storeu_ps(dst + i, sigmoid); 
     } 
     for (; i < size; ++i) 
      dst[i] = RoughSigmoid(src[i] * slope[0]); 
    } 
1

La funzione tanh può essere ottimizzata i In alcune lingue, rendendolo più veloce di una x/(1 + abs (x) personalizzata), come nel caso di Julia.

Problemi correlati