Rumale::Torch

Rumale::Torch provides the learning and inference by the neural network defined in torch.rb with the same interface as Rumale.

Installation

torch.rb is a runtime dependent gem of Rumale::Torch. It requires to install LibTorch:

$ brew install automake libtorch

Here, automake is needed to install rice gem, which torch.rb depends on.

Add this line to your application’s Gemfile:

gem 'rumale-torch'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install rumale-torch

Usage

Example 1. Pendigits dataset classification

We start by downloading the pendigits dataset from LIBSVM Data web site.

$ wget https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/pendigits
$ wget https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/pendigits.t

Training phase:

require 'rumale'
require 'rumale/torch'

Torch.manual_seed(1)
device = Torch.device('cpu')

# Loading pendigits dataset consisting of
# 16-dimensional data divided into 10 classes.
x, y = Rumale::Dataset.load_libsvm_file('pendigits')

# Define a neural network in torch.rb framework.
class MyNet < Torch::NN::Module
  def initialize
    super
    @dropout = Torch::NN::Dropout.new(p: 0.5)
    @fc1 = Torch::NN::Linear.new(16, 128)
    @fc2 = Torch::NN::Linear.new(128, 10)
  end

  def forward(x)
    x = @fc1.call(x)
    x = Torch::NN::F.relu(x)
    x = @dropout.call(x)
    x = @fc2.call(x)
    Torch::NN::F.softmax(x)
  end
end

net = MyNet.new.to(device)

# Create a classifier with neural network model.
classifier = Rumale::Torch::NeuralNetClassifier.new(
  model: net, device: device,
  batch_size: 10, max_epoch: 50, validation_split: 0.1,
  verbose: true
)

# Learning classifier.
classifier.fit(x, y)

# Saving model.
Torch.save(net.state_dict, 'pendigits.pth')
File.binwrite('pendigits.dat', Marshal.dump(classifier))

Testing phase:

require 'rumale'
require 'rumale/torch'

# Loading neural network model.
class MyNet < Torch::NN::Module
  def initialize
    super
    @dropout = Torch::NN::Dropout.new(p: 0.5)
    @fc1 = Torch::NN::Linear.new(16, 128)
    @fc2 = Torch::NN::Linear.new(128, 10)
  end

  def forward(x)
    x = @fc1.call(x)
    x = Torch::NN::F.relu(x)
    # x = @dropout.call(x)
    x = @fc2.call(x)
    Torch::NN::F.softmax(x)
  end
end

net = MyNet.new
net.load_state_dict(Torch.load('pendigits.pth'))

# Loading classifier.
classifier = Marshal.load(File.binread('pendigits.dat'))
classifier.model = net

# Loading test dataset.
x_test, y_test = Rumale::Dataset.load_libsvm_file('pendigits.t')

# Predict labels of test data.
p_test = classifier.predict(x_test)

# Evaluate predicted result.
accuracy = Rumale::EvaluationMeasure::Accuracy.new.score(y_test, p_test)
puts(format("Accuracy: %2.1f%%", accuracy * 100))

The result of executing the above scripts is as follows:

$ ruby train.rb
epoch:  1/50 - loss: 0.2073 - accuracy: 0.3885 - val_loss: 0.2074 - val_accuracy: 0.3853
epoch:  2/50 - loss: 0.1973 - accuracy: 0.4883 - val_loss: 0.1970 - val_accuracy: 0.4893
epoch:  3/50 - loss: 0.1962 - accuracy: 0.4997 - val_loss: 0.1959 - val_accuracy: 0.5013

...

epoch: 50/50 - loss: 0.1542 - accuracy: 0.9199 - val_loss: 0.1531 - val_accuracy: 0.9293

$ ruby test.rb
Accuracy: 91.2%

Example 2. Cross-validation with Rumale

Perform 5-fold cross-validation for regression problem using the housing dataset.

$ wget https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/regression/housing

The example script:

require 'rumale'
require 'rumale/torch'

Torch.manual_seed(1)
device = Torch.device('cpu')

# Loading pendigits dataset consisting of
# 13-dimensional data with single target variable.
x, y = Rumale::Dataset.load_libsvm_file('housing')

# Define a neural network in torch.rb framework.
class MyNet < Torch::NN::Module
  def initialize
    super
    @fc1 = Torch::NN::Linear.new(13, 128)
    @fc2 = Torch::NN::Linear.new(128, 1)
  end

  def forward(x)
    x = @fc1.call(x)
    x = Torch::NN::F.relu(x)
    x = @fc2.call(x)
  end
end

net = MyNet.new.to(device)

# Create a regressor with neural network model.
regressor = Rumale::Torch::NeuralNetRegressor.new(
  model: net, device: device, batch_size: 10, max_epoch: 100
)

# Create evaluation measure, splitting strategy, and cross validation.
ev = Rumale::EvaluationMeasure::R2Score.new
kf = Rumale::ModelSelection::ShuffleSplit.new(n_splits: 5, test_size: 0.1, random_seed: 1)
cv = Rumale::ModelSelection::CrossValidation.new(estimator: regressor, splitter: kf, evaluator: ev)

# Perform 5-cross validation.
report = cv.perform(x, y)

# Output result.
mean_score = report[:test_score].sum / kf.n_splits
puts(format("5-CV R2-score: %.3f", mean_score))

The execution result is as follows:

$ ruby cv.rb
5-CV R2-score: 0.755

Contributing

Bug reports and pull requests are welcome on GitHub at github.com/yoshoku/rumale-torch. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the BSD-3-Clause License.