Математическая оптимизация в C#

  // Full Screen Shot function. Hope this will work well in swift.
  func screenShot() -> UIImage {                                                    
    UIGraphicsBeginImageContext(CGSizeMake(frame.size.width, frame.size.height))
    var context:CGContextRef  = UIGraphicsGetCurrentContext()
    self.view?.drawViewHierarchyInRect(frame, afterScreenUpdates: true)
    var screenShot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext();  
    return screenShot
  }
56
задан Salvador Dali 14 August 2015 в 05:48
поделиться

21 ответ

Попытка:

public static float Sigmoid(double value) {
    return 1.0f / (1.0f + (float) Math.Exp(-value));
}

РЕДАКТИРОВАНИЕ: я сделал быстрый сравнительный тест. На моей машине вышеупомянутый код приблизительно на 43% быстрее, чем Ваш метод, и этот математически эквивалентный код является битом teeniest быстрее (на 46% быстрее, чем оригинал):

public static float Sigmoid(double value) {
    float k = Math.Exp(value);
    return k / (1.0f + k);
}

РЕДАКТИРОВАНИЕ 2: я не уверен, сколько служебные функции C# имеют, но если Вы #include <math.h> в Вашем исходном коде, необходимо быть в состоянии использовать это, которое использует функцию плавающую-exp. Это могло бы быть немного быстрее.

public static float Sigmoid(double value) {
    float k = expf((float) value);
    return k / (1.0f + k);
}

Также при выполнении миллионов вызовов вызов функции наверху мог бы быть проблемой. Попытайтесь делать подставляемую функцию и посмотрите, является ли это какой-либо справкой.

55
ответ дан Sophie Alpert 26 November 2019 в 17:03
поделиться

Умеренное изменение на теме Сопрано:

public static float Sigmoid(double value) {
    float v = value;
    float k = Math.Exp(v);
    return k / (1.0f + k);
}

, Так как Вы только после результата одинарной точности, почему делают Математику. Функция Exp вычисляет двойное? Любой калькулятор экспоненты, который использует повторяющееся суммирование (см. расширение e <глоток> x ) займет больше времени у большей точности, каждый раз. И дважды дважды работа сингла! Таким образом, Вы преобразовываете в сингл сначала, тогда делают Ваш экспоненциал.

, Но функция expf должно быть быстрее все еще. Я не вижу, что потребность в (плавании) сопрано бросает мимоходом к expf, хотя, если C# не делает неявного двойного преобразования плавающего.

Иначе, просто используйте реальный язык, как ФОРТРАН...

1
ответ дан Phil H 26 November 2019 в 17:03
поделиться

Вы могли бы также рассмотреть экспериментирование с альтернативными функциями активации, которые являются более дешевыми для оценки. Например:

f(x) = (3x - x**3)/2

(который мог быть учтен как

f(x) = x*(3 - x*x)/2

для одного меньшего количества умножения). Эта функция имеет нечетную симметрию, и ее производная тривиальна. Используя его для нейронной сети требует нормализации суммы исходных данных путем деления на общее количество исходных данных (ограничивающий домен [-1.. 1], который является также диапазоном).

1
ответ дан joel.neely 26 November 2019 в 17:03
поделиться

Идея: Возможно, можно ли сделать (большую) таблицу поиска со значениями предварительно вычисленной?

2
ответ дан Vilx- 26 November 2019 в 17:03
поделиться

Это немного вне темы, но только из любопытства, я сделал ту же реализацию как та в C, C# и F# в Java. Я просто оставлю это здесь в случае, если кому-то еще любопытно.

Результат:

$ javac LUTTest.java && java LUTTest
Max deviation is 0.001664
10^7 iterations using sigmoid1() took 1398 ms
10^7 iterations using sigmoid2() took 177 ms

я предполагаю, что улучшение по сравнению с C# в моем случае происходит из-за Java, лучше оптимизируемого, чем Моно для OS X. На подобной реализации.NET MS (по сравнению с Java 6, если бы кто-то хочет отправить сравнительные числа) я предполагаю, что результаты отличались бы.

