Сверточная нейронная сеть не сходится

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

Пересекать Круг

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

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 (max) в обратном распространении слоя свертки?

1

Решение

  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 (max) в обратном распространении слоя свертки?

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

5

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector