Реализация функции ReLU и реализация мини-пакета всегда выдают 0 значений - PullRequest
0 голосов
/ 11 апреля 2020

Я пытаюсь создать CNN, который предсказывает цифры из набора данных mnist. Я использую RELU для функции активации и MSE для функции потерь. Моя архитектура такая же, как у в руководстве по keras для набора данных mnist, за исключением слоев Dropout.

model.add(Conv2D(32, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

Проблема, по-видимому (по крайней мере, мне) в моей реализации функции ReLU и использовании мини-пакета. На первой итерации процесса обучения значения выходов очень велики. Они варьируются от 50 до 1000 (я думаю, что это проблема взрывающегося градиента), и это только для первого прямого распространения. На втором все значения на выходе равны 0. Неважно, если я использую мини-пакет 128 или 1, это всегда то же самое. Я пробовал с темпами обучения в диапазоне от 0,01 до 0,0000001, если они всегда одинаковы. Однако, когда я изменяю функцию активации на сигмовидную, все немного по-другому. С мини-пакетом равняется 1 (я думаю, что это SGD, если я использую только 1 пример на прямую / обратную пропину), выходы все на случайным при первом запуске, но если я повторю тренировочный процесс только с одним примером из учебного набора, я вижу, что по крайней мере CNN может запомнить этот один пример. Это реализация производной функции потерь (MSE) :

void backprop(Layer* layer) override
{
    for (size_t i = 0; i < size; i++)
    {
        derivativeWRToInput[i] = -2 * (observedValues[i] - predictedValue[i]);
    }
}

PredictedValue исходит от CNN, а наблюдаемое значение является фактическим значением (Is равно 0 или 1) , Когда я использую ReLU и все выходы из CNN равны 0, тогда производная потери, скажем, (-2, 0, 0 ...), если фактическое значение было 1. Это реализация функции ReLU. .

struct relu
{
    float operator()(const float& input) const
    {
        if (input > 0)
            return  input;
        else
            return 0;
    }

    float operator()(const float& chain_rule_input, const float& activation_output) const
    {
        if (activation_output > 0)
            return chain_rule_input;
        else
            return 0.01f;
    }
};

Я не уверен, правильно ли мое понимание мини-градиентного спуска. Я реализовал так же, как SGD, но не обновлял веса во время backprop, просто сохраняя ошибки весов в памяти, после всех примеров в одном пакете, я беру среднее значение ошибки весов (weights_error / batch_size) и обновляю их со скоростью обучения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...