Код:

public class LUTTest {
    private static final float SCALE = 320.0f;
    private static final  int RESOLUTION = 2047;
    private static final  float MIN = -RESOLUTION / SCALE;
    private static final  float MAX = RESOLUTION / SCALE;

    private static final float[] lut = initLUT();

    private static float[] initLUT() {
        float[] lut = new float[RESOLUTION + 1];

        for (int i = 0; i < RESOLUTION + 1; i++) {
            lut[i] = (float)(1.0 / (1.0 + Math.exp(-i / SCALE)));
        }
        return lut;
    }

    public static float sigmoid1(double value) {
        return (float) (1.0 / (1.0 + Math.exp(-value)));
    }

    public static float sigmoid2(float value) {
        if (value <= MIN) return 0.0f;
        if (value >= MAX) return 1.0f;
        if (value >= 0) return lut[(int)(value * SCALE + 0.5f)];
        return 1.0f - lut[(int)(-value * SCALE + 0.5f)];
    }

    public static float error(float v0, float v1) {
        return Math.abs(v1 - v0);
    }

    public static float testError() {
        float emax = 0.0f;
        for (float x = -10.0f; x < 10.0f; x+= 0.00001f) {
            float v0 = sigmoid1(x);
            float v1 = sigmoid2(x);
            float e = error(v0, v1);
            if (e > emax) emax = e;
        }
        return emax;
    }

    public static long sigmoid1Perf() {
        float y = 0.0f;
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            for (float x = -5.0f; x < 5.0f; x+= 0.00001f) {
                y = sigmoid1(x);
            }
        }
        long t1 = System.currentTimeMillis();
        System.out.printf("",y);
        return t1 - t0;
    }    

    public static long sigmoid2Perf() {
        float y = 0.0f;
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            for (float x = -5.0f; x < 5.0f; x+= 0.00001f) {
                y = sigmoid2(x);
            }
        }
        long t1 = System.currentTimeMillis();
        System.out.printf("",y);
        return t1 - t0;
    }    

    public static void main(String[] args) {

        System.out.printf("Max deviation is %f\n", testError());
        System.out.printf("10^7 iterations using sigmoid1() took %d ms\n", sigmoid1Perf());
        System.out.printf("10^7 iterations using sigmoid2() took %d ms\n", sigmoid2Perf());
    }
}
2
ответ дан Community 26 November 2019 в 17:03
поделиться

Сначала мысль: Как насчет некоторой статистики на переменной значений?

  • значения "значения" обычно маленькие-10 < = оцените < = 10?

В противном случае можно ли, вероятно, получить повышение путем тестирования на за пределы значения

if(value < -10)  return 0;
if(value > 10)  return 1;
  • , значения часто повторяются?

Если так, можно, вероятно, извлечь некоторую пользу от Memoization (вероятно, не, но не повреждает проверять....)

if(sigmoidCache.containsKey(value)) return sigmoidCache.get(value);

, Если ни один из них не может быть применен, то, поскольку некоторые другие предложили, возможно, можно сойти с рук понижение точности сигмоидального...

4
ответ дан Stobor 26 November 2019 в 17:03
поделиться

Примечание: Это - продолжение этот сообщение.

Редактирование: Обновление для вычисления того же самого как это и это , беря некоторое вдохновение от это .

Теперь взгляд, что Вы заставили меня сделать! Вы заставили меня установить Моно!

$ gmcs -optimize test.cs && mono test.exe
Max deviation is 0.001663983
10^7 iterations using Sigmoid1() took 1646.613 ms
10^7 iterations using Sigmoid2() took 237.352 ms

C едва больше стоит усилия, мир продвигается:)

Так, только по фактору <забастовка> 10 6 быстрее. Кто-то с полем окон добирается для исследования использования памяти и производительности с помощью материала MS:)

Используя LUTs для функций активации не таким образом редкий, особенно, когда реализовано в аппаратных средствах. Существует много хорошо доказанных вариантов понятия там, если Вы готовы включать те типы таблиц. Однако, как были уже указаны, искажение могло бы оказаться проблемой, но существуют пути вокруг этого также. Некоторые дополнительные материалы для чтения:

