Torch.rb
For computer vision tasks, also check out TorchVision
Installation
First, install LibTorch. For Homebrew, use:
brew install libtorch
Add this line to your application’s Gemfile:
gem 'torchrb'
It can take a few minutes to compile the extension.
Getting Started
Deep learning is significantly faster with a GPU. If you don’t have an NVIDIA GPU, we recommend using a cloud service. Paperspace has a great free plan.
We’ve put together a Docker image to make it easy to get started. On Paperspace, create a notebook with a custom container. Set the container name to:
ankane/mlstack:torchgpu
And leave the other fields in that section blank. Once the notebook is running, you can run the MNIST example.
API
This library follows the PyTorch API. There are a few changes to make it more Rubylike:
 Methods that perform inplace modifications end with
!
instead of_
(add!
instead ofadd_
)  Methods that return booleans use
?
instead ofis_
(tensor?
instead ofis_tensor
)  Numo is used instead of NumPy (
x.numo
instead ofx.numpy()
)
Tutorial
Some examples below are from Deep Learning with PyTorch: A 60 Minutes Blitz
Tensors
Create a tensor from a Ruby array
x = Torch.tensor([[1, 2, 3], [4, 5, 6]])
Get the shape of a tensor
x.shape
There are many functions to create tensors, like
a = Torch.rand(3)
b = Torch.zeros(2, 3)
Each tensor has four properties
dtype
 the data type :uint8
,:int8
,:int16
,:int32
,:int64
,:float32
,float64
, or:bool
layout
:strided
(dense) or:sparse
device
 the compute device, like CPU or GPUrequires_grad
 whether or not to record gradients
You can specify properties when creating a tensor
Torch.rand(2, 3, dtype: :double, layout: :strided, device: "cpu", requires_grad: true)
Operations
Create a tensor
x = Torch.tensor([10, 20, 30])
Add
x + 5 # tensor([15, 25, 35])
Subtract
x  5 # tensor([5, 15, 25])
Multiply
x * 5 # tensor([50, 100, 150])
Divide
x / 5 # tensor([2, 4, 6])
Get the remainder
x % 3 # tensor([1, 2, 0])
Raise to a power
x**2 # tensor([100, 400, 900])
Perform operations with other tensors
y = Torch.tensor([1, 2, 3])
x + y # tensor([11, 22, 33])
Perform operations inplace
x.add!(5)
x # tensor([15, 25, 35])
You can also specify an output tensor
result = Torch.empty(3)
Torch.add(x, y, out: result)
result # tensor([15, 25, 35])
Numo
Convert a tensor to a Numo array
a = Torch.ones(5)
a.numo
Convert a Numo array to a tensor
b = Numo::NArray.cast([1, 2, 3])
Torch.from_numo(b)
Autograd
Create a tensor with requires_grad: true
x = Torch.ones(2, 2, requires_grad: true)
Perform operations
y = x + 2
z = y * y * 3
out = z.mean
Backprop
out.backward
Get gradients
x.grad # tensor([[4.5, 4.5], [4.5, 4.5]])
Stop autograd from tracking history
x.requires_grad # true
(x**2).requires_grad # true
Torch.no_grad do
(x**2).requires_grad # false
end
Neural Networks
Define a neural network
class MyNet < Torch::NN::Module
def initialize
super
@conv1 = Torch::NN::Conv2d.new(1, 6, 3)
@conv2 = Torch::NN::Conv2d.new(6, 16, 3)
@fc1 = Torch::NN::Linear.new(16 * 6 * 6, 120)
@fc2 = Torch::NN::Linear.new(120, 84)
@fc3 = Torch::NN::Linear.new(84, 10)
end
def forward(x)
x = Torch::NN::F.max_pool2d(Torch::NN::F.relu(@conv1.call(x)), [2, 2])
x = Torch::NN::F.max_pool2d(Torch::NN::F.relu(@conv2.call(x)), 2)
x = x.view(1, num_flat_features(x))
x = Torch::NN::F.relu(@fc1.call(x))
x = Torch::NN::F.relu(@fc2.call(x))
x = @fc3.call(x)
x
end
def num_flat_features(x)
size = x.size[1..1]
num_features = 1
size.each do s
num_features *= s
end
num_features
end
end
Create an instance of it
net = MyNet.new
input = Torch.randn(1, 1, 32, 32)
net.call(input)
Get trainable parameters
net.parameters
Zero the gradient buffers and backprop with random gradients
net.zero_grad
out.backward(Torch.randn(1, 10))
Define a loss function
output = net.call(input)
target = Torch.randn(10)
target = target.view(1, 1)
criterion = Torch::NN::MSELoss.new
loss = criterion.call(output, target)
Backprop
net.zero_grad
p net.conv1.bias.grad
loss.backward
p net.conv1.bias.grad
Update the weights
learning_rate = 0.01
net.parameters.each do f
f.data.sub!(f.grad.data * learning_rate)
end
Use an optimizer
optimizer = Torch::Optim::SGD.new(net.parameters, lr: 0.01)
optimizer.zero_grad
output = net.call(input)
loss = criterion.call(output, target)
loss.backward
optimizer.step
Saving and Loading Models
Save a model
Torch.save(net.state_dict, "net.pth")
Load a model
net = MyNet.new
net.load_state_dict(Torch.load("net.pth"))
net.eval
Tensor Creation
Here’s a list of functions to create tensors (descriptions from the C++ docs):

