Pull to refresh

Использование нейронной сети Хопфилда для решения простейшей задачи

Reading time9 min
Views12K
image В настоящее время достаточно интересной областью программирования является использование нейронных сетей. Не смотря на всю перспективность этой идеи, большинство реализаций которые я встречал были связаны с различным анализом статистических выборок и предсказанием возможного значения какого-либо параметра.

Разговоры об искусственном интеллекте и громоздких экспертных системах — это конечно все хорошо, но как всю эту теорию приблизить к жизни, к нашим приклодным задачам?

Немного о задаче

Требуется написать программу, которая в «зашумленной картинке» будет распознавать эталонные образы.

Выбор нейронной сети

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

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

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

В общем случае, любой сигнал может быть описан вектором X = {xi: i = 1, 2, ..., n},
n — число нейронов в сети и размерность входных и выходных векторов. Каждый элемент хi равен либо +1, либо -1.
Существует вектор, описывающий k-й образец. Когда сеть распознает (или «вспомнит») какой-либо образец на основе предъявленных ей данных, все выходы будут содержать именно его, т.е. Y вектор выходных значений сети: Y ={уi: i = 1, 2, ..., n}. В противном случае, если зашумление слишком сильное на выходах будет «мусор».

На стадии инициализации сети весовые коэффициенты синапсов устанавливаются следующим образом:
image
Здесь i и j — индексы, соответственно, предсинаптического и постсинаптического нейронов;

Алгоритм функционирования сети следующий (t номер итерации):
1. На входы сети подается неизвестный сигнал. Фактически его ввод осуществляется непосредственной установкой значений:

image
поэтому обозначение па схеме сети входных синапсов в явном виде носит чисто условный характер. Ноль в скобке справа от yi означает нулевую итерацию в цикле работы сети.
2. Рассчитывается новое состояние нейронов
image
и новые значения аксонов
image
3. Проверка, изменились ли выходные значения аксонов за последнюю итерацию. Если да переход к пункту 2, иначе (если выходы застабилизировались) — конец. При этом выходной вектор представляет собой образец, наилучшим образом сочетающийся с входными данными.

Таким образом, когда подается новый вектор, сеть переходит из вершины в вершину, пока не стабилизируется. Устойчивая вершина определяется сетевыми весами и текущими входами. Если из вершины в вершину, пока не стабилизируется. Устойчивая вершина определяется сетевыми весами и текущими входами. Если входной вектор частично неправилен или неполон, сеть стабилизируется в вершине, ближайшей к желаемой.
Доказано, что достаточным условием устойчивой работы такой сети является выполнение условий:
Wij = Wji, Wii= 0.

Как я уже говорил, иногда сеть не может провести распознавание и выдает на выходе несуществующий образ. Это связано с проблемой ограниченности возможностей сети. Для сети Хопфилда число запоминаемых образов N не должно превышать величины, примерно равной 0,15n. Кроме того, если два образа А и В сильно похожи, они, возможно, будут вызывать у сети перекрестные ассоциации, т.е. предъявление на входы сети вектора А приведет к появлению на ее выходах вектора В и наоборот.

Приступим к написанию программы

И так для нейронной сети необходима бинарная последовательность (в нашем случае -1/1).
Пусть «1» — это черный цвет пикселя, а "-1" — это белый,
таким образом сможем преобразовать картинку в последовательность.

Для быстроты и точности востановления будем использовать следующую схему:
Картинка 100х100 пикселей и на каждый пиксель приходится свой нейрон.
Таким образом сеть будет состоять из 10 000 нейронов.

Начнем с представления элементарный еденицы — нейрон:

//описание нейрона
private class Neuron       
{
 //изменение состояние нейрона
 public void ChangeState()
 {
  if (s > 0) y = 1;
  if (s < 0) y = -1;
  if (s == 0) y = 0;
 }
 public int s;
 public int x; //вход
 public int y; //выход
 public int index;
}


* This source code was highlighted with Source Code Highlighter.


Теперь рассмотри саму нейронную сеть, думаю для лучшего понимания следует привести основную часть листинга=)

