knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "man/figures/README-", out.width = "100%" )
The goal of rTorch
is providing an R wrapper to PyTorch. rTorch
provides all the functionality of PyTorch plus all the features that R provides. We have borrowed some ideas and code used in R tensorflow to implement rTorch
.
Besides the module torch
, which directly provides PyTorch
methods, classes and functions, the package also provides the modules numpy
as a method called np
, and torchvision
, as well. The dollar sign $
after the module will provide you access to all their sub-objects. Example:
tv <- rTorch::torchvision tv np <- rTorch::np np torch_module <- rTorch::torch torch_module
To lighten up the time in building this rTorch
package, we moved the examples that use tensor operations and neural networks to separate repositories. There are two sets of examples:
rTorch
is available via CRAN and from GitHub.
Install from CRAN using install.packages("rTorch")
from the R console, or from RStudio using Tools
, Install Packages
from the menu.
For the latest version install from GitHub.
Install rTorch
with:
devtools::install_github("f0nzie/rTorch")
Installing from GitHub gives you the flexibility of experimenting with the latest development version of rTorch
. For instance, to install rTorch
from the develop
branch:
devtools::install_github("f0nzie/rTorch", ref="develop")
or clone with Git with:
git clone https://github.com/f0nzie/rTorch.git
There are five major type of Tensors in PyTorch: Byte Float Double Long * Bool
library(rTorch) byte_tensor <- torch$ByteTensor(3L, 3L) float_tensor <- torch$FloatTensor(3L, 3L) double_tensor <- torch$DoubleTensor(3L, 3L) long_tensor <- torch$LongTensor(3L, 3L) bool_tensor <- torch$BoolTensor(5L, 5L) byte_tensor float_tensor double_tensor long_tensor bool_tensor
A 4D
tensor like in MNIST hand-written digits recognition dataset:
mnist_4d <- torch$FloatTensor(60000L, 3L, 28L, 28L) # size mnist_4d$size() # length length(mnist_4d) # shape, like in numpy mnist_4d$shape # number of elements mnist_4d$numel()
A 3D
tensor:
ft3d <- torch$FloatTensor(4L, 3L, 2L) ft3d
# get first element in a tensor ft3d[1, 1, 1]
# create a tensor with a value torch$full(list(2L, 3L), 3.141592)
# 3x5 matrix uniformly distributed between 0 and 1 mat0 <- torch$FloatTensor(3L, 5L)$uniform_(0L, 1L) # fill a 3x5 matrix with 0.1 mat1 <- torch$FloatTensor(3L, 5L)$uniform_(0.1, 0.1) # a vector with all ones mat2 <- torch$FloatTensor(5L)$uniform_(1, 1)
# add two tensors mat0 + mat1
# add three tensors mat0 + mat1 + mat2
# PyTorch add two tensors using add() function x = torch$rand(5L, 4L) y = torch$rand(5L, 4L) print(x$add(y)) print(x + y)
# add an element of a tensor to another tensor mat1[1, 1] + mat2
mat1
# extract part of the tensor indices <- torch$tensor(c(0L, 3L)) torch$index_select(mat1, 1L, indices) # rows = 0; columns = 1
# add a scalar to a tensor mat0 + 0.1
# Multiply tensor by scalar tensor = torch$ones(4L, dtype=torch$float64) scalar = np$float64(4.321) message("a numpy scalar: ", scalar) message("a PyTorch scalar: ", torch$scalar_tensor(scalar)) message("\nResult") (prod = torch$mul(tensor, torch$scalar_tensor(scalar)))
# short version using generics (prod = tensor * scalar)
t1 = torch$tensor(c(1, 2)) t2 = torch$tensor(c(3, 2)) t1 t2
t1 * t2
t1 = torch$tensor(list( c(1, 2, 3), c(1, 2, 3) )) t2 = torch$tensor(list( c(1, 2), c(1, 2), c(1, 2) )) t1 t2
torch$mm(t1, t2)
t1 = torch$tensor(c(1, 2)) t2 = torch$tensor(c(3, 2)) t1 t2
# dot product of two vectors torch$dot(t1, t2)
# Dot product of 1D tensors is a scalar p <- torch$Tensor(list(4L, 2L)) q <- torch$Tensor(list(3L, 1L)) (r = torch$dot(p, q)) # 14 (r <- p %.*% q)
# torch$dot product will work for vectors not matrices t1 = torch$tensor(list( c(1, 2, 3), c(1, 2, 3) )) t2 = torch$tensor(list( c(1, 2), c(1, 2), c(1, 2) )) t1$shape t2$shape
# RuntimeError: 1D tensors expected, got 2D, 2D tensors torch$dot(t1, t2)
The number of columns of the first matrix must be equal to the number of rows of the second matrix.
# for the dot product of nD tensors we use torch$mm() t1 = torch$tensor(list( c(1, 2, 3), c(1, 2, 3) )) t2 = torch$tensor(list( c(1, 2), c(1, 2), c(1, 2) )) torch$mm(t1, t2)
torch$mm(t2, t1)
# for the dot product of 2D tensors we use torch$mm() t1 = torch$arange(1, 11)$view(c(2L,5L)) t2 = torch$arange(11, 21)$view(c(5L,2L)) t1 t2
# result torch$mm(t1, t2)
# 1D tensor t1 = torch$tensor(c(1, 2)) t2 = torch$tensor(c(3, 2)) torch$matmul(t1, t2)
# 2D tensor t1 = torch$tensor(list( c(1, 2, 3), c(1, 2, 3) )) t2 = torch$tensor(list( c(1, 2), c(1, 2), c(1, 2) )) torch$matmul(t1, t2)
# for the dot product of 3D tensors we use torch$matmul() t1 = torch$arange(1, 13)$view(c(2L, 2L, 3L)) # number of columns = 2 t2 = torch$arange(0, 18)$view(c(2L, 3L, 3L)) # number of rows = 2 t1 t2 message("result") torch$matmul(t1, t2)
t1 = torch$arange(1, 13)$view(c(3L, 2L, 2L)) # number of columns = 3 t2 = torch$arange(0, 12)$view(c(3L, 2L, 2L)) # number of rows = 3 t1 t2 message("result") torch$matmul(t1, t2)
m1 = torch$ones(3L, 5L) m2 = torch$ones(3L, 5L) v1 = torch$ones(3L) # Cross product # Size 3x5 (r = torch$cross(m1, m2))
numpy
has been made available as a module inside rTorch
. We could call functions from numpy
refrerring to it as np$any_function
. Examples:
# a 2D numpy array syn0 <- np$random$rand(3L, 5L) syn0
# numpy arrays of zeros syn1 <- np$zeros(c(5L, 10L)) syn1
# add a scalar to a numpy array syn1 = syn1 + 0.1 syn1
# in numpy a multidimensional array needs to be defined with a tuple # From R we use a vector to refer to a tuple in Python l1 <- np$ones(c(5L, 5L)) l1
# vector-matrix multiplication np$dot(syn0, syn1)
# build a numpy array from three R vectors X <- np$array(rbind(c(1,2,3), c(4,5,6), c(7,8,9))) X
# transpose the array np$transpose(X)
With newer PyTorch versions we should work with NumPy array copies There have been minor changes in the latest versions of PyTorch that prevents a direct use of a NumPy array. You will get this warning:
sys:1: UserWarning: The given NumPy array is not writeable, and PyTorch does not support non-writeable tensors. This means you can write to the underlying (supposedly non-writeable) NumPy array using the tensor. You may want to copy the array to protect its data or make it writeable before converting it to a tensor. This type of warning will be suppressed for the rest of this program.
For instance, this code will produce the warning:
# as_tensor. Modifying tensor modifies numpy object as well a = np$array(list(1, 2, 3)) t = torch$as_tensor(a) print(t) torch$tensor(list( 1, 2, 3)) t[1L]$fill_(-1) print(a)
while this other one -with some extra code- will not:
a = np$array(list(1, 2, 3)) a_copy = r_to_py(a)$copy() # we make a copy of the numpy array first t = torch$as_tensor(a_copy) print(t) torch$tensor(list( 1, 2, 3)) t[1L]$fill_(-1) print(a)
make_copy()
To make easier to copy an object in rTorch
we implemented the function make_copy
, which makes a safe copy regardless if it is a torch, numpy or an R type object.
a = np$array(list(1, 2, 3, 4, 5)) a_copy <- make_copy(a) t <- torch$as_tensor(a_copy) t
# convert a numpy array to a tensor np_a = np$array(c(c(3, 4), c(3, 6))) t_a = torch$from_numpy(r_to_py(np_a)$copy()) print(t_a)
# a random 1D tensor np_arr <- np$random$rand(5L) ft1 <- torch$FloatTensor(r_to_py(np_arr)$copy()) # make a copy of numpy array ft1
# tensor as a float of 64-bits np_copy <- r_to_py(np$random$rand(5L))$copy() # make a copy of numpy array ft2 <- torch$as_tensor(np_copy, dtype= torch$float64) ft2
This is a very common operation in machine learning:
# convert tensor to a numpy array a = torch$rand(5L, 4L) b = a$numpy() print(b)
# convert tensor to float 16-bits ft2_dbl <- torch$as_tensor(ft2, dtype = torch$float16) ft2_dbl
Create a tensor of size (5 x 7) with uninitialized memory:
a <- torch$FloatTensor(5L, 7L) print(a)
# using arange to create tensor. starts from 0 v = torch$arange(9L) (v = v$view(3L, 3L))
Initialize a tensor randomized with a normal distribution with mean=0, var=1:
a <- torch$randn(5L, 7L) print(a) print(a$size())
library(rTorch) # 3x5 matrix uniformly distributed between 0 and 1 mat0 <- torch$FloatTensor(3L, 5L)$uniform_(0L, 1L) # fill a 3x5 matrix with 0.1 mat1 <- torch$FloatTensor(3L, 5L)$uniform_(0.1, 0.1) # a vector with all ones mat2 <- torch$FloatTensor(5L)$uniform_(1, 1) mat0 mat1
Binomial <- torch$distributions$binomial$Binomial m = Binomial(100, torch$tensor(list(0 , .2, .8, 1))) (x = m$sample())
m = Binomial(torch$tensor(list(list(5.), list(10.))), torch$tensor(list(0.5, 0.8))) (x = m$sample())
Exponential <- torch$distributions$exponential$Exponential m = Exponential(torch$tensor(list(1.0))) m$sample() # Exponential distributed with rate=1
Weibull <- torch$distributions$weibull$Weibull m = Weibull(torch$tensor(list(1.0)), torch$tensor(list(1.0))) m$sample() # sample from a Weibull distribution with scale=1, concentration=1
Only floating-point types are supported as the default type.
# Default data type torch$tensor(list(1.2, 3))$dtype # default for floating point is torch.float32
# change default data type to float64 torch$set_default_dtype(torch$float64) torch$tensor(list(1.2, 3))$dtype # a new floating point tensor
torch$set_default_dtype(torch$double) torch$tensor(list(1.2, 3))$dtype
x = torch$randn(2L, 3L) # Size 2x3 y = x$view(6L) # Resize x to size 6 z = x$view(-1L, 2L) # Size 3x2 print(y) print(z)
# 0 1 2 # 3 4 5 # 6 7 8 v = torch$arange(9L) (v = v$view(3L, 3L))
# concatenate tensors x = torch$randn(2L, 3L) print(x) # concatenate tensors by dim=0" torch$cat(list(x, x, x), 0L) # concatenate tensors by dim=1 torch$cat(list(x, x, x), 1L)
# ----- Reshape tensors ----- img <- torch$ones(3L, 28L, 28L) print(img$size()) img_chunks <- torch$chunk(img, chunks = 3L, dim = 0L) print(length(img_chunks)) # 1st chunk member img_chunk_1 <- img_chunks[[1]] print(img_chunk_1$size()) print(img_chunk_1$sum()) # 2nd chunk member img_chunk_1 <- img_chunks[[2]] print(img_chunk_1$size()) print(img_chunk_1$sum()) # index_select. get layer 1 indices = torch$tensor(c(0L)) img2 <- torch$index_select(img, dim = 0L, index = indices) print(img2$size()) print(img2$sum()) # index_select. get layer 2 indices = torch$tensor(c(1L)) img2 <- torch$index_select(img, dim = 0L, index = indices) print(img2$size()) print(img2$sum()) # index_select. get layer 3 indices = torch$tensor(c(2L)) img2 <- torch$index_select(img, dim = 0L, index = indices) print(img2$size()) print(img2$sum())
# identity matrix eye = torch$eye(3L) # Create an identity 3x3 tensor print(eye)
(v = torch$ones(10L)) # A tensor of size 10 containing all ones (v = torch$ones(2L, 1L, 2L, 1L)) # Size 2x1x2x1
v = torch$ones_like(eye) # A tensor with same shape as eye. Fill it with 1. v
(z = torch$zeros(10L)) # A tensor of size 10 containing all zeros
# a tensor filled with ones (v = torch$ones(3L, 3L))
# change two rows in the tensor # we are using 1-based index v[2L, ]$fill_(2L) # fill row 1 with 2s v[3L, ]$fill_(3L) # fill row 2 with 3s
print(v)
# Initialize Tensor with a range of values (v = torch$arange(10L)) # similar to range(5) but creating a Tensor
(v = torch$arange(0L, 10L, step = 1L)) # Size 5. Similar to range(0, 5, 1)
u <- torch$arange(0, 10, step = 0.5) u
# range of values with increments including the end value start <- 0 end <- 10 step <- 0.25 w <- torch$arange(start, end+step, step) w
# Initialize a linear or log scale Tensor # Create a Tensor with 10 linear points for (1, 10) inclusively (v = torch$linspace(1L, 10L, steps = 10L)) # Size 5: 1.0e-10 1.0e-05 1.0e+00, 1.0e+05, 1.0e+10 (v = torch$logspace(start=-10L, end = 10L, steps = 5L))
a = torch$rand(5L, 4L) print(class(a))
# converting the tensor to a numpy array, R automatically converts it b = a$numpy() print(class(b))
a$fill_(3.5) # a has now been filled with the value 3.5 # add a scalar to a tensor. # notice that was auto-converted from an array to a tensor b <- a$add(4.0) # a is still filled with 3.5 # new tensor b is returned with values 3.5 + 4.0 = 7.5 print(a) print(b)
# this will throw an error because we don't still have a function for assignment a[1, 1] <- 7.7 print(a) # Error in a[1, 1] <- 7.7 : object of type 'environment' is not subsettable
# This would be the right wayy to assign a value to a tensor element a[1, 1]$fill_(7.7)
# we can see that the first element has been changed
a
Some operations like
narrow
do not have in-place versions, and hence,.narrow_
does not exist. Similarly, some operations likefill_
do not have an out-of-place version, so.fill
does not exist.
# a[[0L, 3L]] a[1, 4]
# replace an element at position 0, 0 (new_tensor = torch$Tensor(list(list(1, 2), list(3, 4))))
# first row, firt column print(new_tensor[1L, 1L])
# change row 1, col 1 with value of 5 new_tensor[1L, 1L]$fill_(5)
# which is the same as doing this new_tensor[1, 1]$fill_(5)
Notice that the element was changed in-place because of
fill_
.
print(new_tensor) # tensor([[ 5., 2.],[ 3., 4.]])
# access an element at position (1, 0), 0-based index print(new_tensor[2L, 1L]) # tensor([ 3.])
# convert it to a scalar value print(new_tensor[2L, 1L]$item()) # 3.
# which is the same as print(new_tensor[2, 1])
# and the scalar print(new_tensor[2, 1]$item())
# Select indices x = torch$randn(3L, 4L) print(x)
# extract first and third row # Select indices, dim=0 indices = torch$tensor(list(0L, 2L)) torch$index_select(x, 0L, indices)
# extract first and third column # Select indices, dim=1 torch$index_select(x, 1L, indices)
# Take by indices src = torch$tensor(list(list(4, 3, 5), list(6, 7, 8)) ) print(src) print( torch$take(src, torch$tensor(list(0L, 2L, 5L))) )
# two dimensions: 3x3 x <- torch$arange(9L) x <- x$view(c(3L, 3L)) t <- torch$transpose(x, 0L, 1L) x # "Original tensor" t # "Transposed"
# three dimensions: 1x2x3 x <- torch$ones(c(1L, 2L, 3L)) t <- torch$transpose(x, 1L, 0L) print(x) # original tensor print(t) # transposed print(x$shape) # original tensor print(t$shape) # transposed
x <- torch$tensor(list(list(list(1,2)), list(list(3,4)), list(list(5,6)))) xs <- torch$as_tensor(x$shape) xp <- x$permute(c(1L, 2L, 0L)) xps <- torch$as_tensor(xp$shape) print(x) # original tensor print(xp) # permuted tensor print(xs) # shape original tensor print(xps) # shape permuted tensor
torch$manual_seed(1234) x <- torch$randn(10L, 480L, 640L, 3L) x[1:3, 1:2, 1:3, 1:2]
xs <- torch$as_tensor(x$size()) # torch$tensor(c(10L, 480L, 640L, 3L)) xp <- x$permute(0L, 3L, 1L, 2L) # specify dimensions order xps <- torch$as_tensor(xp$size()) # torch$tensor(c(10L, 3L, 480L, 640L)) print(xs) # original tensor size print(xps) # permuted tensor size
xp[1:3, 1:2, 1:3, 1:2]
(m0 = torch$zeros(3L, 5L))
(m1 = torch$ones(3L, 5L))
(m2 = torch$eye(3L, 5L))
# is m1 equal to m0 print(m1 == m0)
print(as_boolean(m1 == m0))
# is it not equal print(m1 != m1)
# are both equal print(m2 == m2)
print(as_boolean(m2 == m2))
# some are equal, others don't m1 != m2
# some are equal, others don't m0 != m2
as_boolean(m0 != m2)
# AND m1 & m1
as_boolean(m1 & m1)
# OR m0 | m2
# OR m1 | m2
as_boolean(m1 | m2)
# tensor is less than A <- torch$ones(60000L, 1L, 28L, 28L) C <- A * 0.5 # is C < A = TRUE all(torch$lt(C, A)) all(C < A) # is A < C = FALSE all(A < C)
# tensor is greater than A <- torch$ones(60000L, 1L, 28L, 28L) D <- A * 2.0 all(torch$gt(D, A)) all(torch$gt(A, D))
# tensor is less than or equal A1 <- torch$ones(60000L, 1L, 28L, 28L) all(torch$le(A1, A1)) all(A1 <= A1) # tensor is greater than or equal A0 <- torch$zeros(60000L, 1L, 28L, 28L) all(torch$ge(A0, A0)) all(A0 >= A0) all(A1 >= A0) all(A1 <= A0)
# we implement this little function all_as_boolean <- function(x) { # convert tensor of 1s and 0s to a unique boolean as.logical(torch$all(x)$numpy()) }
all_as_boolean(torch$gt(D, A)) all_as_boolean(torch$gt(A, D)) all_as_boolean(A1 <= A1) all_as_boolean(A1 >= A0) all_as_boolean(A1 <= A0)
# vector of booleans all_true <- torch$BoolTensor(list(TRUE, TRUE, TRUE, TRUE)) all_true
# logical NOT # negate vector with "!" not_all_true <- !all_true not_all_true
# a diagonal matrix diag <- torch$eye(5L) diag <- diag$to(dtype=torch$uint8) # convert to unsigned integer diag
as_boolean(diag)
# logical NOT not_diag <- !diag not_diag
# and the negation !not_diag
as_boolean(!not_diag)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.