1. Molen26.04.2024 в 16:36от
Загрузка...

Нейронная сеть Хопфилда или напиши сам FineReader

Тема в разделе "Программирование", создана пользователем Egoist, 28.03.2017.

  1. Egoist

    ҉҉҉҉҉҉҉҉҉҉҉҉҉҉
    Egoist

    Статус:
    Оффлайн
    Регистрация:
    28.08.13
    Сообщения:
    178
    Репутация:
    224 +/-
    Статья посвящена введению в нейронные сети и примеру их
    реализации. В первой части дано небольшое теоретическое введение в нейронные сети
    на примере нейронной сети Хопфилда. Показано, как осуществляется обучение сети и
    как описывается ее динамика. Теоретически показана способность нейронной сети Хопфилда
    очищать от шума заранее обученный ключевой образ. Во второй части показано, как
    можно реализовать алгоритмы, описанные в первой части при помощи языка С++. Разработанная
    программа наглядно показывает способность нейронной сети очищать от шума ключевой
    образ. В конце статьи есть ссылка на исходный код проекта.
    Теоретическое введение
    Введение

    Для начала, необходимо определить, что такое нейрон. В
    биологии нейрон - специализированная клетка, из которой состоит нервная система. Биологический нейрон имеет строение
    [​IMG]
    Нейронную сеть можно ввести как совокупность нейронов и их взаимосвязей. Следовательно, для того, чтобы определить искусственную (не биологическую) нейронную сеть необходимо:
    • Задать архитектуру сети;
    • Определить динамику отдельных элементов сети - нейронов;
    • Определить правила, по которым нейроны будут взаимодействовать между собой;
    • Описать алгоритм обучения, т.е. формирования связей для решения поставленной задачи.
    В качестве архитектуры нейронной сети будет использоваться сеть Хопфилда. Данная модель, видимо, является наиболее распространенной математической моделью в нейронауке. Это обусловлено ее простотой и наглядность. Сеть Хопфилда показывает, каким образом может быть организована память в сети из элементов, которые не являются очень надежными. Экспериментальные данные показывают, что при увеличении количества вышедших из строя нейронов до 50%, вероятность правильного ответа крайне близка к 100%.

    Формальное описание сети Хопфилда
    Сеть состоит из N искусственных нейронов, аксон каждого нейрона связан с дендритами остальных нейронов, образуя обратную связь. Архитектура сети
    [​IMG]
    Каждый нейрон может находиться в одном из 2-х состояний:

    [​IMG] (1)
    где S(t) - состояние нейрона в момент t. "Возбуждению" нейрона соответствует +1, а "торможению" -1. Дискретность состояний нейрона отражает нелинейный, пороговый характер его функционирования и известный в нейрофизиологи как принцип "все или ничего".

    Динамика состояния во времени i-ого нейрона в сети из N нейронов описывается дискретной динамической системой:

    [​IMG] (2)
    где [​IMG] - матрица весовых коэффициентов, описывающих взаимодействие дендритов i-ого нейрона с аксонами j-ого нейрона.

    Стоит отметить, что [​IMG] и случай [​IMG] не рассматривается.

    Обучение и устойчивость к шуму
    Обучение сети Хопфилда выходным образам [​IMG] сводится к вычислению значений элементов матрицы [​IMG]. Формально можно описать процесс обучения следующим образом: пусть необходимо обучить нейронную сеть распознавать M образов, обозначенных [​IMG]. Входной образ [​IMG] представляет собой: [​IMG] где [​IMG] - шум, наложенный на исходный образ. Фактически, обучение нейронной сети - определение нормы в пространстве образов [​IMG]. Тогда, очистка входного образа от шума можно описать как минимизацию выражения [​IMG]

    Важной характеристикой нейронной сети является отношение числа ключевых образов M, которые могут быть запомнены, к числу нейронов сети N: [​IMG]. Для сети Хопфилда значение [​IMG] не больше 0.14.

    Вычисление квадратной матрицы [​IMG] размера NxN для M ключевых образов производится по правилу Хебба:

    [​IMG] (3)
    где [​IMG] означает i-ый элемент образа [​IMG]

    Стоит отметить, что в силу коммутативности операции умножения, соблюдается равенство

    [​IMG] (4)
    Входной образ, который предъявляется для распознавания соответствует начальным данным для системы, служащий начальным условием для динамической системы (2):

    [​IMG] (5)
    Уравнений (1), (2), (3), (5) достаточно для определения искусственной нейронной сети Хопфилда и можно перейти к ее реализации.

    Реализация нейронной сети Хопфилда
    Реализация нейронной сети Хопфилда, определенной выше будет производиться на языке C++. Для начала, необходимо определить дополнительные типы данных (подробнее см. листинг 1)

    • тип весовых коэффициентов (выбран тип float);
    • тип, описывающий состояние нейрона (выбран собственный введеный тип-перечисление);
    • тип, описывающий состояние сети в момент t (выбран стандартный контейнер vector);
    • тип, описывающий матрицу весовых коэффициентов связей нейронов (выбран контейнер vector контейнеров vector).
    Листинг 1. Определение новых типов
    enum neuron_state {LOWER_STATE=-1, UPPER_STATE=1};
    typedef float koef_t;
    typedef vector<neuron_state> neurons_line;
    typedef vector<vector<koef_t>> link_koefs;
    Обучение сети, или, точнее, вычисление элементов матрицы [​IMG] в соответствии с (3) производится функцией LearnNeuroNet, принимающей на вход список обучающих образов и возвращающей объект типа link_koefs_t. Значения [​IMG] вычисляются только для нижнетреугольный элементов. Значения верхнетреугольных элементов вычисляются в соответствии с выражением (4). Общий вид метода LearnNeuroNet показан в листинге 2.

    Листинг 2. Обучение нейронной сети
    link_koefs LearnNeuroNet(const list &src_images) {
    link_koefs result_koefs;
    size_t neurons_count = src_images.front().size();
    result_koefs.resize(neurons_count);
    for (size_t i = 0; i < neurons_count; ++i) {
    result_koefs.resize(neurons_count, 0);
    }
    for (size_t i = 0; i < neurons_count; ++i) {
    for (size_t j = 0; j < i ; ++j ) {
    koef_t val = 0;
    val = std::accumulate(begin(src_images), end(src_images), koef_t(0.0), [i, j] (koef_t old_val, const neurons_line &image) -> koef_t{
    return old_val + (image * image[j]);
    });
    result_koefs[j] = val;
    result_koefs[j] = val;
    }
    }
    return result_koefs;
    }


    Обновление состояний нейронов реализовано с помощью функтора neuro_net_system. В перегруженный оператор ( ) функтора передается начальное состояние функтора передается начальное состояние [​IMG] , являющееся входным распознаваемых образом (в соответствии с (5)) - указатель на объект типа neurons_line.

    Оператор ( ) функтора модифицирует передаваемый объект типа neurons_line до состояния нейронной сети в момент времени T. Значение T жестко не фиксировано и определяется выражением:

    [​IMG] (6)
    т.е., когда состояние каждого нейрона не изменилось за 1 "такт".

    Для вычисления (2) применены 2 алгоритма STL:




      • inner_product для вычисления суммы произведений весовых коэффициентов и состояний нейронов (т.е. вычисление (2) для определенного i);
      • transform для вычисления новых значений для каждого нейрона (т.е. вычисление пункта выше для каждого возможного i)
    Исходный код функтора neurons_net показан в листинге 3.

    Листинг 2. Функтор, реализующий работу нейронной сети
    struct neuro_net_system {

    const link_koefs &_koefs;

    size_t _steps_processed;


    neuro_net_system(const link_koefs &koefs): _koefs(koefs) {}


    bool Execute(neurons_line *line) {

    bool value_changed = false;


    neurons_line old_values(begin(*line), end(*line));

    link_koefs::const_iterator it_koefs = begin(_koefs);


    std::transform(

    begin(*line),

    end(*line),

    begin(*line),

    [&old_values, &it_koefs, &value_changed] (neuron_state old_value) -> neuron_state {

    koef_t tmp_val = std::inner_product(

    begin(old_values),

    end(old_values),

    begin(*it_koefs++),

    koef_t(0)

    );

    neuron_state new_value = (tmp_val > 0 ? UPPER_STATE : LOWER_STATE);

    value_changed = (new_value != old_value) || value_changed;

    return new_value;

    });
    return value_changed;
    }
    neurons_line* operator () (neurons_line *line) {

    bool need_continue = true;

    _steps_processed = 0;

    while (need_continue) {

    need_continue = Execute(line);

    ++_steps_processed;
    }
    return line;
    }
    };
    Для вывода в консоль входных и выходных образов создан тип neurons_line_print_descriptor, который хранит ссылку на образ и формат форматирования (ширину и высоту прямоугольника, в который будет вписан образ). Для этого типа переопределен оператор <<. Исходный код типаneurons_line_print_descriptor и оператора вывода в поток показан в листинге 4.

    Листинг 4. Обучение нейронной сети
    struct neurons_line_print_descriptor {

    const neurons_line &_line;

    const size_t _width;

    const size_t _height;


    neurons_line_print_descriptor (const neurons_line &line, size_t width, size_t height): _line(line), _width(width), _height(height) {}

    };


    template <typename Ch, typename Tr>

    std::basic_ostream<Ch, Tr>& operator << (std::basic_ostream<Ch, Tr>&stm, const neurons_line_print_descriptor &line) {

    neurons_line::const_iterator it = begin(line._line), it_end = end(line._line);


    for (size_t i = 0; i < line._height; ++i) {

    for (size_t j = 0; j 0)

    cout << " ";

    else

    cout << "0";

    ++it;

    }

    cout << endl;

    }


    return stm;

    }

    Рабочий пример можно скачать отсюда|VT.
    Пример представляет собой проект MS VisualStudio 2010. Пример компилируется компиляторами VC++ и Intel C++ (другие компиляторы не проверялись).

    Пример работы нейронной сети
    Для проверки работоспособности реализации, нейронная сеть была обучена 2 ключевым образам:

    [​IMG][​IMG]

    Рис.3 Ключевые образы

    На вход подавались искаженные образы. Нейронная сеть корректно распознала исходные образы. Искаженные образы и распознанные образы показаны на рис.4, 5

    [​IMG][​IMG]

    Рис.4 Распознавание образа 1

    [​IMG][​IMG]

    Рис.5 Распознавание образа 2

    Запуск программы производится из командной строки строчкой вида: AppName WIDTH HEIGHT SOURCE_FILE [LEARNE_FILE_N], где:




      • AppNaame - название исполняемого файла;
      • WIDTH, HEIGHT - ширина и высота прямоугольника, в который будут вписываться выходной и ключевые образы;
      • SOURCE_FILE - исходный файл с начальным образом;
      • [LEARNE_FILE_N] - один или несколько файлов с ключывыми образами (через пробел).
     
  2. Vanya

    ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ
    Vanya

    Статус:
    Оффлайн
    Регистрация:
    29.01.15
    Сообщения:
    6.417
    Репутация:
    4.725 +/-
    ааааа
    многа букаф
    соокаа
    сложна сложна
     
  3. Hlovvi

    Hlovvi

    Статус:
    Оффлайн
    Регистрация:
    27.03.17
    Сообщения:
    17
    Репутация:
    5 +/-
    CУКОА
    Шо это зы язык
     
  4. Egoist

    ҉҉҉҉҉҉҉҉҉҉҉҉҉҉
    Egoist

    Статус:
    Оффлайн
    Регистрация:
    28.08.13
    Сообщения:
    178
    Репутация:
    224 +/-
  5. TheOne

    TheOne

    Статус:
    Оффлайн
    Регистрация:
    14.08.17
    Сообщения:
    6
    Репутация:
    0 +/-
    Странно что не питон) Тема сама по себе очень крутая, для тех кто хочет с нуля разобраться есть от майкрософта неплохой курс на edx.org по глубинному обучению (Deep Learning Explained), правда у меня времени как всегда нет на него((