--- title: "mirai - Torch Integration" vignette: > %\VignetteIndexEntry{mirai - Torch Integration} %\VignetteEngine{litedown::vignette} %\VignetteEncoding{UTF-8} --- ### Torch Integration Custom serialization functions may be registered to handle external pointer type reference objects. This allows tensors from the [`torch`](https://torch.mlverse.org/) package to be used seamlessly in 'mirai' computations. #### Setup Steps 1. Create the serialization configuration, specifying 'class' as 'torch_tensor' and 'vec' as TRUE. 1. Set up daemons, supplying the configuration to the 'serial' argument. 1. (Optional) Use `everywhere()` to make the `torch` package available on all daemons for convenience. ``` r library(mirai) library(torch) cfg <- serial_config( class = "torch_tensor", sfunc = torch::torch_serialize, ufunc = torch::torch_load, vec = TRUE ) daemons(1, serial = cfg) #> [1] 1 everywhere(library(torch)) ``` #### Example Usage The below example creates a convolutional neural network using `torch::nn_module()`. A set of model parameters is also specified. The model specification and parameters are then passed to and initialized within a 'mirai'. ``` r model <- nn_module( initialize = function(in_size, out_size) { self$conv1 <- nn_conv2d(in_size, out_size, 5) self$conv2 <- nn_conv2d(in_size, out_size, 5) }, forward = function(x) { x <- self$conv1(x) x <- nnf_relu(x) x <- self$conv2(x) x <- nnf_relu(x) x } ) params <- list(in_size = 1, out_size = 20) m <- mirai(do.call(model, params), model = model, params = params) m[] #> An `nn_module` containing 1,040 parameters. #> #> ── Modules ──────────────────────────────────────────────────────────────────────────────────────────────────────── #> • conv1: #520 parameters #> • conv2: #520 parameters ``` The returned model is an object containing many tensor elements. ``` r m$data$parameters$conv1.weight #> torch_tensor #> (1,1,.,.) = #> -0.0744 -0.0110 -0.0537 -0.0237 -0.1781 #> -0.1662 -0.0952 -0.1977 -0.1710 -0.0747 #> -0.0216 -0.0314 -0.1124 -0.1277 0.0098 #> 0.1225 0.1188 0.1468 0.1675 -0.1111 #> 0.1517 0.1384 0.1313 0.0376 -0.0254 #> #> (2,1,.,.) = #> -0.0459 0.0087 -0.0113 -0.0393 -0.1890 #> 0.0141 0.1516 0.0133 0.0606 -0.1009 #> -0.0160 -0.1533 -0.0492 -0.0738 0.1385 #> -0.1819 0.1906 0.0515 0.0980 0.1830 #> -0.0678 0.1724 0.0914 -0.1575 0.1338 #> #> (3,1,.,.) = #> -0.0386 0.0314 0.0473 -0.1881 -0.0848 #> -0.0805 0.1641 -0.0009 -0.1189 0.1966 #> 0.0166 0.1321 0.0586 -0.0890 -0.0104 #> 0.1341 0.1446 -0.0266 0.1377 0.0130 #> 0.0878 -0.0120 0.1676 -0.0057 -0.0602 #> #> (4,1,.,.) = #> 0.0808 0.1956 0.0116 0.0120 -0.0445 #> -0.0620 -0.1917 0.0159 -0.0426 0.1548 #> 0.1291 0.0993 -0.0191 0.1040 0.0930 #> -0.0752 -0.0272 -0.1484 0.0472 -0.1651 #> 0.1708 0.0247 -0.0511 0.1567 -0.1810 #> #> (5,1,.,.) = #> 0.1985 -0.0241 0.1558 0.0839 0.1445 #> ... [the output was truncated (use n=-1 to disable)] #> [ CPUFloatType{20,1,5,5} ][ requires_grad = TRUE ] ``` It is usual for model parameters to then be passed to an optimiser. This can also be initialized within a 'mirai' process. ``` r optim <- mirai(optim_rmsprop(params = params), params = m$data$parameters) optim[] #> #> Inherits from: #> Public: #> add_param_group: function (param_group) #> clone: function (deep = FALSE) #> defaults: list #> initialize: function (params, lr = 0.01, alpha = 0.99, eps = 1e-08, weight_decay = 0, #> load_state_dict: function (state_dict, ..., .refer_to_state_dict = FALSE) #> param_groups: list #> state: State, R6 #> state_dict: function () #> step: function (closure = NULL) #> zero_grad: function () #> Private: #> step_helper: function (closure, loop_fun) daemons(0) #> [1] 0 ``` Above, tensors and complex objects containing tensors were passed seamlessly between host and daemon processes, in the same way as any other R object. The custom serialization in `mirai` leverages R's own native 'refhook' mechanism to allow such completely transparent usage. Designed to be fast and efficient, data copies are minimised and the 'official' serialization methods from the `torch` package are used directly.