Нейронные сети на C#

ОГЛАВЛЕНИЕ

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

•    Скачать исходники - 251 Кб
•    Скачать демонстрационный проект - 181 Кб

Введение

История нейронных сетей начинается в 1950-х гг., когда была представлена архитектура простейших нейронных сетей. После начальной работы в области идея нейронных сетей стала весьма популярной. Но затем область угасла, когда обнаружилось, что нейронные сети тех времен годились для крайне ограниченного числа задач. В 1970-х гг. область снова расцвела, когда была представлена идея многослойных нейронных сетей с алгоритмом обучения с обратным распространением. С тех пор много разных исследователей изучало область нейронных сетей, что привело к появлению широкого диапазона разных нейронных архитектур, применявшихся к множеству разных задач. Теперь нейронные сети применяются к таким задачам, как классификация, распознавание, приближение, предсказание, кластеризация, моделирование памяти и множество других разных задач, число которых растет.

В данной статье описана библиотека C# для расчетов нейронной сети. Библиотека реализует несколько известных архитектур нейронной сети и их алгоритмы обучения, такие как обратное распространение, самоорганизующаяся карта Кохонена, эластичная сеть, обучение по дельта-правилу и обучение перцептрона. Применение библиотеки показано на нескольких примерах:

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

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

Статья не дает полную теорию нейронных сетей, которую легко найти на множестве разных ресурсов по всему интернету и на CodeProject. Вместо этого статья предполагает, что у читателя есть общие знания о нейронных сетях, и поэтому цель статьи – обсудить библиотеку C# для вычислений нейронной сети и ее применение к разным задачам.

Применение библиотеки

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

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

Библиотека содержит шесть основных сущностей:
•    Neuron – базовый абстрактный класс для всех нейронов, инкапсулирующих такие общие сущности, как вес нейрона, выходное значение и входное значение. Другие классы нейрона наследуются от базового класса, чтобы расширить его дополнительными свойствами и специализировать его.
•    Layer – представляет коллекцию нейронов. Это базовый абстрактный класс, инкапсулирующий общий функционал всех слоев нейронов.
•    Network – представляет нейронную сеть, является коллекцией слоев нейронов. Это базовый абстрактный класс, предоставляющий общий функционал типовой нейронной сети. Для реализации конкретной архитектуры нейронной сети требуется унаследовать класс, расширив его специфичным функционалом любой архитектуры нейронной сети.
•    IActivationFunction – интерфейс функции активации. Функции активации используются в нейронах активации – типе нейрона, где вычисляется взвешенная сумма его входов, и затем значение передается на вход функции активации, и выходное значение становится выходным значением нейрона.
•    IUnsupervisedLearning – интерфейс для алгоритмов неуправляемого обучения – типа алгоритмов обучения, где системе даются образцы входов только на этапе обучения, но без желаемых выходов. Задача системы – организоваться так, чтобы найти взаимосвязь и сходства между выборками данных.
•    ISupervisedLearning - интерфейс для алгоритмов управляемого обучения – типа алгоритмов обучения, где системе на этапе обучения даются образцы входов вместе с желаемыми выходными значениями. Задача системы – обобщить учебные данные и научиться предоставлять правильное выходное значение, когда ей предъявляется только входное значение.

Библиотека предоставляет следующие архитектуры нейронной сети:

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

