среда, 13 июня 2012 г.

Обучение персептрона с помощью ГА


Лабораторная работа

по курсу 

"Интеллектуальные системы":

Обучение нейронной сети с помощью генетических алгоритмов

Выполнил 
студент группы ИУ5-81
Баришок Н.И.


Краткое описание
Разработка компьютерной программы, которая обучает искусственный нейрон распознавать изображения с помощью генетических алгоритмов.
Цель работы
Изучить принципы работы и алгоритм обучения простейших искусственных нейронных сетей (НС).

Требования к исходным данным и функциональности компьютерной программы
  • В программе должна быть реализована возможность задания обучающей выборки из внешних файлов изображений
  • Изображения должны быть черно-белыми (bitmap) и размером не менее 16 (4x4) пикселей.
  • Программа должна иметь два режима работы: обучения и распознавания.
  • Обучение НС должно производиться с помощью генетического алгоритма .
На экранной форме режима обучения должны отображаться:
  • элементы обучающей выборки (изображения)
  • правильные варианты элементов обучающей выборки
  • текущие (итоговые) веса нейронов и значение порога активационной функции
  • размер ошибки, при котором обучение нейрона завершается
  • режим обучения должен иметь два варианта работы:
    • пошаговый - на экране должны отображаться все представители (хромосомы) одного поколения до и после применения каждого оператора (скрещивания, селекции, редукции и мутации).
    • циклический - на экране должны отражаться только агрегированные данные по каждому поколению и итоговый набор хромосом.
На экранной форме режима распознавания должны отображаться:
  • распознаваемое изображение (должно выбираться из всего множества)
  • результат распознавания
  • веса нейронов и значение порога активационной функции
  • значения выхода нейрона
В целом, структура работы алгоритма обучения персептрона остается прежней, однако, применение генетических алгоритмов вносит следующие коррективы:
-значение целевой функции вычисляется для текущей популяции, в случае если выполнено одно из двух условий (коэффициенты подобраны с подобающей точностью,- расчет идет как минимальное значение целевой функции для правильной картинки  минус максимальное значение для неправильной (выбирается из всех значений для неправильных), данное значение должно удовлетворять выбранной пользователем дельты; второе условие - достигнут предел итераций);
-в случае если одно из условий выполнилось - выбираем наиболее подходющий набор весов и заканчиваем выполнение алгоритма
-в случае, если ни одно из условий не выполнилось - для набора хромосом, где в качестве гена - значение веса для ij-ого пиксела картинки, выполняются все стадии генетического алгоритма с целью формирования новой, более приспособленной популяции, а именно: селекция, затем применение генетических операторов - скрещивания и мутации - затем идет формирование новой популяции на базе сформированных значений хромосом. В данном случае вероятность мутации пришлось искуственно завысить с целью, чтобы предотвратить слишком быстрое схождение алгоритма.

Код, отвечающий за выполнение вышеописанных функций, можно посмотреть ниже:
private void CarryOutWeightsSelection()
        {
            ObservableCollection<double> tempLocals = new ObservableCollection<double>();
            double localdelta = 0;
            foreach (ObservableCollection<double> weight in weightsSet)
            {
                localdelta = 0;
                double y1res = this.EvaluateThreshHoldFunc(this.etalonPictures.ElementAt(0), weight);
                localdelta += Math.Abs(y1res - 1);

                foreach (ObservableCollection<bool> pic in this.trainingPictures)
                {
                    double y0res = this.EvaluateThreshHoldFunc(pic, weight);
                    localdelta += Math.Abs(y0res);
                }
                tempLocals.Add(localdelta);

                if (this.Delta >= localdelta)
                {

                    m_weights = new ObservableCollection<double>(weight);
                    
                    m_thresholdFunc = this.EvaluateThreshHoldFunc(this.etalonPictures.ElementAt(0), m_weights);

                    int _index = 0;
                    foreach (PixelRow prow in this.CheckImageRows)
                        foreach (Pixel px in prow.Pixels){
                            px.ElementaryWeight = m_weights.ElementAt(_index);
                            _index++;
                }
                    this.OnRaiseUpdateEvent();
                    break;
                }
                

            }

            if (iteration == this.GenerationsLimit)
            {
                m_weights = new ObservableCollection<double>(tempWeightSelected);
                this.CalculationsDone = true;

                m_thresholdFunc = this.EvaluateThreshHoldFunc(this.etalonPictures.ElementAt(0), m_weights);

                int _index = 0;
                foreach (PixelRow prow in this.CheckImageRows)
                    foreach (Pixel px in prow.Pixels)
                    {
                        px.ElementaryWeight = m_weights.ElementAt(_index);
                        _index++;
                    }
                this.OnRaiseUpdateEvent();
                return;
            }

            if (localMinimums.Count == 0 || ((localMinimums.Min() >= tempLocals.Min()) && localMinimums.Count != 0))
            {
                double var = tempLocals.Min();
                tempWeightSelected = weightsSet.ElementAt(tempLocals.IndexOf(var));
                this.localMinimums.Add(var);
                this.OnRaiseUpdateEvent(var, var);// second for minimums changes only
            }
            else
                this.OnRaiseUpdateEvent(tempLocals.Min());
            if (this.Delta > localdelta)
            {
                this.CalculationsDone = true;
                return;
            }
            iteration++;
            this.MakeGeneticStep();
        }

        private void MakeGeneticStep()
        {
            ObservableCollection<ObservableCollection<double>> tempWeightsSet = new ObservableCollection<ObservableCollection<double>>();
            //tournament selection method
            int l_viagraAnalisator = 0;

            m_needPopulationViagra = false;

            for (int indexer = 0; indexer < weightsSet.Count; indexer++)
            {
                ObservableCollection<double> firstParticipant = weightsSet.ElementAt((new Random()).Next(0, weightsSet.Count));
                ObservableCollection<double> secondParticipant = null;
                do// remove this cycle by adding different numbers into constructor
                {
                    ObservableCollection<double> temp = weightsSet.ElementAt((new Random(DateTime.Now.Millisecond)).Next(0, weightsSet.Count));
                    if (firstParticipant != temp)
                    {
                        secondParticipant = temp; break;
                    }
                    if (l_viagraAnalisator == weightsSet.Count * weightsSet.Count)
                    {
                        secondParticipant = temp;
                        m_needPopulationViagra = true;
                        break;
                    }
                    if (l_viagraAnalisator == weightsSet.Count * (weightsSet.Count-1)) Thread.Sleep(5);
                    l_viagraAnalisator++;

                } while (true);


                tempWeightsSet.Add(this.DoSelection(firstParticipant, secondParticipant));
            }

            ObservableCollection<ObservableCollection<double>> onemorecollection = new ObservableCollection<ObservableCollection<double>>(tempWeightsSet);
            if (!m_needPopulationViagra)
            {
                onemorecollection.Clear();
                l_viagraAnalisator = 0;
                for (int i = 0; i < weightsSet.Count / 2; i++)
                {
                    //take 2 random elements
                    int pos1, pos2;
                    pos1 = (new Random()).Next(0, tempWeightsSet.Count);
                    ObservableCollection<double> tempCol1 = tempWeightsSet.ElementAt(pos1);
                    ObservableCollection<double> tempCol2 = new ObservableCollection<double>();

                    do
                    {
                        if (tempWeightsSet.Count == 2)
                        {
                            pos2 = 1 - pos1;
                            tempCol2 = tempWeightsSet.ElementAt(pos2); break;
                        }
                        pos2 = (new Random()).Next(0, tempWeightsSet.Count);
                        ObservableCollection<double> tempo = tempWeightsSet.ElementAt(pos2);
                        if (tempo != tempCol1)
                        {
                            tempCol2 = tempo; break;
                        }
                        if (l_viagraAnalisator == weightsSet.Count * weightsSet.Count)
                        {
                            tempCol2 = tempo; //big probability of algorithm has come to a point
                            m_needPopulationViagra = true;
                            break;
                        }
                        l_viagraAnalisator++;
                    } while (true);
                    int point1 = (new Random(DateTime.Now.Millisecond)).Next(0, 16);

                    int point2 = (new Random(DateTime.Now.Millisecond)).Next(point1, 16);
                    for (int indexer = point1; indexer <= point2; indexer++)
                    {
                        double vv = tempCol1.ElementAt(indexer);
                        tempCol1.Insert(indexer, tempCol2.ElementAt(indexer));
                        tempCol1.RemoveAt(indexer + 1);

                        tempCol2.Insert(indexer, vv);
                        tempCol2.RemoveAt(indexer + 1);
                    }
                    onemorecollection.Add(tempCol1);
                    onemorecollection.Add(tempCol2);
                    if (pos1 < pos2) { tempWeightsSet.RemoveAt(pos2); tempWeightsSet.RemoveAt(pos1); }
                    else if (pos1 == pos2)
                    {
                        tempWeightsSet.RemoveAt(pos1);
                    }
                    else
                    {
                        tempWeightsSet.RemoveAt(pos1);
                        tempWeightsSet.RemoveAt(pos2);
                    }

                }
            }
            //mutation
            ObservableCollection<ObservableCollection<double>> nextcollection = new ObservableCollection<ObservableCollection<double>>();
            double l_mutationProbability = 0;
            if (m_needPopulationViagra) l_mutationProbability = 0.15;
            else l_mutationProbability = 0.5;
            for (int indexer = 0; indexer < onemorecollection.Count; indexer++)
            {
                ObservableCollection<double> nexttemp = onemorecollection.ElementAt(indexer);
                Random p = new Random();
                if (p.NextDouble() > l_mutationProbability)//triple mutation
                {
                    int pos = p.Next(0, 16);
                    nexttemp.Insert(pos, Math.Round((new Random()).NextDouble() * 2 - 1, 3));
                    nexttemp.RemoveAt(pos + 1);
                    Thread.Sleep(TimeSpan.FromTicks(20));
                    //pos = p.Next(pos, 16);
                    //nexttemp.Insert(pos, Math.Round((new Random()).NextDouble() * 2 - 1, 3));
                    //nexttemp.RemoveAt(pos + 1);

                    Thread.Sleep(TimeSpan.FromTicks(20));
                    pos = p.Next(pos, 16);
                    nexttemp.Insert(pos, Math.Round((new Random()).NextDouble() * 2 - 1, 3));
                    nexttemp.RemoveAt(pos + 1);

                    Thread.Sleep(TimeSpan.FromTicks(20));
                }
                nextcollection.Add(nexttemp);
            }

            weightsSet = nextcollection;

        }

        private ObservableCollection<double> DoSelection(ObservableCollection<double> firstParticipant, ObservableCollection<double> secondParticipant)
        {
            double deviation1 = 0, deviation2 = 0;
            foreach (ObservableCollection<bool> pic in this.trainingPictures)
            {
                deviation1 += (double)Math.Abs(this.EvaluateThreshHoldFunc(pic, firstParticipant));
                deviation2 += (double)Math.Abs(this.EvaluateThreshHoldFunc(pic, secondParticipant));
            }
            double temp = this.EvaluateThreshHoldFunc(this.etalonPictures.ElementAt(0), firstParticipant);
            if (temp < 1)
                deviation1 += 1 - temp;

            temp = this.EvaluateThreshHoldFunc(this.etalonPictures.ElementAt(0), secondParticipant);
            if (temp < 1)
                deviation2 += 1 - temp;

            return (deviation1 < deviation2) ? firstParticipant : secondParticipant;
        }

Скриншоты, описывающие за ход выполнения алгоритма:



Архив с решением доступен здесь

В данной работе была использована библиотека dynamic display library для отображения хода выполнения алгоритма в режиме реального времени. Данная библиотека доступна на сайте.

При выполнении ЛР были использованы следующие источники:
-"интеллектуальная" часть:
+Рутковская Д., Пилиньский М., Рутковский Л. - Нейронные сети, генетические алгоритмы и нечеткие системы (2006)
+конспекты лекций
-программная часть:
+msdn.microsoft.com
+stackoverflow.com
+статьи по теме: работа с потоками в C#
Описание разработки:
-язык программирования: C#
-техннология: WPF
-среда разработки: VS 2010
* для корректной работы прораммы на ПК необходимо установить:
-.net frameork v4.0
-entity framework v4.2

Комментариев нет:

Отправить комментарий