×
Игры с Бессознательным. Театр (03 сен 2019)

Из цикла Новые Материалы Блога.

Идея Использование scikit-learn в Ruby посредством PyCall

Больше
2 мес. 3 нед. назад #1 от Aleksej
Существует, оказывается, способ поюзать питоновские либы в Ruby! В качестве примера - SciKit Learn Python, простой и эффективный инструментарий машинного обучения, созданный с помощью NumPy и SciPy в Python. Для среды Ruby придется установить gem PyCall, который поможет протестировать современные технологии искусственного интеллекта (в Python их больше, чем для Ruby или многих иных языков программирования) в вашем любимом приложении на основе Rails, Sinatra, Hanimi или другом на основе Ruby. В этом примере будет продемонстрирована реализация OCR (оптическое распознавание символов) с использованием Random Forest Classifier в Ruby. В качестве набора данных мы будем использовать базу данных рукописных цифр MNIST, а для реализации Random Forest попробуем библиотеку SciKit Learn Python. Задача Pycall - импортировать произвольные модули Python в модули Ruby и вызвать функции Python с автоматическим преобразованием типов из Ruby в Python.

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

Прежде всего установим Python, если еще не установлен. Далее - upgrade pip the python package manager.
В линуксе так:
$ pip install -U pip

В Windows:
$ python -m pip install -U pip

Далее:
$ pip install -U numpy
$ pip install -U scipy
$ pip install -U scikit-learn
$ gem install pycall

Собственно, файл learn.rb:
require 'pycall/import'
require './dataset_reader.rb'

include PyCall::Import
pyfrom :'sklearn.ensemble', import: :RandomForestClassifier

test_labels = DatasetReader.read_labels( "data/t10k-labels.idx1-ubyte" )
test_images = DatasetReader.read_images( "data/t10k-images.idx3-ubyte" )
rows, columns = DatasetReader.read_rows_columns( "data/t10k-images.idx3-ubyte" )

puts "Labels: #{test_labels.size}, Images: #{test_images.size}, Rows: #{rows}, Columns: #{columns}"

train_labels = DatasetReader.read_labels( "data/train-labels.idx1-ubyte" )
train_images = DatasetReader.read_images( "data/train-images.idx3-ubyte" )

puts "Labels: #{train_labels.size}, Images: #{train_images.size}"

# Initialize a RandomForestClassifier
clf = RandomForestClassifier.new()

# Fit with training data
clf.fit(train_images, train_labels)

# Score our fit using the test data
classification_score = clf.score(test_images,test_labels)
puts "Prediction score for Random Forest classifier #{(classification_score*100).round(2)}%"

# Do a prediction for one sample
sample = 9
puts clf.predict([test_images[sample]])
puts clf.predict_proba([test_images[sample]])
puts "Correct label: #{test_labels[sample]}"

# Reshape back to 2 dimmensions and print to console
#reshaped = test_images[sample].each_slice(rows).to_a
#puts test_labels[sample]
#reshaped.each do |r|
#  puts r.inspect
#end

и dataset_reader.rb:
# Dataset reader helper to read datasets in the format described: http://yann.lecun.com/exdb/mnist/
class DatasetReader
  def self.read_labels file
    labels = []
    File.open(file) do |f|

      magic_number = f.read(4).unpack("l>").first # Unpack first 4 bytes into integer big endian
      number_of_labels = f.read(4).unpack("l>").first # Unpack next 4 bytes into integer big endian

      1.upto(number_of_labels) do |n| # For each label
        labels.push f.read(1).unpack("c").first # Unpack byte and return as byte
      end
    end

    return labels
  end

  def self.read_images file
    images = []
    rows, columns = nil

    File.open(file) do |f|

      magic_number = f.read(4).unpack("l>").first # Unpack first 4 bytes into integer big endian
      number_of_images = f.read(4).unpack("l>").first # Unpack next 4 bytes into integer big endian
      rows = f.read(4).unpack("l>").first # Unpack next 4 bytes into integer big endian
      columns = f.read(4).unpack("l>").first # Unpack next 4 bytes into integer big endian

      1.upto(number_of_images) do |n| # For each image
        image_data = f.read(rows*columns).unpack("C*")
        images.push image_data
      end
    end

    return images
  end

  def self.read_rows_columns file
    rows, columns = nil

    File.open(file) do |f|
      magic_number = f.read(4).unpack("l>").first # Unpack first 4 bytes into integer big endian
      number_of_images = f.read(4).unpack("l>").first # Unpack next 4 bytes into integer big endian
      rows = f.read(4).unpack("l>").first # Unpack next 4 bytes into integer big endian
      columns = f.read(4).unpack("l>").first # Unpack next 4 bytes into integer big endian
    end

    return rows, columns
  end
end

, а клонировать приложение полностью без проблем возможно с гитхаба .

Итак, do a prediction for sample = 9:
Запускаем:
$ ruby learn.rb 
Labels: 10000, Images: 10000, Rows: 28, Columns: 28
Labels: 60000, Images: 60000
/usr/lib64/python3.6/site-packages/sklearn/ensemble/forest.py:245: FutureWarning: The default value of n_estimators will change from 10 in version 0.20 to 100 in 0.22.
  "10 in version 0.20 to 100 in 0.22.", FutureWarning)
Prediction score for Random Forest classifier 94.47%
[9]
[[0.  0.  0.  0.  0.1 0.  0.  0.1 0.  0.8]]
Correct label: 9

Что и требовалось.

P.S.



А еще обращают на себя внимание две игрушки на рубях, демонстрирующие обучение AI на основе Neural Network и Q-learning. Любители навороченной графики и World of Tanks, вероятно, презрительно пожмут плечами, а мне так очень понравилось. :)
Еще раз рекомендую: www.practicalai.io/teaching-a-neural-net...ame-with-q-learning/

Пожалуйста Войти или Регистрация, чтобы присоединиться к беседе.