arange
returns a tensor with a sequence of integersTorch.arange(3) # tensor([0, 1, 2])

empty
returns a tensor with uninitialized valuesTorch.empty(3) # tensor([7.0054e45, 0.0000e+00, 0.0000e+00])

eye
returns an identity matrixTorch.eye(2) # tensor([[1, 0], [0, 1]])

full
returns a tensor filled with a single valueTorch.full([3], 5) # tensor([5, 5, 5])

linspace
returns a tensor with values linearly spaced in some intervalTorch.linspace(0, 10, 5) # tensor([0, 5, 10])

logspace
returns a tensor with values logarithmically spaced in some intervalTorch.logspace(0, 10, 5) # tensor([1, 1e5, 1e10])

ones
returns a tensor filled with all onesTorch.ones(3) # tensor([1, 1, 1])

rand
returns a tensor filled with values drawn from a uniform distribution on [0, 1)Torch.rand(3) # tensor([0.5444, 0.8799, 0.5571])

randint
returns a tensor with integers randomly drawn from an intervalTorch.randint(1, 10, [3]) # tensor([7, 6, 4])

randn
returns a tensor filled with values drawn from a unit normal distributionTorch.randn(3) # tensor([0.7147, 0.6614, 1.1453])

randperm
returns a tensor filled with a random permutation of integers in some intervalTorch.randperm(3) # tensor([2, 0, 1])

zeros
returns a tensor filled with all zerosTorch.zeros(3) # tensor([0, 0, 0])
Examples
Here are a few full examples:
 Image classification with MNIST (日本語版)
 Collaborative filtering with MovieLens
 Sequence models and word embeddings
LibTorch Installation
Download LibTorch. For Linux, use the cxx11 ABI
version. Then run:
bundle config build.torchrb withtorchdir=/path/to/libtorch
Here’s the list of compatible versions.
Torch.rb  LibTorch 

0.2.0+  1.5.0 
0.1.8  1.4.0 
0.1.00.1.7  1.3.1 
Homebrew
For Mac, you can use Homebrew.
brew install libtorch
Then install the gem (no need for bundle config
).
Performance
Linux
Deep learning is significantly faster on a GPU. Install CUDA and cuDNN and reinstall the gem.
Check if CUDA is available
Torch::CUDA.available?
Move a neural network to a GPU
net.cuda
rbenv
This library uses Rice to interface with LibTorch. Rice and earlier versions of rbenv don’t play nicely together. If you encounter an error during installation, upgrade rubybuild and reinstall your Ruby version.
brew upgrade rubybuild
rbenv install [version]
History
View the changelog
Contributing
Everyone is encouraged to help improve this project. Here are a few ways you can help:
 Report bugs
 Fix bugs and submit pull requests
 Write, clarify, or fix documentation
 Suggest or add new features
To get started with development:
git clone https://github.com/ankane/torch.rb.git
cd torch.rb
bundle install
bundle exec rake compile  withtorchdir=/path/to/libtorch
bundle exec rake test
You can use this script to test on GPUs with the AWS Deep Learning Base AMI (Ubuntu 18.04).
Here are some good resources for contributors: