Оптимизация алгоритма восхождения на холм в C# для обучения нейронных сетей

Я написал небольшой проект на C#, который создает и обучает нейронные сети. Для получения дополнительной информации см. Мой предыдущий вопрос здесь: ( https://scicomp.stackexchange.com/questions/19481).

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

Похоже, в Интернете не так много примеров простых алгоритмов восхождения на холм в C#. Есть.NET Math Library, но я бы предпочел не платить за что-то.

Алгоритм восхождения на холм работает на каждом весе и каждом смещении в сети для обучения сети, и я выполняю несколько проходов. Я изучил обратное распространение, но, похоже, это применимо только к одному тренировочному примеру, у меня ~7000 примеров в моих тренировочных данных, а функция пригодности оценивает среднюю производительность сети по всем из них и возвращает непрерывный результат (двойной) счет.

Вот мой текущий код:

    public static double ImproveProperty(ref double property, double startingFitness, int maxIters, Random r, ref Defs.Network network, Func<Defs.Network, double> fitnessFunction)
    {
        //Record starting values
        var lastFitness = startingFitness;
        var lastValue = property;
        //Randomise magnitude of change to reduce chance 
        //of getting stuck in local optimums
        var magnitude = r.NextDouble();
        var positive = true;
        var iterCount = 0f;
        var magnitudeChange = 5;
        while (iterCount < maxIters)
        {
            iterCount++;
            if (positive)
            {   //Try adding a positive value to the property
                property += magnitude;
                //Evaluate the fitness
                var fitness = fitnessFunction(network);
                if (fitness == lastFitness)
                {   //No change in fitness, increase the magnitude and re-try
                    magnitude *= magnitudeChange;
                    property = lastValue;
                }
                else if (fitness < lastFitness)
                {   //This change decreased the fitness (bad)
                    //Put the property back and try going in the negative direction
                    property = lastValue;
                    positive = false;
                }
                else
                {   //This change increased the fitness (good)
                    //on the next iteration we will try 
                    //to apply the same change again
                    lastFitness = fitness;
                    lastValue = property;
                    //don't increase the iteration count as much
                    //if a good change was made
                    iterCount -= 0.9f;
                }
            }
            else
            {   //Try adding a negative value to the property
                property -= magnitude;
                var fitness = fitnessFunction(network);
                if (fitness == lastFitness)
                {
                    //No change in fitness, increase the magnitude and re-try
                    magnitude *= magnitudeChange;
                    property = lastValue;
                }
                else if (fitness < lastFitness)
                {
                    //This change decreased the fitness (bad)
                    //Now we know that going in the positive direction 
                    //and the negative direction decreases the fitness
                    //so make the magnitude smaller as we are probably close to an optimum
                    property = lastValue;
                    magnitude /= magnitudeChange;
                    positive = true;
                }
                else
                {
                    //This change increased the fitness (good)
                    //Continue in same direction
                    lastFitness = fitness;
                    lastValue = property;
                    iterCount -= 0.9f;
                }
            }
            //Check bounds to prevent math functions overflowing
            if (property > 100)
            {
                property = 100;
                lastFitness = fitnessFunction(network);
                return lastFitness;
            }
            else if (property < -100)
            {
                property = -100;
                lastFitness = fitnessFunction(network);
                return lastFitness;
            }
        }
        return lastFitness;
    }

Фитнес-функция очень дорогая, поэтому ее следует вызывать как можно меньше. Я ожидаю каких-либо улучшений в достижении местного оптимума с меньшим количеством обращений к функции фитнеса. Застревание в локальном оптимуме не является большой проблемой, я построил график функции пригодности по отношению к значению различных весов и смещений в сети, и похоже, что на графике обычно есть 1-3 локальных оптимума. Если сеть остается в одном и том же состоянии в течение нескольких проходов, тогда я могу добавить параметр к этой функции, чтобы попытаться возобновить восхождение на гору со случайного значения.

1 ответ

Этот подход не будет действительно масштабным; Вы пытаетесь оценить очень дорогую общую функцию пригодности несколько раз, чтобы получить небольшое улучшение по одному параметру. Это вся причина, почему градиентные методы смотрят на проблему оптимизации выборка за выборкой или, как правило, мини-партия за мини-партиями. Функция пригодности разлагается на сумму функций пригодности для каждого образца (или партии), и это то, что позволяет вычислить небольшое обновление и сделать шаг в правильном направлении.

Вы должны немного прочитать о теории. Есть много хороших онлайн-ресурсов, например, эта онлайн-книга.

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

Другие вопросы по тегам