Некоторые глюки с этим:

  • ошибка повышается, когда Вы достигаете вне таблицы (но сходится к 0 в экстремальных значениях); для x приблизительно +-7.0. Это происходит из-за выбранного масштабного коэффициента. Большие значения МАСШТАБА дают более высокие ошибки в среднем диапазоне, но меньший в краях.
  • Это обычно - очень глупый тест, и я не знаю C#, Это - просто простое преобразование моего C-кода:)
  • Rinat Abdullin очень корректен, что искажение и потеря точности могло бы вызвать проблемы, но так как я не видел переменные, за которые я могу только совет Вы для попытки этого. На самом деле я соглашаюсь со всем, что он говорит за исключением выпуска таблиц поиска.

Прощение кодирование вставки копии...

using System;
using System.Diagnostics;

class LUTTest {
    private const float SCALE = 320.0f;
    private const int RESOLUTION = 2047;
    private const float MIN = -RESOLUTION / SCALE;
    private const float MAX = RESOLUTION / SCALE;

    private static readonly float[] lut = InitLUT();

    private static float[] InitLUT() {
      var lut = new float[RESOLUTION + 1];

      for (int i = 0; i < RESOLUTION + 1; i++) {
        lut[i] = (float)(1.0 / (1.0 + Math.Exp(-i / SCALE)));
      }
      return lut;
    }

    public static float Sigmoid1(double value) {
        return (float) (1.0 / (1.0 + Math.Exp(-value)));
    }

    public static float Sigmoid2(float value) {
      if (value <= MIN) return 0.0f;
      if (value >= MAX) return 1.0f;
      if (value >= 0) return lut[(int)(value * SCALE + 0.5f)];
      return 1.0f - lut[(int)(-value * SCALE + 0.5f)];
    }

    public static float error(float v0, float v1) {
      return Math.Abs(v1 - v0);
    }

    public static float TestError() {
        float emax = 0.0f;
        for (float x = -10.0f; x < 10.0f; x+= 0.00001f) {
          float v0 = Sigmoid1(x);
          float v1 = Sigmoid2(x);
          float e = error(v0, v1);
          if (e > emax) emax = e;
        }
        return emax;
    }

    public static double TestPerformancePlain() {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 10; i++) {
            for (float x = -5.0f; x < 5.0f; x+= 0.00001f) {
                Sigmoid1(x);
            }
        }
        sw.Stop();
        return sw.Elapsed.TotalMilliseconds;
    }    

    public static double TestPerformanceLUT() {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 10; i++) {
            for (float x = -5.0f; x < 5.0f; x+= 0.00001f) {
                Sigmoid2(x);
            }
        }
        sw.Stop();
        return sw.Elapsed.TotalMilliseconds;
    }    

    static void Main() {
        Console.WriteLine("Max deviation is {0}", TestError());
        Console.WriteLine("10^7 iterations using Sigmoid1() took {0} ms", TestPerformancePlain());
        Console.WriteLine("10^7 iterations using Sigmoid2() took {0} ms", TestPerformanceLUT());
    }
}
5
ответ дан Community 26 November 2019 в 17:03
поделиться

F# Имеет Лучшую Производительность, чем C# в математических алгоритмах.NET. Настолько переписывающая нейронная сеть в F# могла бы улучшить общую производительность.

, Если мы повторно реализуем отрывок сравнительного тестирования LUT (я использовал немного настроенную версию) в F#, тогда получающийся код:

  • выполняет сравнительный тест sigmoid1 в [1 115], 588,8 мс вместо 3899,2 мс
  • выполняют sigmoid2 (LUT) сравнительный тест в [1 116] 156,6 мс вместо 411,4 мс
[еще 1110], детали могли быть найдены в сообщение в блоге . Вот является отрывок F# JIC:

#light

let Scale = 320.0f;
let Resolution = 2047;

