2010-08-20 64 views
3
private static double [] sigtab = new double[1001]; // values of f(x) for x values 

static { 
    for(int i=0; i<1001; i++) { 
     double ifloat = i; 
     ifloat /= 100; 
     sigtab[i] = 1.0/(1.0 + Math.exp(-ifloat)); 
    } 
} 

public static double fast_sigmoid (double x) { 
    if (x <= -10) 
     return 0.0; 
    else if (x >= 10) 
     return 1.0; 
    else { 
     double normx = Math.abs(x*100); 
     int i = (int)normx; 
     double lookup = sigtab[i] + (sigtab[i+1] - sigtab[i])*(normx - Math.floor(normx)); 
     if (x > 0) 
      return lookup; 
     else // (x < 0) 
      return (1 - lookup); 
    } 
} 

任何人都知道爲什麼這個「快速S形」實際上運行速度慢於使用Math.exp的確切版本?查找錶快速S形功能

回答

1

你的意思是在一個雙元素數組中查找並執行一些微積分應該比在現場計算它快嗎?

儘管CPU只有基本的操作,它可以很容易地處理指數。我會說少於5個基本操作。

你在這裏做的事很複雜,而且實際上需要去取內存中的某些元素。 64位* 1001肯定適合您的緩存,但緩存訪問時間肯定不符合註冊表訪問時間。

這種情況並沒有讓我感到驚訝。

1

你應該分析代碼,但我敢打賭,它的服用大約一半的CPU週期調用Math.floor(它是緩慢的,因爲它會調用本地方法StrictMath.floor(double),招致JNI開銷。)

可能比(精確的)硬件實現更快地計算(較不準確)S型函數版本。下面是tanh一個例子,它應該很容易轉換成你的函數(是expit(-x)?)正在這裏使用

兩個技巧往往是有用的基於LUT的近似值:

  • 模擬加入四捨五入大常數(迫使FPU將截斷它,太少的位來表示總和)
  • 讓您的表大小的2的冪(指每一個電話少乘)

public static float fastTanH(float x) { 
    if (x<0) return -fastTanH(-x); 
    if (x>8) return 1f; 
    float xp = TANH_FRAC_BIAS + x; 
    short ind = (short) Float.floatToRawIntBits(xp); 
    float tanha = TANH_TAB[ind]; 
    float b = xp - TANH_FRAC_BIAS; 
    x -= b; 
    return tanha + x * (1f - tanha*tanha); 
} 

private static final int TANH_FRAC_EXP = 6; // LUT precision == 2 ** -6 == 1/64 
private static final int TANH_LUT_SIZE = (1 << TANH_FRAC_EXP) * 8 + 1; 
private static final float TANH_FRAC_BIAS = 
    Float.intBitsToFloat((0x96 - TANH_FRAC_EXP) << 23); 
private static float[] TANH_TAB = new float[TANH_LUT_SIZE]; 
static { 
    for (int i = 0; i < TANH_LUT_SIZE; ++ i) { 
     TANH_TAB[i] = (float) Math.tanh(i/64.0); 
    } 
} 
+0

@Downvoter,如果你不同意'Math.floor'是一個瓶頸,[這裏是一個解釋(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6908131)。 – finnw 2011-01-30 15:16:33