Разные алгоритмы обучения применяются для обучения разных нейронных сетей и для решения разных задач:
•    Обучение перцептрона  – алгоритм считается первым алгоритмом обучения нейронной сети, история которого начинается с 1957 г. Алгоритм применяется к однослойной сети активации, где каждый нейрон имеет пороговую функцию активации. Диапазон его применений мал и ограничен классификацией линейно разделимых данных.
•    Обучение по дельта-правилу – алгоритм является следующим шагом после алгоритма обучения перцептрона. Он использует производную функции активации и применим только к однослойным сетям активации, где каждый нейрон имеет непрерывную функцию активации вместо пороговой функции активации. Самая известная непрерывная функция активации – однополярная и двухполярная сигма-функция. Поскольку алгоритм применим только к однослойным сетям, он ограничен некоторыми задачами классификации и распознавания.
•    Обучение с обратным распространением  – один из самых известных алгоритмов для обучения многослойной нейронной сети. Изначально он был описан в 1974г., и с тех пор он активно изучался и применялся к широкому спектру разных задач. Поскольку алгоритм способен обучать многослойные нейронные сети, диапазон его применения очень большой и включает в себя такие задачи, как приближение, предсказание, распознавание объекта и т.д.
•    Обучение SOM  – этот алгоритм был разработан Кохоненом и считается одним из самых известных алгоритмов неуправляемого обучения для задач кластеризации. Он рассматривает нейронную сеть как двумерную карту узлов, где каждый узел представляет отдельный класс. Алгоритм организует сеть так, что ей удается находить взаимосвязь и сходства между выборками данных.
•    Обучение эластичной сети  – алгоритм походит на идею алгоритма обучения SOM, но рассматривает нейроны сети не как двумерную карту узлов, а как кольцо. В ходе обучения кольцо приобретает некоторую форму, представляющую решение. Одна из самых распространенных демонстраций этого алгоритма обучения – задача коммивояжера (TSP).

В качестве дополнительного источника информации библиотека снабжена справочной информацией, распространяемой в формате справки HTML.


Классификация

Этот пример показывает использование однослойной сети активации с пороговой функцией активации и применением алгоритма обучения перцептрона. Количество нейронов сети равняется количеству разных классов данных, и каждый нейрон обучен классифицировать только определенный класс. При передаче выборки данных обученной сети один нейрон сети должен активироваться (выработать выход, равный 1), но все остальные нейроны должны отключиться (выработать выход, равный 0). Класс выборки данных определяется по номеру активированного нейрона. Если активируются несколько нейронов или ни один из них, то сеть не способна правильно классифицировать предъявленную выборку данных. В случае двумерных выборок данных легко наглядно представить сеть, потому что веса и пороговое значение каждого нейрона представляет собой линию, отделяющую один класс от всех остальных.

// подготовить учебные данные
double[][] input = new double[samples][];
double[][] output = new double[samples][];
// ... подготовка данных ...

// создать перцептрон
ActivationNetwork network = new ActivationNetwork( new ThresholdFunction( ),
                                                   2, classesCount );
// создать учителя
PerceptronLearning teacher = new PerceptronLearning( network );
// установить скорость обучения
teacher.LearningRate = learningRate;
// цикл
while ( ... )
{
    // выполнить эпоху процедуры обучения
    double error = teacher.RunEpoch( input, output );
    ...
}

Несмотря на простоту архитектуры сети, ее можно использовать для множества разных задач классификации/распознавания. Единственный недостаток этой архитектуры в том, что сеть может классифицировать только линейно разделимые данные.

Приближение

Этот пример показывает использование многослойных нейронных сетей, обученных по алгоритму обратного распространения, применяемому к задаче приближения функции. Допустим, что известно значение функции лишь в ограниченном количестве точек, но надо вычислить функцию в каких-то других точках, находящихся в пределах значений min и max X, для которых известно значение функции. В ходе этапа обучения сеть обучают вырабатывать правильное значение функции для предъявленного значения X. После окончания обучения сеть используется для вычисления значения функции для разных других значений, которые не были доступны сети в ходе процедуры обучения.

// подготовить учебные данные
double[][] input = new double[samples][];
double[][] output = new double[samples][];
// ... подготовка данных ...
// создать многослойную нейронную сеть
ActivationNetwork    network = new ActivationNetwork(
    new BipolarSigmoidFunction( sigmoidAlphaValue ),
    1, neuronsInFirstLayer, 1 );
// создать учителя
BackPropagationLearning teacher = new BackPropagationLearning( network );
// установить скорость и темп обучения
teacher.LearningRate = learningRate;
teacher.Momentum     = momentum;
// цикл
while ( ... )
{
    // выполнить эпоху процедуры обучения
    double error = teacher.RunEpoch( input, output ) / samples;
    ...
}

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

Предсказание временного ряда

Этот пример тоже показывает многослойную нейронную сеть с алгоритмом обучения с обратным распространением, но примененным к другой задаче – предсказанию временного ряда. Задача предсказания временного ряда является очень важной и известной, и много исследователей работает над ней, пробуя множество разных алгоритмов и методов для задачи. Легко объяснить популярность задачи, взглянув на области ее применения. Одна из популярных областей – трейдинг. Если вы сможете предсказать будущие стоимости акций или курсы обмена валют и если сможете сделать это поистине хорошо, то станете богачом. Но надо вернуться к нейронным сетям. В ходе этапа обучения определенное количество предыдущих значений временного ряда предъявляется сети, и сеть обучают предсказывать следующее значение временного ряда. Чем больше учебных выборок есть, тем лучше будет полученная модель прогнозирования. Также очень важный параметр – размер окна – сколько значений из истории используется для предсказания будущего значения. Чем больше размер окна, тем лучше будет полученная модель, но может быть и не так – в зависимости от временного ряда, и требуются эксперименты. Но больший размер окна уменьшает количество требуемых учебных выборок, поэтому это компромиссное значение.

Пример кода для данной задачи аналогичен коду предыдущего примера, изменена только процедура подготовки учебных данных.


Кластеризация цвета

Это очень простой пример, демонстрирующий процесс построения самоорганизующейся карты Кохонена. Создается квадратная сеть SOM, состоящая из 10000 нейронов со случайно инициализированными весами. Каждый нейрон имеет три веса, интерпретируемые как значения RGB для отображения. Начальное отображение сети покажет какую-либо ярко раскрашенную карту. В ходе процедуры обучения выбираются случайные цвета и последовательно передаются входу сети. Повторяя итерации обучения, нейронная сеть организуется так, чтобы больше не выглядеть как случайная картинка при отображении, но она получает определенную структуру - цвета, близкие к палитре RGB, тоже размещаются ближе на карте Кохонена.

// установить диапазон рандомизации весов нейронов
Neuron.RandRange = new DoubleRange( 0, 255 );
// создать сеть
DistanceNetwork network = new DistanceNetwork( 3, 100 * 100 );
// создать алгоритм обучения
SOMLearning trainer = new SOMLearning( network );
// вход
double[] input = new double[3];
// цикл
while ( ... )
{
    // обновить скорость и радиус обучения
    // ...

    // подготовить вход сети
    input[0] = rand.Next( 256 );
    input[1] = rand.Next( 256 );
    input[2] = rand.Next( 256 );

    // выполнить итерацию обучения
    trainer.Run( input );
   
    ...
}

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

Задача коммивояжера

Задача коммивояжера показывает применение эластичной сети, похожей на SOM в плане самоорганизации, но отличающейся в интерпретации нейронной сети. SOM интерпретирует нейронную сеть как двумерную карту узлов, а эластичная сеть интерпретирует ее как кольцо узлов. В ходе этапа обучения векторы свойств последовательно предъявляются сети, что заставляет сеть принять какую-либо форму, представляющую решение. В случае задачи TSP каждый нейрон сети имеет два веса, представляющие координаты (X, Y). В ходе этапа обучения координаты произвольных городов последовательно передаются входу сети, и сеть организует свои веса так, чтобы они представляли путь коммивояжера.

// установить диапазон случайных генераторов
Neuron.RandRange = new DoubleRange( 0, 1000 );
// создать сеть
DistanceNetwork network = new DistanceNetwork( 2, neurons );
// создать алгоритм обучения
ElasticNetworkLearning trainer = new ElasticNetworkLearning( network );
// вход
double[] input = new double[2];
// цикл
while ( ... )
{
    // установить вход сети
    int currentCity = rand.Next( citiesCount );
    input[0] = map[currentCity, 0];
    input[1] = map[currentCity, 1];

    // выполнить одну итерацию обучения
    trainer.Run( input );
    ...
}

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

Заключение

Пять представленных примеров показывает, что начальная цель библиотеки была достигнута – она гибкая, многократно используемая и легко применимая к разным задачам. Хотя надо сделать еще много работы из-за большого диапазона разных архитектур нейронной сети и их алгоритмов обучения, тем не менее библиотека применима к множеству разных задач и может быть расширена, чтобы решать еще больше задач. Следует надеяться, что библиотека будет полезной и интересной для разных исследователей.