let Min = -single(Resolution)/Scale;
let Max = single(Resolution)/Scale;

let range step a b =
  let count = int((b-a)/step);
  seq { for i in 0 .. count -> single(i)*step + a };

let lut = [| 
  for x in 0 .. Resolution ->
    single(1.0/(1.0 +  exp(-double(x)/double(Scale))))
  |]

let sigmoid1 value = 1.0f/(1.0f + exp(-value));

let sigmoid2 v = 
  if (v <= Min) then 0.0f;
  elif (v>= Max) then 1.0f;
  else
    let f = v * Scale;
    if (v>0.0f) then lut.[int (f + 0.5f)]
    else 1.0f - lut.[int(0.5f - f)];

let getError f = 
  let test = range 0.00001f -10.0f 10.0f;
  let errors = seq { 
    for v in test -> 
      abs(sigmoid1(single(v)) - f(single(v)))
  }
  Seq.max errors;

open System.Diagnostics;

let test f = 
  let sw = Stopwatch.StartNew(); 
  let mutable m = 0.0f;
  let result = 
    for t in 1 .. 10 do
      for x in 1 .. 1000000 do
        m <- f(single(x)/100000.0f-5.0f);
  sw.Elapsed.TotalMilliseconds;

printf "Max deviation is %f\n" (getError sigmoid2)
printf "10^7 iterations using sigmoid1: %f ms\n" (test sigmoid1)
printf "10^7 iterations using sigmoid2: %f ms\n" (test sigmoid2)

let c = System.Console.ReadKey(true);

И вывод (Компиляция выпуска против F# 1.9.6.2 CTP без отладчика):

Max deviation is 0.001664
10^7 iterations using sigmoid1: 588.843700 ms
10^7 iterations using sigmoid2: 156.626700 ms

ОБНОВЛЕНИЕ: обновленное сравнительное тестирование для использования 10^7 повторения для создания результатов сопоставимыми с C

UPDATE2: вот результаты проверки производительности реализация C от той же машины для сравнения:

Max deviation is 0.001664
10^7 iterations using sigmoid1: 628 ms
10^7 iterations using sigmoid2: 157 ms
5
ответ дан Community 26 November 2019 в 17:03
поделиться

Первое, что пришло на ум данная статья объясняет способ приблизить экспоненциал путем злоупотребления плавающей точки , (нажмите на ссылку в верхнем правом для PDF), но я не знаю, будет ли это иметь много применения для Вас в.NET.

кроме того, другая точка: в целях учебных больших сетей быстро, логистическое сигмоидальное, которое Вы используете, довольно ужасно. Посмотрите раздел 4.4 из Эффективный Backprop LeCun и др. и используйте что-то центрируемое нулем (на самом деле, прочитайте та целая газета, это очень полезно).

4
ответ дан dwf 26 November 2019 в 17:03
поделиться

FWIW, вот мои сравнительные тесты C# для ответов, уже отправленных. (Пустой функция, которая просто возвращается 0, для измерения вызова функции наверху)

Empty Function:       79ms   0
Original:             1576ms 0.7202294
Simplified: (soprano) 681ms  0.7202294
Approximate: (Neil)   441ms  0.7198783
Bit Manip: (martinus) 836ms  0.72318
Taylor: (Rex Logan)   261ms  0.7202305
Lookup: (Henrik)      182ms  0.7204863
public static object[] Time(Func<double, float> f) {
    var testvalue = 0.9456;
    var sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < 1e7; i++)
        f(testvalue);
    return new object[] { sw.ElapsedMilliseconds, f(testvalue) };
}
public static void Main(string[] args) {
    Console.WriteLine("Empty:       {0,10}ms {1}", Time(Empty));
    Console.WriteLine("Original:    {0,10}ms {1}", Time(Original));
    Console.WriteLine("Simplified:  {0,10}ms {1}", Time(Simplified));
    Console.WriteLine("Approximate: {0,10}ms {1}", Time(ExpApproximation));
    Console.WriteLine("Bit Manip:   {0,10}ms {1}", Time(BitBashing));
    Console.WriteLine("Taylor:      {0,10}ms {1}", Time(TaylorExpansion));
    Console.WriteLine("Lookup:      {0,10}ms {1}", Time(LUT));
}
8
ответ дан Jimmy 26 November 2019 в 17:03
поделиться

