У кого есть опыт с SOM (самоорганизующаяся нейросеть)?

 

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

Алгоритм используется тот, что в книге Кохонена описан под названием LVQ1.

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

Алгоритм обучения:

1) Карта заранее разбита на области, соответствующие классам входных значений.

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

3) Если найденный узел попадает в область карты того класса, к которому относиться обучающий пример, тогда значение этого узла (а также окружающих узлов) придвигается к значению обучающего примера. Иначе - отодвигается.

Формула: map[i][j][k] (+=/-=) alpha * elastic * (input[k] - map[i][j][k]);

i, j - координаты узла карты, k - номер значения в векторе. Альфа - затухающий коэффициент обучения, эластик - коэффициент эластичности карты (для самого корректируемого узла 1, на шаг дальше 0.5, на два шага дальше 0.3).

Прибавляем, когда сдвигаем узел в сторону целевого вектора, отнимаем, когда отодвигаем.

Алгоритм, казалось бы, элементарен и ошибиться негде. Однако не работает - не приводит к появлению гладкой кластеризованной карты, а приводит к каше.

Может я чего-то не знаю?

 
#property copyright "me"
#property link      "http://www.me.net"

int start()
{
   double SOM[15][8][36];
   OpenSOM(SOM);
   
   // циклы обучения
   int cycles = 100; // количество циклов
   int c;
   
   for (c = 0; c < cycles; c++)
   {
      Print("Начало цикла ", c);
      // затухающая скорость обучения
      double alpha = 0.5 * (1 - c / cycles);
      
      // перебор моментов времени
      int t;
      int start_date = 2000;
      int end_date = 500;

      for (t = start_date; t > end_date; t--)
      {
         // получение входного вектора и результирующего значения, вычисление класса
         double input[1];
         ArrayResize(input, ArrayRange(SOM, 2));
         double output;
         
         int k;
         for (k = 0; k < ArrayRange(SOM, 2); k++)
         {
            input[k] = iClose("EURUSD", PERIOD_H1, t + k) - iOpen("EURUSD", PERIOD_H1, t + k); 
         }
         
         output = iClose("EURUSD", PERIOD_H1, t - 2) - iOpen("EURUSD", PERIOD_H1, t);
         
         int class;
         
         if (output <= -0.0020) { class = 0; }
         if ((output < -0.0005) && (output > -0.0020)) { class = 1; }
         if ((output <= +0.0005) && (output >= -0.0005)) { class = 2; }
         if ((output < +0.0020) && (output > + 0.0005)) { class = 3; }
         if (output >= +0.0020) { class = 4; }
         
         // находим ближайжий к вектору узел карты
         double min_dist = 1000000;
         double calc_dist;
         int min_i, min_j;
         
         int i, j;
         for (i = 0; i < ArrayRange(SOM, 0); i++)
            for (j = 0; j < ArrayRange(SOM, 1); j++)
            {  
               calc_dist = 0;
               
               for (k = 0; k < ArrayRange(SOM, 2); k++)
               {
                  calc_dist += (SOM[i][j][k] - input[k]) * (SOM[i][j][k] - input[k]);
               }
               
               calc_dist = MathSqrt(calc_dist);
               
               if (calc_dist < min_dist)
               {
                  min_dist = calc_dist;
                  min_i = i;
                  min_j = j;
               }
            } // конец поиска ближайшего узла
         
         // теперь коррекция узла и его окрестности радиусом 2
         double elastic;
         for (i = min_i - 2; i <= min_i + 2; i++)
            for (j = min_j - 2; j<= min_j + 2; j++)
            {
               // если индексы за границами массива, то пропускаем итерацию
               if ((i < 0) || (i > ArrayRange(SOM, 0) - 1) || 
                   (j < 0) || (j > ArrayRange(SOM, 1) - 1))
                   { continue; }
               // коэффициент эластичности
               elastic = MathMin((1 / (1 + MathAbs(min_i - i))), (1 / (1 + MathAbs(min_j - j))));
                    
               for (k = 0; k < ArrayRange(SOM, 2); k++)
               {    
                  if (min_i / (ArrayRange(SOM, 0) / 5) == class)
                  {  // если класс входного вектора совпадает с классом ближайшего узла, то придвигаем
                     SOM[i][j][k] += alpha * elastic * (input[k] - SOM[i][j][k]);
                  }
                  else
                  {  // а если не совпадает, то отодвигаем
                     SOM[i][j][k] -= alpha * elastic * (input[k] - SOM[i][j][k]);
                  }
                  
                  if (MathAbs(SOM[i][j][k]) > 1000)
                  {
                     if (SOM[i][j][k] < 0)
                     {
                        SOM[i][j][k] = -1000;
                     }
                     else
                     {
                        SOM[i][j][k] = 1000;
                     }
                  }
               }  
            } // конец коррекции узлов сетки
         
      } // конец перебора моментов времени
      
   } // конец циклов обучения

   SaveSOM(SOM);
   Print("Обучение завершено, файл сохранён");

   return(0);
}

void OpenSOM(double& map[][][])
{
   int handle = FileOpen("eurusd.som", FILE_BIN|FILE_READ);
   FileReadArray(handle, map, 0, ArrayRange(map, 0) * ArrayRange(map, 1) * ArrayRange(map, 2));
   FileClose(handle);     
}

void SaveSOM(double map[][][])
{
   int handle = FileOpen("eurusd.som", FILE_BIN|FILE_WRITE);
   FileWriteArray(handle, map, 0, ArrayRange(map, 0) * ArrayRange(map, 1) * ArrayRange(map, 2));
   FileClose(handle);   

   // текстовый файл для контроля   
   handle = FileOpen("eurusd.txt", FILE_CSV|FILE_WRITE);
   int i, j;
   
   for (i = 0; i < ArrayRange(map, 0); i++)
      for (j = 0; j < ArrayRange(map, 1); j++)
      {
         FileWrite(handle, 
                   map[i][j][0], map[i][j][1], map[i][j][2], map[i][j][3], map[i][j][4], map[i][j][5],
                   map[i][j][6], map[i][j][7], map[i][j][8], map[i][j][9], map[i][j][10], map[i][j][11],
                   map[i][j][12], map[i][j][13], map[i][j][14], map[i][j][15], map[i][j][16], map[i][j][17],
                   map[i][j][18], map[i][j][19], map[i][j][20], map[i][j][21], map[i][j][22], map[i][j][23],
                   map[i][j][24], map[i][j][25], map[i][j][26], map[i][j][27], map[i][j][28], map[i][j][29],
                   map[i][j][30], map[i][j][31], map[i][j][32], map[i][j][33], map[i][j][34], map[i][j][35]);
      }
   
   FileClose(handle);     
}
 

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

В таком случае SOM не нужен

Причина обращения: