Я задал вопрос о сети, которую я строю прошлая неделя, и я повторил предложения, которые привели меня к нахождению нескольких проблем. Я вернулся к этому проекту и решил все проблемы и узнал много больше о CNN в процессе. Теперь я застрял в проблеме, когда все мои веса перешли к массивно отрицательным значениям, что в сочетании с концами RELU на выходном изображении всегда было абсолютно черным (что делало невозможным выполнение классификатором своей работы).
На двух помеченных изображениях:
Они передаются в двухуровневую сеть, один классификатор (который получает 100% сам по себе) и сверточный слой 3 * 3 с одним фильтром.
На первой итерации вывод из слоя conv выглядит следующим образом (изображения в том же порядке, что и выше):
Фильтр 3 * 3 * 3, потому что изображения RGB. Все веса являются случайными числами между 0.0f-1.0f. На следующей итерации изображения полностью черный, печать фильтров показывает, что теперь они находятся в диапазоне -49678,5f (самый высокий, который я вижу) и -61932,3f.
Эта проблема, в свою очередь, связана с тем, что градиенты, возвращаемые из слоя Логистическая регрессия / Линейный, являются невероятно высокими для креста (метка 0, прогноз 0). Для круга (метка 1, прогноз 0) значения находятся в диапазоне примерно от -12 до -5, но для креста все они находятся в положительном диапазоне от 1000 до 2000.
Код, который отправляет их обратно, выглядит примерно так (некоторые части опущены):
void LinearClassifier::Train(float * x,float output, float y)
{
float h = output - y;
float average = 0.0f;
for (int i =1; i < m_NumberOfWeights; ++i)
{
float error = h*x[i-1];
m_pGradients[i-1] = error;
average += error;
}
average /= static_cast<float>(m_NumberOfWeights-1);
for (int theta = 1; theta < m_NumberOfWeights; ++theta)
{
m_pWeights[theta] = m_pWeights[theta] - learningRate*m_pGradients[theta-1];
}
// Bias
m_pWeights[0] -= learningRate*average;
}
Это передается обратно в один слой свертки:
// This code is in three nested for loops (for layer,for outWidth, for outHeight)
float gradient = 0.0f;
// ReLu Derivative
if ( m_pOutputBuffer[outputIndex] > 0.0f)
{
gradient = outputGradients[outputIndex];
}
for (int z = 0; z < m_InputDepth; ++z)
{
for ( int u = 0; u < m_FilterSize; ++u)
{
for ( int v = 0; v < m_FilterSize; ++v)
{
int x = outX + u - 1;
int y = outY + v - 1;
int inputIndex = x + y*m_OutputWidth + z*m_OutputWidth*m_OutputHeight;
int kernelIndex = u + v*m_FilterSize + z*m_FilterSize*m_FilterSize;
m_pGradients[inputIndex] += m_Filters[layer][kernelIndex]*gradient;
m_GradientSum[layer][kernelIndex] += input[inputIndex]*gradient;
}
}
}
Этот код повторяется путем передачи каждого изображения по одному за раз. Очевидно, что градиенты движутся в правильном направлении, но как я могу остановить появление огромных градиентов в функции прогнозирования?
Активации RELU известны тем, что делают это. Вы обычно должны использовать низкую скорость обучения. Причина этого заключается в том, что когда RELU возвращает положительные числа, он может продолжать свободно учиться, но когда единица начинает возвращать действительно низкое число, оно может стать «мертвым» нейроном и никогда больше не активироваться. Если у вас есть система, в которой положительные числа могут свободно изменяться, но отрицательные числа ниже определенного порогового значения потенциально могут «застрять», система в конечном итоге будет полностью зависать, если этот минимальный порог будет достигнут.
Также инициализация ваших весов очень деликатна с нейронами RELU. Похоже, что вы инициализируете диапазон 0-1, что создает огромный уклон. Два совета здесь: используйте диапазон с центром около 0 и диапазон, который намного меньше. Что-то вроде (-0,1) — (0,1)
Другое дело, что, по моему опыту, устройства RELU кажутся более полезными в сложных задачах (таких как классификация Imagenet). Дайте возможность подразделениям TanH сделать это. То, как вы инициализируете вес и скорость обучения, не так деликатно.
Я исправил это, уменьшив градиенты в слое CNN, но теперь я запутался, почему это работает / нужно, так что если у кого-то есть интуиция, почему это работает, это было бы здорово.