Если Вы в состоянии к interop с C++, Вы могли бы рассмотреть хранение всех значений в массиве и цикле по ним использующий SSE как это:

void sigmoid_sse(float *a_Values, float *a_Output, size_t a_Size){
    __m128* l_Output = (__m128*)a_Output;
    __m128* l_Start  = (__m128*)a_Values;
    __m128* l_End    = (__m128*)(a_Values + a_Size);

    const __m128 l_One        = _mm_set_ps1(1.f);
    const __m128 l_Half       = _mm_set_ps1(1.f / 2.f);
    const __m128 l_OneOver6   = _mm_set_ps1(1.f / 6.f);
    const __m128 l_OneOver24  = _mm_set_ps1(1.f / 24.f);
    const __m128 l_OneOver120 = _mm_set_ps1(1.f / 120.f);
    const __m128 l_OneOver720 = _mm_set_ps1(1.f / 720.f);
    const __m128 l_MinOne     = _mm_set_ps1(-1.f);

    for(__m128 *i = l_Start; i < l_End; i++){
        // 1.0 / (1.0 + Math.Pow(Math.E, -value))
        // 1.0 / (1.0 + Math.Exp(-value))

        // value = *i so we need -value
        __m128 value = _mm_mul_ps(l_MinOne, *i);

        // exp expressed as inifite series 1 + x + (x ^ 2 / 2!) + (x ^ 3 / 3!) ...
        __m128 x = value;

        // result in l_Exp
        __m128 l_Exp = l_One; // = 1

        l_Exp = _mm_add_ps(l_Exp, x); // += x

        x = _mm_mul_ps(x, x); // = x ^ 2
        l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_Half, x)); // += (x ^ 2 * (1 / 2))

        x = _mm_mul_ps(value, x); // = x ^ 3
        l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_OneOver6, x)); // += (x ^ 3 * (1 / 6))

        x = _mm_mul_ps(value, x); // = x ^ 4
        l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_OneOver24, x)); // += (x ^ 4 * (1 / 24))

#ifdef MORE_ACCURATE

        x = _mm_mul_ps(value, x); // = x ^ 5
        l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_OneOver120, x)); // += (x ^ 5 * (1 / 120))

        x = _mm_mul_ps(value, x); // = x ^ 6
        l_Exp = _mm_add_ps(l_Exp, _mm_mul_ps(l_OneOver720, x)); // += (x ^ 6 * (1 / 720))

#endif

        // we've calculated exp of -i
        // now we only need to do the '1.0 / (1.0 + ...' part
        *l_Output++ = _mm_rcp_ps(_mm_add_ps(l_One,  l_Exp));
    }
}

Однако помнят, что массивы, которые Вы будете использовать, должны быть выделены с помощью _aligned_malloc (some_size * sizeof (плавание), 16), потому что SSE требует памяти, выровненной к границе.

Используя SSE, я могу вычислить результат для всех 100 миллионов элементов приблизительно через половину секунды. Однако выделение так большой памяти за один раз будет стоить Вам почти двух третей гигабайта, таким образом, я предложил бы обработать больше, но меньшие массивы за один раз. Вы могли бы даже хотеть рассмотреть использование подхода двойной буферизации с 100K элементами или больше.

кроме того, если число элементов начинает расти значительно, Вы могли бы хотеть принять решение обработать эти вещи на GPU (просто создают 1D float4 структура и выполняют очень тривиальную программу построения теней фрагмента).

8
ответ дан Jasper Bekkers 26 November 2019 в 17:03
поделиться

В 100 миллионах вызовов я начал бы задаваться вопросом, не скашивает ли профилировщик наверху Ваши результаты. Замените вычисление не и посмотрите, ли это все еще , сообщил для потребления 60% времени выполнения...

