6.Метод прямого прохода Dropout

Прямой проход традиционно организован в методе FeedForward. Напомню, что данный метод объявлен виртуальным в базовом классе нейронного слоя. Он переопределяется в каждом новом классе для выстраивания конкретного алгоритма работы класса. Данный класс не будет исключением — в нем также переопределим данный метод.

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

bool CNeuronDropout::FeedForward(CNeuronBase *prevLayer)
  {
//--- блок контролей
   if(!prevLayer || !prevLayer.GetOutputs() || !m_cOutputs)
      return false;

После успешного прохождения блока контролей мы переходим к выполнению алгоритма метода Dropout.

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

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

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

//--- генерируем тензор маскирования данных
   ulong total = m_cOutputs.Total();
   if(!m_cDropOutMultiplier.m_mMatrix.Fill(m_dInitValue))
      return false;
   for(int i = 0i < m_iOutNumberi++)
     {
      int pos = (int)(MathRand() * MathRand() / MathPow(32767.02) * total);
      if(m_cDropOutMultiplier.m_mMatrix.Flat(pos) == 0)
        {
         i--;
         continue;
        }
      if(!m_cDropOutMultiplier.m_mMatrix.Flat(pos0))
         return false;
     }

После генерации вектора маскирования нам остается его применить к исходным данным. Для этого достаточно поэлементно умножить два буфера: исходных данных и маскирования.

Здесь надо вспомнить, что согласно нашей концепции построения библиотеки, в каждом методе классе по возможности мы создаем две ветки выполнения алгоритма: стандартными средствами MQL5 и с использованием технологии многопоточных вычислений средствами OpenCL. Поэтому далее мы создаем разветвление алгоритма в зависимости от выбранного устройства вычислительных операций.

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

//--- разветвление алгоритма в зависимости от устройства выполнения операций
   if(!m_cOpenCL)
     {

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

//--- проверка флага режима работы
      if(!m_bTrain)
         m_cOutputs.m_mMatrix = prevLayer.GetOutputs().m_mMatrix;
      else         
         m_cOutputs.m_mMatrix = prevLayer.GetOutputs().m_mMatrix *
                                m_cDropOutMultiplier.m_mMatrix;
     }
   else  // Блок OpenCL
     {
      return false;
     }
//---
   return true;
  }

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

Далее переходим к организации процесса обратного прохода.