Алгоритм искусственного интеллекта для оптимизации/прогнозирования многомерных решений

У меня есть 6 параметров int в диапазоне от 0 до 100

Общая комбинация чисел составляет 100 ^ 6, и каждая комбинация дает результат в диапазоне прибл. от -10000 до 100000 и более.

Input data example:
MySimulation (57, 78, 20, 10, 90, 50) = 300  <- Best Result
MySimulation (50, 80, 10, 90, 35, 8) = 200
MySimulation (4, 55, 40, 99, 40, 50) = -50 <- Worst Result

Чем выше результат, тем лучше комбинация чисел, у меня уже есть расчет, который дает результат, мне нужен только ИИ, чтобы найти лучшую комбинацию чисел, которая дает более высокий результат.

Output data example:
55, 70, 25, 15, 95, 52   <- Let's say these combination of
                            numbers was choosen by AI and would 
                            give a result of 400 with my simulation

Примечание. Порядок чисел также важен.

Как я могу уменьшить общее количество комбинаций 100 ^ 6 с помощью ИИ, чтобы получить наилучший результат без повторения всех 100 ^ 6 комбинаций?

Я планирую использовать Accord.NET на C# (или есть что-то лучше?), пример кода был бы полезен, потому что я новичок в ИИ.


person Mario M    schedule 05.10.2017    source источник
comment
Вы спрашиваете, как перепроектировать Calculation, чтобы найти оптимальное решение, или вы спрашиваете, как найти довольно хорошее решение? Вы можете использовать инфраструктуру ИИ, чтобы найти довольно хорошее решение, но это в основном проблема локально-максимального типа, и, кроме того, она имеет небольшую область. Типичный подход (очень грубо) состоит в том, чтобы попробовать случайные числа, выбрать лучшие результаты, а затем попробовать другие комбинации ближайших чисел и повторять, пока вы не будете удовлетворены.   -  person BurnsBA    schedule 05.10.2017
comment
У меня уже есть расчет, который дает результат, мне просто нужно довольно хорошее решение.   -  person Mario M    schedule 05.10.2017
comment
Без предположений вы не сможете превзойти поиск методом грубой силы. Я не вижу никаких предположений/априорной информации в вашем посте.   -  person sascha    schedule 05.10.2017
comment
нет никаких предположений, потому что если вы измените число, это повлияет на другие числа   -  person Mario M    schedule 07.10.2017
comment
Не используйте Accord.NET. Это не проблема машинного обучения. Взгляните на генетические алгоритмы: stackoverflow.com/questions/ 14008/. Или искусственный отжиг.   -  person MineR    schedule 09.10.2017
comment
Accord.net также имеет GA, и он обновляется позже, чем aforge.   -  person Mario M    schedule 10.10.2017
comment
Если вас интересует многоцелевая оптимизация, посмотрите мой ответ. Кроме того, есть пакет под названием jmetal, который может быть полезен.   -  person Joe    schedule 13.10.2017
comment
Если вам абсолютно необходимо найти наилучшее оптимальное решение вашей проблемы, то вы не сможете превзойти поиск методом грубой силы. Вам действительно нужно будет рассмотреть все возможные комбинации, оценить их результат и выбрать лучшее значение, которое вы нашли. Однако, если вы готовы немного пожертвовать производительностью ради скорости, вам действительно стоит взглянуть на Accord.Genetics при использовании Accord.NET — оно может предоставить вам методы поиска достаточно хорошего решения без необходимости изучения всего пространства поиска.   -  person Cesar    schedule 23.10.2017


Ответы (3)


Добро пожаловать в область многоцелевой оптимизации. Это область, в которой я написал диссертацию. Существует множество алгоритмов для решения подобных задач, но, пожалуй, самыми известными являются два — NSGA-II и SPEA2.

Конечно, у вас есть только одна цель: независимо от того, что дает ваша функция подсчета очков. Я бы сказал, что многоцелевые алгоритмы также применимы, потому что вас интересует не просто одно решение, а их совокупность.

Могу я указать вам на http://jmetalnet.sourceforge.net/?

Идея состоит в том, что вы будете генерировать популяции случайных векторов, содержащих входные данные, которые варьируются в вашей области из 100 ^ 6 возможных решений. Эти популяции будут мутировать и скрещиваться друг с другом для создания новых решений, и из этих новых популяций они будут отобраны таким образом, что будут выбраны более предпочтительные решения, чтобы остаться (и выжить в эволюции).

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

Изложил

  1. Создайте случайную популяцию решений.
  2. Произвольно изменяйте/пересекайте свои решения.
  3. Подсчитайте пригодность каждого и рассортируйте.
  4. Уменьшите выборку до исходного размера популяции лучших решений.
  5. Повторяйте шаги 2-4 [до сходимости: пока средняя пригодность > порога?]
  6. Выходное окончательное поколение.

Полученные результаты:

Вот плохой анализ, и обратите внимание, вы могли бы добиться большего успеха, усредняя результат (скажем) 20 прогонов на каждом уровне параметров. С самого начала вы можете сказать, что уровень мутаций должен оставаться низким, и, очевидно, более высокий размер популяции может помочь (до точки схождения).

Результаты в формате среднего балла до, после, максимум 600

PopSize=100,NumGens=50,MutRate=0,2,CrossRate=0,8
295,23 542,12

PopSize=100,NumGens=500,MutRate=0,2,CrossRate=0,8
298,53 565

PopSize=1000,NumGens=50,MutRate=0,2,CrossRate=0,8
301 814 579 334

PopSize=10000,NumGens=500,MutRate=0,2,CrossRate=0,8
299,8901,588

PopSize=1000,NumGens=50,MutRate=0,4,CrossRate=0,8
306,22 385,55

Код

Я написал этот код примерно за 20 минут, поэтому он не должен быть элегантным или потрясающим. Я надеюсь, что это просто доносит суть.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace moo_in_csharp
{
    internal class Individual
    {
        public int[] Decisions;
        public double Fitness;
        private int _numdecisions = 6;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Individual()
        {
            Decisions = new int[_numdecisions];
        }

        /// <summary>
        /// Replaces first half of decisions with those of the mate.
        /// </summary>
        /// <param name="mate"></param>
        public void Crossover(Individual mate)
        {
            int crossoverPoint = _numdecisions / 2;
            for (int i = 0; i < crossoverPoint; i++)
            {
                Decisions[i] = mate.Decisions[i];
            }
        }

        /// <summary>
        /// Simple fitness function that computes sum of a+b+c+d+e+f.
        /// </summary>
        public double Evaluate()
        {
            Fitness = Decisions.Sum();
            return Fitness;
        }

        /// <summary>
        /// Assigns random values to its decisions.
        /// </summary>
        public void Generate()
        {
            for (int i = 0; i < _numdecisions; i++)
            {
                Decisions[i] = Program.rand.Next(0, 101);
            }
        }

        /// <summary>
        /// Randomly mutate select decisions.
        /// </summary>
        public void Mutate()
        {
            for (int i = 0; i < _numdecisions; i++)
            {
                Decisions[i] = Program.rand.Next(0, 101);
            }
        }
    }

    internal class Program
    {
        public static Random rand = new Random();

        private static void Main(string[] args)
        {
            //parameters
            int populationSize = 100;
            int numGenerations = 50;
            double mutationRate = 0.2;
            double crossoverRate = 0.8;

            //build initial population
            List<Individual> solutions = new List<Individual>();
            for (int i = 0; i < populationSize; i++)
            {
                var solution = new Individual();
                solution.Generate();
                solution.Evaluate();
                solutions.Add(solution);
            }

            //calculate average score of initial population
            var averageScoreBefore = solutions.Select(x => x.Evaluate()).Average();

            //iterate across number of generations
            for (int i = 0; i < numGenerations; i++)
            {
                //build offspring by mating sequential pairs of solutions
                var offspring = new List<Individual>();
                for (int e = 0; e < solutions.Count() - 1; e += 2)
                {
                    if (rand.NextDouble() < crossoverRate)
                    {
                        var newIndividual = new Individual();
                        solutions[e].Decisions.CopyTo(newIndividual.Decisions, 0);
                        newIndividual.Crossover(solutions[e + 1]);
                        offspring.Add(newIndividual);
                    }
                }

                //add our offspring to our solutions
                solutions.AddRange(offspring);

                //mutate solutions at a low rate
                foreach (var solution in solutions)
                {
                    if (rand.NextDouble() < mutationRate)
                    {
                        solution.Mutate();
                    }
                }

                //sort our solutions and down-sample to initial population size
                solutions = solutions.OrderByDescending(x => x.Evaluate()).ToList();
                solutions = solutions.Take(populationSize).ToList();
            }

            //calculate average score after
            var averageScoreAfter = solutions.Select(x => x.Evaluate()).Average();
            Debug.WriteLine(averageScoreBefore + "," + averageScoreAfter);
        }
    }
}

Другие примечания

Ваше время выполнения будет в основном зависеть от вашей функции оценки физической подготовки. Для простых математических функций эта среда выполнения не будет сложной. Очевидно, что если вместо этого задействован процесс, вы захотите свести количество оценок к минимуму. Это то, что я изучал для своей докторской диссертации и разработал новый алгоритм под названием GALE:

person Joe    schedule 12.10.2017
comment
Благодарю вас! Я попробую код в своем приложении и посмотрю, куда он попадет. - person Mario M; 13.10.2017
comment
В вашем образце используются NSGA-II, SPEA2 или GALE? - person Mario M; 13.10.2017
comment
Код здесь не является ни тем, ни другим. Здесь дело обстоит проще, потому что у вас есть только одна объективная оценка физической подготовки, поэтому проще составить быструю и грязную программу. - person Joe; 14.10.2017
comment
Я реализовал код, это просто волшебство, за 5 минут я получил на 30% лучшую физическую форму, чем 64mil. грубая сила с более низким разрешением, которая заняла более 30 минут. Но я вижу, что он не всегда держит высший балл. После того, как он достигает 470, то через несколько поколений оценка лучшего решения достигает 390, это нормально? - person Mario M; 14.10.2017
comment
Иногда хорошие решения превращаются в худшие решения. Это часть магии генетических алгоритмов. Этот код генерирует популяцию, поэтому вы получаете пул хороших решений. Вы можете добавить критерий остановки или установить скорость мутации еще ниже. Также вы можете архивировать лучшие скоринговые решения после каждого поколения в виде отдельного списка, если хотите. - person Joe; 15.10.2017

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

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

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

Реализация самих этих алгоритмов обычно не очень сложна, и я думаю, что вы даже можете использовать библиотеку, такую ​​​​как NMath, например. Взгляни на

http://www.centerspace.net/doc/NMath/user/simulated-annealing-85273.htm

Ваша «целевая функция», значение, которое вы пытаетесь максимизировать (или минимизировать), является результатом моделирования.

Хотя реализация самих алгоритмов несложна, эффективная реализация различных их аспектов сложна.

Что вам нужно сделать, так это определить функцию соседства, то есть способ перехода от решения (или состояния, если хотите) к другому решению. В вашем случае это может быть пример кода, предложенный BurnsCA, который будет шагом с 1 выбором, потому что вы случайно выберете другое значение для одного параметра. Вы также можете попробовать ходы с 2 вариантами или более, если 1 вариант не показывает улучшений достаточно быстро.

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

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

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

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

Если это одноразовая вещь, и описанное выше невозможно, вы также можете рассмотреть возможность распараллеливания вычислений на тысячах машин для получения оптимального решения. Например. Пакетная служба Azure или аналогичные службы. Поскольку вы можете протестировать 50 миллионов комбинаций за 30 минут (на одной машине без распараллеливания?), в принципе вы могли бы подготовить 20 000 виртуальных машин и протестировать все комбинации за 30 минут.

person kkirk    schedule 12.10.2017
comment
Спасибо, но проблема в том, что оптимальное решение должно быть найдено много раз, а не один раз, для большего количества продуктов, а также, если я получу лучшие результаты с ИИ, чем текущий перебор 60 миллионов пользовательских выбранных комбинаций, я могу добавить больше параметров и улучшить расчет даже больше. - person Mario M; 12.10.2017
comment
Добро пожаловать, и в этом случае, вероятно, стоит попробовать (мета) эвристику, такую ​​​​как имитация отжига (или аналогичная). Я хотел бы отметить одну деталь: ни ИИ, ни алгоритм локального поиска не предоставят вам гарантированное глобально оптимальное решение, только локально оптимальное, которое может или может не быть глобально оптимальным (вы не узнаете, пока не решите проблему до оптимальности, например, путем перебора). - person kkirk; 13.10.2017
comment
Я знаю, но по крайней мере я могу сравнить с лучшим результатом от 60mil. комбинация грубой силы, которая у меня есть прямо сейчас и дает приличные результаты, поэтому, если я получу что-то хотя бы на 20-30% лучше, результат будет хорошим. - person Mario M; 13.10.2017