namespace Neurons
{
  class NetHopfild
  {
    //описание нейрона
    private class Neuron       
    {
      ...
    }

    private const int size = 10000;
    public NetHopfild()
    {
      //инстанциирование нейронов, мытрицы связей
      mass = new Neuron[size];
      for (int i = 0; i < size; i++)
      {
        mass[i] = new Neuron();
      }
      matrix_of_connect = new int[size, size];
      last_y = new int[size];
      //
      for (int i = 0; i < size; i++)
      {
        mass[i].index = i;
      }
      for (int i = 0; i < size; i++)
      {
        for (int j = 0; j < size; j++)
        {
          matrix_of_connect[i,j] = 0;
        }
      }
    }

    public void Initialise(int []input)
    {
      for (int i = 0; i < size; i++)
      {
        for (int j = 0; j < size; j++)
        {
          if (i == j) matrix_of_connect[i,j] = 0;
          else matrix_of_connect[i,j] += input[i] * input[j];
        }
      }
    }

    public void FindImage(int []input)
    {
      for (int i = 0; i < size; i++)   //заносим входящие значения
         {
          mass[i].x = input[i];
          last_y[i] = mass[i].y;
         }

      for (int k = 0; k < size; k++)   //сначало вычисляем S потом У
         {
          mass[k].s = 0;
          for (int i = 0; i < size; i++)
            {
              mass[k].s = mass[k].s + matrix_of_connect[i,mass[k].index] * mass[i].x;
            }
           mass[k].ChangeState();
         }
        bool flag = true;
        //проверяем на равенство входного и выходного векторов    
         for (int i = 0; i < size; i++)      
           {
            if(last_y[i]!=mass[i].y)
             {
              flag = false;
              break;
             }
           }
       if(flag == false)
       {
         int[] temp = new int[size];
         for (int i = 0; i < size; i++)
          {
           temp[i] = mass[i].y;
          }
         //если неустойчивое состояние, то рекурсивный вызов функции, пока не будет стабилизации сети
         FindImage(temp);
       }
    }
    private Neuron []mass;
    private int[,] matrix_of_connect;
    private int[] last_y;
    //индексатор для синапсов нейронов (выходные значения каждого нейрона)
    public int [] Synapses
    {
      ...
    }
  };
}


* This source code was highlighted with Source Code Highlighter.


И так сеть готова, теперь будем с ней работать.

В данном случае я не буду описывать весь исходник, опишу лишь основные моменты

Преобразование картинки в бинарную последовательность

    private int [] BitmapToIntArray()
    {
      //теперь перегоняем картинку в последовательность 1 и -1
      Color currentPixel;
      int[] temp = new int[size];
      int index = 0;
      for (int i = 0; i < picture.Height; i++)
      {
        for (int j = 0; j < picture.Width; j++)
        {
          currentPixel = picture.GetPixel(j, i);
          currentPixel.ToArgb();
          if (currentPixel == Color.FromArgb(255, 255, 255))
          {
            temp[index] = -1;
          }
          else
          {
            temp[index] = 1;
          }
          index++;
        }
      }
      return temp;
    }


* This source code was highlighted with Source Code Highlighter.


Результаты

Пусть есть два образа-эталона

imageimage
Обучаем нашу нейронную сеть

image

Аналогично обучаем второму образу.

image

Создадим помехи в картинке:

image

Загружаем в програмку искаженную картинку:

image

Жмем «Восстановить», через несколько секунд:

image

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

imageА так как у нас сеть оперирует только с бинарными последовательностями, то думаю не составит труда переписать эту программку, для восстановления например опечаток в словах=) и прочие вкусности=)

Для тех кому интересно выкладываю архив с образами и зашумлениями, сама програмка

Ну и конечно если кто-то совсем проникнится идеей и понадобятся сорцы, напишите в коментариях, я обязательно выложу=)

UPD: Поправил страсти в коде=) На которые указали: graninas, Nagg, Peregrinus
Tags:
Hubs:
+46
Comments29

Articles

Change theme settings