Быстрая альтернатива степени числа

 

Вычисление степени числа очень ресурсоемкая задача.

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

Первое, правда и последнее, что пришло на ум, заранее рассчитать функцию с достаточно для практического применения мелким шагом на заданном диапазоне чисел:

(2.0/(1.0+MathPow(2.0,-x)))-1.0;

записать полученные значения в массив. А уже по мере надобности искать положение аргумента функции между двумя рассчитанными заранее значениями в массиве. Останется только интерполяцией или другим способом посчитать искомое значение функции.

Есть ли какие нибудь более быстрые способы расчета степени числа с заданной наперед точностью?

 

joo:

Есть ли какие нибудь более быстрые способы расчета степени числа с заданной наперед точностью?

ну вообще есть древний алгоритм возведения в натуральную степень. не уверен что вам подойдет.

double fastPow(double base, int exponent)
{
  double res = 1;
  while(exponent > 0)
  {
    if (exponent % 2 == 0)
    {
      exponent /= 2;
      base *= base;
    }
    else
    {
      exponent--;
      res *= base;
    }
  }
  return res;
};
 
sergeev:

ну вообще есть древний алгоритм возведения в натуральную степень. не уверен что вам подойдет.

Можно чуть ускорить, но человеку скорее всего нужна именно дробная степень.

double fastPow(double base,int exponent)
  {
   double res=1;
   while(exponent>0)
     {
      if(exponent%2==0)
        {
         exponent>>=1;  // вот тут
         base*=base;
        }
      else
        {
         exponent--;
         res*=base;
        }
     }
   return res;
  };
 

В вики тоже красивый алгоритм, но тоже целая степень.

int power(int t,int k)
  {
// возведение t в степень k
   int res=1;
   while(k)
     {
      if(k &1) res*=t;
      t *= t;
      k>>= 1;
     }
   return res;
  }

 

Быстрей всего рациональная сигмоида. Вот тест.

void OnStart()
  {
   double x,f;
   uint start;
   start=GetTickCount();
   for(x=-100;x<=100;x+=0.00001)
     {
      f=(2.0/(1.0+MathPow(2.0,-x)))-1.0;
     }
   Print("sigmoid pow = ",IntegerToString(GetTickCount()-start));

   start=GetTickCount();
   for(x=-100;x<=100;x+=0.00001)
     {
      f=(2.0/(1.0+MathExp(-x)))-1.0;
     }
   Print("sigmoid exp = ",IntegerToString(GetTickCount()-start));

   start=GetTickCount();
   for(x=-100;x<=100;x+=0.00001)
     {
      f=x/(1.0+MathAbs(x));
     }
   Print("sigmoid abs = ",IntegerToString(GetTickCount()-start));
  }

2011.02.04 02:56:31 Sigmoid_Test_Rate (AUDUSD,H4) sigmoid abs = 1594

2011.02.04 02:56:29 Sigmoid_Test_Rate (AUDUSD,H4) sigmoid exp = 4313

2011.02.04 02:56:25 Sigmoid_Test_Rate (AUDUSD,H4) sigmoid pow = 6828


 

Urain прав, мне нужно было (уже ненужно, объяснение и результаты ниже) возведение в степень вещественного числа.

В моей приведённой формуле забыл поставить коэффициент 10. Прелесть этой формулы заключается в том, что "чувствительность" её лежит в [-1;1], а на выходе соответственно (-1;1), что во многих случаях удобно (и не только с сетками), и в том, что кривизна кривой более-менее равномерна в диапазоне чувствительности. Немного изменил формулы, предложенные Serj Che, для того, что бы функции работали в нужном диапазоне.

Вот график моей функции:

Вот график с экспонентой:

Вот без возведения в степень вообще:


Это хорошая функция в плане скорости, но кривизная её меня не устроила,

и я чуток ещё поэксперементировал, и получил такую формулу:

Видим вполне приличную кривизну.

А теперь собственно тест на скорость:

void OnStart()
  {
   double x,f;
   uint start,t1,t2,t3,t4;
   
   //-------------------------------------
   Sleep(10);
   start=GetTickCount();
   for(x=-1.0;x<=1.0;x+=0.00000001)
     {
      f=(2.0/(1.0+MathPow(2.0,-x*10.0)))-1.0;
     }
   t1=GetTickCount()-start;
   //-------------------------------------
   
   //-------------------------------------
   Sleep(10);
   start=GetTickCount();
   for(x=-1.0;x<=1.0;x+=0.00000001)
     {
      f=(2.0/(1.0+MathExp(-x*7.0)))-1.0;
     }
   t2=GetTickCount()-start;
   //-------------------------------------
   
   //-------------------------------------
   Sleep(10);
   start=GetTickCount();
   for(x=-1.0;x<=1.0;x+=0.00000001)
     {
      f=6.0*x/(1.0+MathAbs(5.0*x));
     }
   t3=GetTickCount()-start;
   //-------------------------------------
   
   //-------------------------------------
   Sleep(10);
   start=GetTickCount();
   for(x=-1.0;x<=1.0;x+=0.00000001)
     {
      f=3.0*x/(1.0+MathAbs(x)+x*x);
     }
   t4=GetTickCount()-start;
   
   Print("sigmoid pow = ",t1," sigmoid exp = ",t2," sigmoid abs = ",t3," sigmoid new = ",t4);
  }

Результат, как говорится, на лицо:

2011.02.04 12:07:26    Sigma 1 (EURUSD,M15)    sigmoid pow = 25703 sigmoid exp = 12718 sigmoid abs = 2500 sigmoid new = 2437


Получилось, почему то, даже быстрее, чем по предыдущей формуле. Ускорение составляет больше чем в 10 раз, по сравнению с моей первой формулой сигмоиды!

Всем большое спасибо. Чуть позже выложу результат тестирования, так сказать, на "натуре" - запущу сетку с весами штук эдак под 1000-2000.

 
joo:

А какова скорость стандартной функции pow?
 
Academic:
А какова скорость стандартной функции pow?
Смотрите первый тест из четырех.
 

Результат оказался скромнее, чем ожидал. :(

Так, для сети с 1260 весами (60 нейронов с функцией активации, 150000 запусков, такие результаты:
2011.02.04 12:51:32    Tests MLP easy v2 (EURUSD,M15)    4547 мс - Время исполнения
2011.02.04 12:45:24    Tests MLP easy (EURUSD,M15)    5219 мс - Время исполнения

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

Но все равно, теперь я быстрее конкурентов на 15%. :)  

Документация по MQL5: Стандартные константы, перечисления и структуры / Торговые константы / Свойства ордеров
Документация по MQL5: Стандартные константы, перечисления и структуры / Торговые константы / Свойства ордеров
  • www.mql5.com
Стандартные константы, перечисления и структуры / Торговые константы / Свойства ордеров - Документация по MQL5
 
joo:

Результат оказался скромнее, чем ожидал. :(

:) А теперь посчитаем.

допустим, в сети 80 входов, 200 на скрытом слое, 5 на выходе, сигмоиды только на скрытом слое, простой 3-слойный персептрон.

Распространение сигнала = O1 (80*200 + 5*200) = О1(85*200), преобразование сигнала = О2 (1*200).

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

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

Воистину преждевременная оптимизация зло.

 
joo:

Результат оказался скромнее, чем ожидал. :(

Так, для сети с 1260 весами (60 нейронов с функцией активации, 150000 запусков, такие результаты:
2011.02.04 12:51:32    Tests MLP easy v2 (EURUSD,M15)    4547 мс - Время исполнения
2011.02.04 12:45:24    Tests MLP easy (EURUSD,M15)    5219 мс - Время исполнения

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

Но все равно, теперь я быстрее конкурентов на 15%. :)  

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

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

double Sigma(double d,double A){return( 1./(1.+exp(-A*d)) );}
здесь A-коэффициентах угла атаки. Во все остальные формулы типа sigmoid abs  ,  sigmoid new не так то легко встроить этот коэффициент а ведь с этого же всё началось, Коэф в функции активации это основное что тебе нужно? или тебе просто нужна функция делающая нелинейное преобразование?