Исходные коды библиотеки нейронных сетей на F# для .NET Framework

Периодически я получаю запросы относительно исходных кодов нейронных сетей, использованных в моих работах по анализу тональности, генерации текстов, а также в статьях на Хабре. Поэтому я решил выложить всё-таки их в открытый доступ, вместе с библиотекой на которую они опираются, несмотря на достаточно сырой код. В этой статье я напишу где взять, что можно сделать и немного о том, как пользоваться. Написана библиотка на языке F#, но использовать ее можно из любого .NET языка.
Реализация
Библиотека для нейронных сетей написана мною на языке F#. Почему F#? Если коротко, то F# как язык имеет достоинства популярного сейчас Python (например такие как: простой синтаксис, возможность запускать скрипты в интерактивном режиме, кросс-платформенность) и очень хорошо подходит для быстрого прототипирования программ. Но существенным (для меня) отличием является статическая сильная типизация. F# имеет такую функцию как автоматический вывод типов, т.е. компилятор без явного объявления типов переменных может определить тип на этапе компиляции. Раньше я использовал Python и могу сказать, что лично мне переход на F# экономит много бессонных ночей отладки. У Python есть свои плюсы конечно, но в для меня они не перевесили минусов.

Одна беда, если для Python нейросетевых библиотек сегодня можно выбирать на любой вкус и цвет, то на F#, их не так много. А на момент начала разработки ситуация была еще хуже — было несколько библиотек для .NET Framework типа Encog, например, но новые архитектуры реализовать было на них нельзя без существенных изменений. Поэтому я написал свою с нуля. Это, в любом случае, весьма полезное упражнение для любого, кто хочет заниматься тематикой серьезно, т. к. позволяет глубже понять основные принципы.

Возможности
На сегодняшний день

  • Нейронные сети прямого распространения, произвольные комбинации слоев
  • Рекуррентные слои. Обучение методом обратного распространения ошибки через время
  • Двунаправленные рекуррентные сети
  • Разные функции активации, включая softmax и relU, возможность определять свои функции
  • Dropout, Noise layers, (можно сделать автокодировщик)
  • Проекционные слои (для векторов слов)
  • Одномерные сверточные слои для обработки текста, поддержка k-max pooling
  • Обучение: SGD, SGD+Momentum, RMSProp, RPROP
  • Полезные функции для работы с текстами, подсчета F1 и т. п.
  • Сохранение любой модели на диск и чтение с диска
  • Работа из любого .NET языка
  • Малый размер. Кроме .Net framework или mono не нужно дополнительно ставить ничего.
  • Есть пример кода, как сделать выделение терминов из текста
  • Разные экспериментальные дополнения


Минусы:

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

У меня реально нет возможности привести все в должный вид, написать и содержать в актуальном виде красивую документацию и все такое. Есть, конечно, слабая надежда, что кто-то заинтересуется этой разработкой и поможет.

Для чего может быть полезно? Как простая библиотека нейронных сетей для интеграции функционала в .NET приложения (я недавно использовал чтобы экспортировать в .NET проект разветвленную модель из библиотеки keras). Для опытов. Для желающих использовать F#. Для учебных целей — весь код реализован с нуля на F#, ничего не спрятано в разные другие библиотеки. Всем, кто спрашивал у меня исходные коды.

Средство годится для выделения терминов из текста, классификации текстов, и для генерации текстов тоже (хотя этого примера в комплекте нет, но нужные средства в библиотеки присутствуют).

Где взять?
https://github.com/Durham/NeuThink

Как начать использовать?
Вот пример реализации простой нейронной сети для функции XOR на F#:

open NeuThink.Neuron
open NeuThink.NeuronTraining

let() =
 let nn = new GeneralNetwork()
 nn.AddPerceptronLayer(4,2,[|1|],true,0)
 nn.AddPerceptronLayer(1,4,[|-1|],false,1)
 nn.FinalizeNet()

 let outputs = [|[|-1.0|];[|-1.0|];[|1.0|];[|1.0|]|]
 let inputs = [|[|1.0;1.0|];[|0.0;0.0|];[|1.0;0.0|];[|0.0;1.0|]|]

 MomentumSGD 100 nn (new NeuThink.DataSources.SimpleProvider(inputs)) (new NeuThink.DataSources.SimpleProvider(outputs)) 0.2  (Some([|0;0;0;0|])) None
 nn.SetInput([|1.0;1.0|])
 System.Console.WriteLine(nn.Compute().[0])

Здесь создана нейронная сеть с одним скрытым слоем из 4 нейронов и одним выходным слоем из 1 одного нейрона. Вообще-то для XOR достаточно 2 нейронов в скрытом слое, но минимальная сеть чаще сваливается в локальный минимум при обучении. Вместо 0 и 1 для выхода XOR использованы -1 и 1, потому что по умолчанию функция активации во всех слоях tanh, с выходным диапазоном от -1 до 1.

nn.AddPerceptronLayer(4,2,[|1|],true,0)


Означает добавление одного полностью соединенного слоя (их еще называют Dense или MLP слоями). 4 — число нейронов в слое, 2- число входов. [|1|] — это массив номеров слоев, к которым будет подсоединен данный слой (неудобно, да. зато работает и можно определить произвольный граф). Если данные никуда подавать не надо, нужно указать -1 (почему не пустой массив? так исторически получилось…). Следующий параметр означает, что входные данные будут поступать в этот слой, а 0 — указывает на порядок обработки слоя.

После добавления всех нужных слоев надо вызывать метод FinalizeNet (), он выполняет некоторые служебные вычисления.

Входные данные организованы в виде массива массивов float, каждый внутренний массив соответствует одному обучающему примеру. Выходные данные организованы также. Для обучения сети из массива надо сделать Источник Данных для нейронной сети. Это должен быть класс, реализующий интерфейс IInputProvider (для выходных данных) или IOutputProvider. Так сложно сделано для того случая, когда данные генерируются динамически или читаются с диска, потому, что не влезают в память. В простом случае сложностей не нужно и мы используем встроенный класс SimpleProvider, инициализируя его данными из массива.

Далее вызывается собственно функция обучения нейронной сети с начальной скоростью обучения 0.2 и числом итераций 100 штук. Затем обученную сеть можно использовать для предсказания результатов на новых данных.

Стоит отметить, что у F# есть интерактивная консоль, можно работать из нее:

4843eadd1500466d8304ecf620cddd0d.jpg

В общем, коротко пока все. Если возникнут вопросы или пожелания, обращайтесь.

© Habrahabr.ru