1

Я смотрел несколько видеороликов в глубоких учебных/сверточных нейронных сетях, таких как here и here, и я попытался реализовать свои собственные на C++. Я попытался сохранить входные данные достаточно простыми для моей первой попытки, поэтому идея состоит в том, чтобы различать крест и круг, у меня есть небольшой набор данных по 25 из каждого (64 * 64 изображения), они выглядят так:Свернутая нейронная сеть не сходится

CrossCircle

сама сеть пять слоев:

Convolution (5 filters, size 3, stride 1, with a ReLU) 
MaxPool (size 2) 
Convolution (1 filter, size 3, stride 1, with a ReLU) 
MaxPool (size 2) 
Linear Regression classifier 

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

Обучение сверточного слоя выглядит примерно так, удалены некоторые петли, чтобы сделать его чище

// Yeah, I know I should change the shared_ptr<float> 
void ConvolutionalNetwork::Train(std::shared_ptr<float> input,std::shared_ptr<float> outputGradients, float label) 
{ 
    float biasGradient = 0.0f; 

    // Calculate the deltas with respect to the input. 
    for (int layer = 0; layer < m_Filters.size(); ++layer) 
    { 
     // Pseudo-code, each loop on it's own line in actual code 
     For z < depth, x <width - filterSize, y < height -filterSize 
     {    
      int newImageIndex = layer*m_OutputWidth*m_OutputHeight+y*m_OutputWidth + x; 

      For the bounds of the filter (U,V) 
      { 
       // Find the index in the input image 
       int imageIndex = x + (y+v)*m_OutputWidth + z*m_OutputHeight*m_OutputWidth; 
       int kernelIndex = u +v*m_FilterSize + z*m_FilterSize*m_FilterSize; 
       m_pGradients.get()[imageIndex] += outputGradients.get()[newImageIndex]*input.get()[imageIndex]; 
       m_GradientSum[layer].get()[kernelIndex] += m_pGradients.get()[imageIndex] * m_Filters[layer].get()[kernelIndex]; 

       biasGradient += m_GradientSum[layer].get()[kernelIndex]; 
      }  
     } 
    } 

    // Update the weights 
    for (int layer = 0; layer < m_Filters.size(); ++layer) 
    { 
     For z < depth, U & V < filtersize 
     { 
      // Find the index in the input image 
      int kernelIndex = u +v*m_FilterSize + z*m_FilterSize*m_FilterSize; 
      m_Filters[layer].get()[kernelIndex] -= learningRate*m_GradientSum[layer].get()[kernelIndex]; 
     } 
     m_pBiases.get()[layer] -= learningRate*biasGradient; 
    } 
} 

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

Максимальные пулы вычисляют градиенты обратно, как это (он сохраняет максимальные показатели и нули всех остальные градиенты из)

void MaxPooling::Train(std::shared_ptr<float> input,std::shared_ptr<float> outputGradients, float label) 
{ 
    for (int outputVolumeIndex = 0; outputVolumeIndex <m_OutputVolumeSize; ++outputVolumeIndex) 
    { 
     int inputIndex = m_Indices.get()[outputVolumeIndex]; 
     m_pGradients.get()[inputIndex] = outputGradients.get()[outputVolumeIndex]; 
    } 
} 

И последний регресс слой вычисляет свои градиенты, как это:

void LinearClassifier::Train(std::shared_ptr<float> data,std::shared_ptr<float> output, float y) 
{ 
    float * x = data.get(); 

    float biasError = 0.0f; 
    float h = Hypothesis(output) - y; 

    for (int i =1; i < m_NumberOfWeights; ++i) 
    { 
     float error = h*x[i]; 
     m_pGradients.get()[i] = error; 
     biasError += error; 
    } 

    float cost = h; 
    m_Error = cost*cost; 

    for (int theta = 1; theta < m_NumberOfWeights; ++theta) 
    { 
     m_pWeights.get()[theta] = m_pWeights.get()[theta] - learningRate*m_pGradients.get()[theta]; 
    } 

    m_pWeights.get()[0] -= learningRate*biasError; 
} 

После 100 итераций обучения на двух примерах предсказание на каждом из них совпадает с другим и неизменным с самого начала.

  1. Должно ли сверточную сеть, подобную этой, иметь возможность различать два класса?
  2. Правильно ли это?
  3. Должен ли я учитывать ReLU (макс.) В backpropagation уровня свертки?

ответ

5
  1. Если сверточная сеть, как это может быть в состоянии различать между этими двумя классами?

Да. На самом деле даже сам линейный классификатор должен иметь возможность легко различать (если изображения более или менее центрированы).

  1. Правильно ли это?

Наиболее вероятной причиной является ошибка в ваших градиентных формулах. Всегда следуйте 2 простых правил:

  1. Пуск с базовой модели. Не начинайте работу с сетью 2-conv.Начните свой код без любой свертки. Он работает сейчас? Когда вы работаете 1 линейный слой, добавьте одиночную свертку. Он работает сейчас? и так далее.
  2. Всегда Проверьте свои градиенты численно. Это так легко сделать и сэкономит вам часы отладки! Напомним, что из анализа

    [grad f(x) ]_i ~ (f(x+eps*e_i) - f(x-eps*e_i))/2*eps 
    

    , где с помощью [] _i То есть i-й координаты, а e_i То есть i-й канонический вектор (нулевой вектор с одной на i-й координаты)

Должен ли я учитывать ReLU (макс.) В backpropagation уровня свертки?

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

+0

Спасибо! Я попробую и вернусь к вам. Изображения не центрированы и имеют разные цвета и т. Д., Поэтому я думаю, что линейный классификатор не будет работать во всем тестовом наборе. Но я попробую это на этих двух. – Davors72