(обновляется с измерениями производительности) (обновляется снова с реальными результатами:)
Я думаю, что решение с таблицей поиска поможет вам значительно повысить производительность при минимальных затратах памяти и точности.
Следующий фрагмент является примером реализации на C (я не говорю на c # достаточно бегло, чтобы засушить его). Он работает и работает достаточно хорошо, но я уверен, что в нем есть ошибка:)
#include <math.h>
#include <stdio.h>
#include <time.h>
#define SCALE 320.0f
#define RESOLUTION 2047
#define MIN -RESOLUTION / SCALE
#define MAX RESOLUTION / SCALE
static float sigmoid_lut[RESOLUTION + 1];
void init_sigmoid_lut(void) {
int i;
for (i = 0; i < RESOLUTION + 1; i++) {
sigmoid_lut[i] = (1.0 / (1.0 + exp(-i / SCALE)));
}
}
static float sigmoid1(const float value) {
return (1.0f / (1.0f + expf(-value)));
}
static float sigmoid2(const float value) {
if (value <= MIN) return 0.0f;
if (value >= MAX) return 1.0f;
if (value >= 0) return sigmoid_lut[(int)(value * SCALE + 0.5f)];
return 1.0f-sigmoid_lut[(int)(-value * SCALE + 0.5f)];
}
float test_error() {
float x;
float emax = 0.0;
for (x = -10.0f; x < 10.0f; x+=0.00001f) {
float v0 = sigmoid1(x);
float v1 = sigmoid2(x);
float error = fabsf(v1 - v0);
if (error > emax) { emax = error; }
}
return emax;
}
int sigmoid1_perf() {
clock_t t0, t1;
int i;
float x, y = 0.0f;
t0 = clock();
for (i = 0; i < 10; i++) {
for (x = -5.0f; x <= 5.0f; x+=0.00001f) {
y = sigmoid1(x);
}
}
t1 = clock();
printf("", y); /* To avoid sigmoidX() calls being optimized away */
return (t1 - t0) / (CLOCKS_PER_SEC / 1000);
}
int sigmoid2_perf() {
clock_t t0, t1;
int i;
float x, y = 0.0f;
t0 = clock();
for (i = 0; i < 10; i++) {
for (x = -5.0f; x <= 5.0f; x+=0.00001f) {
y = sigmoid2(x);
}
}
t1 = clock();
printf("", y); /* To avoid sigmoidX() calls being optimized away */
return (t1 - t0) / (CLOCKS_PER_SEC / 1000);
}
int main(void) {
init_sigmoid_lut();
printf("Max deviation is %0.6f\n", test_error());
printf("10^7 iterations using sigmoid1: %d ms\n", sigmoid1_perf());
printf("10^7 iterations using sigmoid2: %d ms\n", sigmoid2_perf());
return 0;
}
Предыдущие результаты были связаны с тем, что оптимизатор выполнил свою работу и оптимизировал расчеты. Выполнение кода на самом деле дает немного другие и гораздо более интересные результаты (на моем пути медленный MB Air):
$ gcc -O2 test.c -o test && ./test
Max deviation is 0.001664
10^7 iterations using sigmoid1: 571 ms
10^7 iterations using sigmoid2: 113 ms
TODO:
Есть вещи для улучшения и способы устранения слабых мест; как это сделать, оставлено в качестве упражнения для читателя:)
- Настройте диапазон функции, чтобы избежать перехода туда, где начинается и заканчивается таблица.
- Добавьте функцию небольшого шума, чтобы скрыть артефакты наложения.
- Как сказал Рекс, интерполяция может сделать вас немного более точным с точки зрения производительности при довольно низкой производительности.