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);
}
}
任何人都知道为什么这个“快速sigmoid”实际上比使用Math.exp的确切版本运行得慢?
答案 0 :(得分:1)
你的意思是查找一个双元素数组并执行一些微积分应该比当场计算更快吗?
尽管CPU只有基本操作,但它可以很容易地处理取幂。我会在不到5个基本操作中说。
你在这里做的事情有点复杂,需要实际上不得不去取内存中的一些元素。 64位* 1001肯定适合您的缓存,但缓存访问时间肯定与注册表访问时间不匹配。
这种情况至少不会让我感到惊讶。
答案 1 :(得分:1)
你应该对你的代码进行概要分析,但我敢打赌,Math.floor
调用大约一半的CPU周期(这很慢,因为它调用本机方法StrictMath.floor(double)
,导致JNI开销。 )
可能比(精确)硬件实现更快地计算(不太准确)sigmoid函数版本。以下是tanh
的示例,它应该很容易转换为您的函数(是expit(-x)
吗?)
此处使用的两个技巧通常在基于LUT的近似中很有用:
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);
}
}