Вам не нужна среда машинного обучения для реализации алгоритма локальной оптимизации.

// Example linear calculation
public int Calculation(int a, int b, int c, int d, int e, int f)
{
    int result = 0;
    unchecked
    {
        result = (int)((double)a * Math.Tan((double)b * Math.PI / ((double)c + 0.001)));
        result += d + e + f;
    }

    return result;
}


var rand = new Random();

// The currently best known solution set
var best = new int[6] { 1, 1, 1, 1, 1, 1 };

// Score for best known solution set
int bestScore = int.MinValue;

// loop variables
int score;
var work = new int[6];

// Loop as appropriate.
for (int i=0; i<500; i++)
{
    // Copy over the best solution to modify it
    best.CopyTo(work, 0);

    // Change one of the parameters of the best solution
    work[rand.Next() % 6] = rand.Next() % 101;

    // Calculate new score with modified solution
    score = Calculation(work[0], work[1], work[2], work[3], work[4], work[5]);

    // Only keep this solution if it's better than anything else
    if (score > bestScore)
    {
        work.CopyTo(best, 0);
        bestScore = score;
    }
}

Приведенное выше решение сходится довольно быстро, в основном потому, что функция вычисления очень удобна. После 500 итераций:

int[6] { 98, 1, 2, 98, 99, 100 }

Где оптимальное решение было бы { 100, 1, 2, 100, 100, 100 }

Обратите внимание, что этот подход локальной оптимизации работает только в основном для линейных задач.

Не показано, но вы также хотели бы увидеть разные начальные значения или перезапустить все это несколько раз.

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

person BurnsBA    schedule 05.10.2017
comment
Это изящное изображение описывает не ваш алгоритм (где анимация выглядела бы совсем по-другому), а немного более сложный подход имитации отжига. - person sascha; 05.10.2017
comment
Я не знаю, сработает ли это, это просто случайное испытание, оно не пробует числа, близкие к лучшим результатам. Также обратите внимание, что изменение одного числа из этих 6 чисел приведет к совершенно другому результату с другими числами. - person Mario M; 05.10.2017
comment
Что ж, если ваша основная проблема нелинейна, вам понадобится совершенно другой подход. - person BurnsBA; 05.10.2017
comment
Это не очень линейно, потому что изменение одного числа дает лучшие результаты при изменении других чисел. - person Mario M; 05.10.2017
comment
Существует множество способов улучшить алгоритм поиска, но это действительно зависит от проблемы, и ни одна из подробностей проблемы не приводится. Вы можете изменить несколько параметров в приведенном выше примере в каждом цикле или только изменить их, скажем, на 10%, или изменить их любым другим способом, который имеет смысл. Но опять же, это зависит от Calculation, и никаких подробностей об этом не было опубликовано. - person BurnsBA; 05.10.2017
comment
Расчет представляет собой сложное моделирование - person Mario M; 05.10.2017
comment
@MarioM Пожалуйста, сначала проведите базовое исследование оптимизации, так как кажется, что вам чего-то не хватает. Во-первых: мое утверждение, доказанное теорией, остается в силе: без предположений грубая сила асимптотически оптимальна. Во-вторых: сложное моделирование может привести к недетерминированному/зашумленному поведению; медленная оценка функции (не может вывести градиенты) и негладкость. Каждая из этих характеристик может быть смертельно опасна при определенном подходе. Так что проведите небольшое исследование, чтобы понять, что такое оптимизация, а затем подумайте, как представить свою проблему! Как вы уже видели: в ответе выше уже использовались несовместимые предположения. - person sascha; 05.10.2017
comment
Функция оптимизирована, на самом деле она делает 50 миллионов комбинаций брутфорсом за 30 минут (с более низкими цифрами точности), но комбинаций осталось слишком много - person Mario M; 05.10.2017
comment
изменение одного параметра влияет на другие параметры - person Mario M; 06.10.2017