Или еще лучше, создайте некоторые данные тестирования и используйте таймер секундомера для профилирования приблизительно миллиона вызовов.

8
ответ дан Shog9 26 November 2019 в 17:03
поделиться
  1. Помнят, это , любые изменения в этой функции активации происходят за счет различного поведения . Это даже включает переключение на плавание (и таким образом понижение точности) или использование замен активации. Только экспериментирование с Вашим вариантом использования покажет правильный путь.
  2. В дополнение к простой оптимизации кода, я также рекомендовал бы рассмотреть распараллеливание вычислений (т.е.: усиливать несколько ядер Вашей машины или даже машин в Windows Azure Clouds) и улучшение учебных алгоритмов.

ОБНОВЛЕНИЕ: Сообщение на таблицах поиска для функций активации ANN

UPDATE2: я удалил точку на LUTs, так как я перепутал их с полным хешированием. Спасибо переходит в Henrik Gustafsson для откладывания меня на дорожке. Таким образом, память не является проблемой, хотя пространство поиска все еще испорчено с локальным экстремальным значением немного.

14
ответ дан Community 26 November 2019 в 17:03
поделиться

Взгляните на это сообщение . это имеет приближение для e^x, записанного в Java, это должно быть кодом C# для (непротестированного):

public static double Exp(double val) {  
    long tmp = (long) (1512775 * val + 1072632447);  
    return BitConverter.Int64BitsToDouble(tmp << 32);  
}

В моих сравнительных тестах это [больше чем 117] в 5 раз быстрее, чем Math.exp () (в Java). Приближение основано на бумаге" А Быстрое, Компактное Приближение Показательной функции ", которая была разработана точно, чтобы использоваться в нейронных сетях. Это - в основном то же как таблица поиска записей 2048 года и линейной аппроксимации записей, но всего этого с приемами плавающей точки IEEE.

РЕДАКТИРОВАНИЕ: Согласно Специальный Соус это - ~3.25x быстрее, чем реализация CLR. Спасибо!

22
ответ дан martinus 26 November 2019 в 17:03
поделиться

Если это для функции активации, имеет значение ужасно очень, если вычисление e^x абсолютно точно?

, Например, при использовании приближения (1+x/256) ^256, на моем тестировании Pentium в Java (я принимаю, C# по существу компилирует в те же инструкции по процессору), это приблизительно в 7-8 раз быстрее, чем e^x (Math.exp ()) и с точностью до 2 десятичных разрядов до приблизительно x +/-1.5, и в правильном порядке величины через диапазон Вы заявили. (Очевидно, для повышения до этих 256 Вы на самом деле придаете временам номер 8 квадратную форму - не используют Математику. Голова для этого!) В Java:

double eapprox = (1d + x / 256d);
eapprox *= eapprox;
eapprox *= eapprox;
eapprox *= eapprox;
eapprox *= eapprox;
eapprox *= eapprox;
eapprox *= eapprox;
eapprox *= eapprox;
eapprox *= eapprox;

Продолжают удваиваться или сокращаться наполовину 256 (и добавлять/удалять умножение) в зависимости от того, как точный Вы хотите, чтобы приближение было. Даже с n=4, это все еще дает приблизительно 1,5 десятичных разряда точности для значений x beween-0.5 и 0.5 (и появляется польза в 15 раз быстрее, чем Math.exp ()).

P.S. Я забыл упоминать - Вы должны, очевидно, не действительно , делятся на 256: умножьтесь на постоянный 1/256. JIT-компилятор Java делает эту оптимизацию автоматически (по крайней мере, Горячая точка делает), и я предполагал, что C# должен сделать также.

30
ответ дан Neil Coffey 26 November 2019 в 17:03
поделиться

Сопрано имело некоторую хорошую оптимизацию Ваш вызов:

public static float Sigmoid(double value) 
{
    float k = Math.Exp(value);
    return k / (1.0f + k);
}

, Если Вы пробуете таблицу поиска и находите, она использует слишком много памяти, Вы, всегда могли смотря на значение Вашего параметра для каждого последовательные вызовы и используя некоторую кэширующуюся технику.

, Например, попытка, кэширующая последнее значение и результат. Если следующий вызов имеет то же значение как предыдущее, Вы не должны вычислять его, поскольку Вы кэшировали бы последний результат. Если бы текущий вызов совпал с предыдущим вызовом даже 1 из 100 времена, Вы могли потенциально сохранить себя 1 миллион вычислений.

Или, можно найти, что в рамках 10 последовательных вызовов, параметр, передаваемый по значению в среднем те же 2 раза, таким образом, Вы могли тогда попытаться кэшировать последние 10 значений/ответов.

4
ответ дан Jeremy 26 November 2019 в 17:03
поделиться

Здесь много хороших ответов. Я бы посоветовал выполнить его через эту технику , просто чтобы убедиться

  • , что вы не вызываете его больше раз, чем нужно.
    (Иногда функции вызываются чаще, чем необходимо, просто потому, что их так легко вызывать.)
  • Вы не вызываете его повторно с одними и теми же аргументами
    (где вы можете использовать мемоизацию)

Кстати, у вас есть функция обратного логита,
или обратная логарифмическая функция отношения шансов log (f / (1-f)) .

1
ответ дан 26 November 2019 в 17:03
поделиться

При необходимости в гигантском повышении скорости Вы могли бы, вероятно, изучить параллелизацию функции с помощью (GE) силу. IOW, используйте DirectX для управления видеокартой в выполнение его для Вас. Я понятия не имею, как сделать это, но я видел, что люди используют видеокарты для всех видов вычислений.

0
ответ дан erikkallen 26 November 2019 в 17:03
поделиться

1) Вы называете это только от одного места? Если так, можно получить небольшое количество производительности путем перемещения кода из той функции и просто исправления его, где Вы обычно вызывали бы Сигмоидальную функцию. Мне не нравится эта идея с точки зрения удобочитаемости кода и организации, но когда необходимо получить каждое последнее увеличение производительности, это могло бы помочь, потому что я думаю, что вызовы функции требуют нажатия/поп регистров на стеке, которого можно было избежать, если бы код был все встроен.

2) я понятия не имею, могло ли это помочь, но попытаться делать Ваш параметр функции касательно параметра. Посмотрите, быстрее ли это. Я предложил бы делать его константой (который будет оптимизацией, если это было в C++), но c# не поддерживает параметры константы.

0
ответ дан Jeremy 26 November 2019 в 17:03
поделиться

Делая поиск Google, я нашел альтернативную реализацию Сигмоидальной функции.

public double Sigmoid(double x)
{
   return 2 / (1 + Math.Exp(-2 * x)) - 1;
}

, который корректен для Ваших потребностей? Это быстрее?

http://dynamicnotions.blogspot.com/2008/09/sigmoid-function-in-c.html

0
ответ дан Haacked 26 November 2019 в 17:03
поделиться

(Обновленный с измерениями производительности) (Обновленный снова с реальными результатами:)

я думаю, что решение для таблицы поиска получило бы Вас очень далеко когда дело доходит до производительности по незначительной стоимости памяти и точности.

следующий отрывок является реализацией в качестве примера в 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;
}

Предыдущие результаты произошли из-за оптимизатора, делающего его задание, и оптимизировали далеко вычисления. Создание его на самом деле выполнить код приводит к немного отличающимся и намного более интересным результатам (на моем пути медленный Воздух МБ):

$ 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

profile

<час>

TODO:

существуют вещи улучшиться и способы удалить слабые места; то, как сделать, оставлен как осуществление читателю:)

  • Мелодия диапазон функции для предотвращения перехода, где таблица запускается и заканчивается.
  • Добавляют небольшую функцию шума для сокрытия артефактов искажения.
  • , Поскольку Rex сказал, интерполяция могла получить Вас вполне немного дальнейший мудрый точностью будучи довольно дешевой мудрый производительностью.
1
ответ дан 9 revs, 2 users 100% 26 November 2019 в 17:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: