Лабораторная работа
по курсу
"Интеллектуальные системы":
Обучение нейронной сети с помощью генетических алгоритмов
Выполнил
студент группы ИУ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#
Описание разработки:
-язык программирования: C#
-техннология: WPF
-среда разработки: VS 2010
* для корректной работы прораммы на ПК необходимо установить:
-.net frameork v4.0
-entity framework v4.2