нейронные сети на ruby

Недавно я узнал о том, что больше не надо заморачиваться и писать всю логику работы нейросетки. Собственно говоря, это уже давно можно было не делать благодаря существованию такой библиотеки на C как Fast Artificial Neural Network. А спустя считанные минуты я с удивлением обнаружил, что FANN уже давно портатированна на различные языки более высокого уровня, включая PHP, Python и, конечно, апогей человеческого гения – Ruby.
Касперский пиарится на Красном Октябре
Для Ruby интерфейс к FANN обеспечивает гем ruby-fann.

установка

Для установки его под рельсы надо закинуть в Gemfile

gem 'ruby-fann'

и забандлить

bundle install

установка graphviz

Еще для отображения графической схемы нейронной сети можно воспользоваться graphviz. Эта штука вроде как должна экспортировать графическую схему сетки в PNG или VRML.

yum install graphviz
gem install ruby-graphviz

эксплуатация

У гема есть подробная документация.
В качестве примера я не буду дублировать код на сайте гема, а приведу нейросеть для решения классической задачи с XOR, только с большим количеством нейронов.

Код упакован в rake-файл рельс.

namespace :test do
  desc 'test NN'
  task :nn => :environment do
 
    require 'ruby_fann/neural_network'
    require 'ruby_fann/neurotica' #только для graphviz
 
    #объявляется ИНС
    fann = RubyFann::Standard.new(
	:num_inputs=>2, #входы
	#кол-во нейронов на первом и 
        #втором уровнях соответственно
	:hidden_neurons=>[3, 2], 
	:num_outputs=>1 #выходы
    )
 
    #обучение
    pairs=[[0,0],[1,0],[0,1],[1,1]] #учебные данные
    training_data = RubyFann::TrainData.new(
  	:inputs=>pairs, 
  	#правильные результаты в соответствии с учебными данными
  	:desired_outputs=>[[0],[1],[1],[0]] 
    )
 
    #собственно само обучение
    fann.train_on_data(
	training_data, #данные для обучения
	1000, #макс. кол-во эпох
	1, #кол-во эпох спустя которые выводить рез-тат
	0.01 #допустимая погрешность
    )
 
    #проверка обученности сети
    pairs.each do |pair|
	puts "#{pair}: #{fann.run(pair)}"
    end
 
    #вывод полученных нейронов
    fann.get_neurons.each {|n| p n}
 
    #вывод графической схемы
    graph=RubyFann::Neurotica.new()
    graph.graph(fann, 'xor_nn.png')
  end
end
$ bundle exec rake test:nn
Max epochs     1000. Desired error: 0.0099999998.
Epochs            1. Current error: 0.2501267791. Bit fail 4.
Epochs            2. Current error: 0.2520069778. Bit fail 4.
Epochs            3. Current error: 0.2551138699. Bit fail 4.
Epochs            4. Current error: 0.2513942719. Bit fail 4.
Epochs            5. Current error: 0.2500072420. Bit fail 4.
...
Epochs          370. Current error: 0.0440760627. Bit fail 1.
Epochs          371. Current error: 0.0343447179. Bit fail 0.
Epochs          372. Current error: 0.0233961642. Bit fail 0.
Epochs          373. Current error: 0.0175627228. Bit fail 0.
Epochs          374. Current error: 0.0113563286. Bit fail 0.
Epochs          375. Current error: 0.0067548323. Bit fail 0.
[0, 0]: [0.045396712927026114]
[1, 0]: [0.9389753126711914]
[0, 1]: [0.9501094945639519]
[1, 1]: [0.061412697926247546]
{:activation_function=>:linear, :activation_steepness=>0.0, :layer=>0, :sum=>0.0, :value=>1.0, :connections=>[]}
{:activation_function=>:linear, :activation_steepness=>0.0, :layer=>0, :sum=>0.0, :value=>1.0, :connections=>[]}
{:activation_function=>:sigmoid_stepwise, :activation_steepness=>0.5, :layer=>1, :sum=>1.3608390766086655, :value=>0.9258633161869951, :connections=>[0, 1, 2]}
{:activation_function=>:sigmoid_stepwise, :activation_steepness=>0.5, :layer=>1, :sum=>5.5199047483640955, :value=>1.0, :connections=>[0, 1, 2]}
{:activation_function=>:sigmoid_stepwise, :activation_steepness=>0.5, :layer=>1, :sum=>1.7510475747029322, :value=>0.9606836699618748, :connections=>[0, 1, 2]}
{:activation_function=>:sigmoid_stepwise, :activation_steepness=>0.5, :layer=>2, :sum=>-2.005618142945213, :value=>0.029562103469001694, :connections=>[3, 4, 5, 6]}
{:activation_function=>:sigmoid_stepwise, :activation_steepness=>0.5, :layer=>2, :sum=>-2.3070915806646646, :value=>0.018010737799533, :connections=>[3, 4, 5, 6]}
{:activation_function=>:sigmoid_stepwise, :activation_steepness=>0.5, :layer=>3, :sum=>-1.4195548161431304, :value=>0.061412697926247546, :connections=>[7, 8, 9]}
rake aborted!
graph attribute 'output' invalid
/usr/local/rvm/gems/ruby-1.9.2-p290/gems/ruby-graphviz-1.0.8/lib/graphviz/attrs.rb:53:in `[]='
/usr/local/rvm/gems/ruby-1.9.2-p290/gems/ruby-graphviz-1.0.8/lib/graphviz.rb:378:in `[]='
/usr/local/rvm/gems/ruby-1.9.2-p290/gems/ruby-graphviz-1.0.8/lib/graphviz.rb:901:in `block in initialize'
/usr/local/rvm/gems/ruby-1.9.2-p290/gems/ruby-graphviz-1.0.8/lib/graphviz.rb:878:in `each'
/usr/local/rvm/gems/ruby-1.9.2-p290/gems/ruby-graphviz-1.0.8/lib/graphviz.rb:878:in `initialize'
/usr/local/rvm/gems/ruby-1.9.2-p290/gems/ruby-fann-1.1.3/lib/ruby_fann/neurotica.rb:37:in `new'
/usr/local/rvm/gems/ruby-1.9.2-p290/gems/ruby-fann-1.1.3/lib/ruby_fann/neurotica.rb:37:in `graph'
/home/username/ror/nn/lib/tasks/test.rake:42:in `block (2 levels) in <top (required)>'
Tasks: TOP => test:nn
(See full trace by running task with --trace)

Как видно по ошибке в конце вывода, удалось все кроме создания PNG-файла. Но т.к. graphviz далеко не самый важный и нужный элемент, то и Бог с ним.
Описываемое далее выходит за рамки темы объявленной в заголовке, однако, чтобы не забыть я напишу.

определение количества нейронов

Теоретически, именно теоретически, количество нейронов определяется по формуле из следствия теорем Арнольда – Колмогорова – Хехт-Нильсена:

Ny — количество выходов;
Q — количество обучающих примеров;
Nw — необходимое число синаптических связей;
Nx — количество входов.
Из этого неравенства следует, что количество нейронов для сети с одним скрытым слоем определяется:

Если нарисовать схему нейросети с более чем одним скрытым слоем, то становится очевидным равенство:

l — количество слоев;
Ni — количество нейронов в i-м слое.
Если же в каждом скрытом слое подразумевается одно и тоже количество нейронов, то тождество преобретает вид:

Решив квадратное уравнение относительно N и отбросив заведомо отрицательный корень, получаем, что количество нейронов в каждом скрытом слое определяется по формуле:

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

4 Comments to “нейронные сети на ruby”

  1. Constantine пишет:

    Lukmus, сделай пожалуйста тему про реализацию парсера на Ruby какого нибудь контента.

  2. Constantine пишет:

    Просто парсера. Ни где нет толковой информции об этом. Тестерам бы пригодилось.

    • lukmus пишет:

      А какой информации ты ожидаешь от меня? Я не знаю, что тут можно еще сказать кроме как того, что парсить можно через регулярки (str.scan(/(reg)/)), через разбиение на фрагменты (str.split(‘first separator’)[1].split(‘second separator’)[0]), ну, или используя другие строковые функции языка (http://www.ruby-doc.org/core-1.9.3/String.html). Это все, что я знаю про парсинг в смысле вычленения необходимой инфы из текста.
      Если под парсингом ты понимаешь анализ текста, то тут тебе поможет теория автоматов, версия которой для Ruby никак не может отличаться от вариантов для других ЯП

Leave a Reply to lukmus

(обязательно)

(обязательно)