Cómo crear redes neuronales con Torch en R

En este tutorial vamos a aprender a usar Torch en R. Y es que, Torch es uno de los frameworks más utilizados para la creación de redes neuronales y deep-learning y recientemente ha sido lanzado para R.

En el tutorial abordaremos todo lo que necesitas para programar tus redes neuronales de principio a fin. Más concretamente vamos a ver:

  • Cómo instalar Torch. Parece una tontería, pero para que funcione correctamente tienes que tener la versión de R correcta y ciertos plugins que veremos. No es difícil, pero sí muy importante.
  • Diferentes formas para crear las estructuras de nuestras redes neuronales. Torch ofrece varias formas de crear nuestras redes. Veremos cuáles son y cuándo es recomendable utilizar cada una de ellas.
  • Cómo cargar nuestros datos en Torch. Ya sea que usemos datos numéricos o imágenes, aprenderás a cargar tus datos en un formato que Torch los entienda. Además, en el caso de las imágenes, en este tutorial te enseño también cómo usar Torchvision, un paquete de Torch que permite aplicar transformaciones y cargar las imágenes en batch.
  • Cómo entrenar nuestras redes con Torch en R. Te explicaré qué tipos de funciones de activación y optimizadores hay, cómo se implementan y las características de cada uno de ellos.
  • Cómo guardar tus modelos. Y es que, un modelo no sirve de nada si no lo ponemos en producción. Así que te explicaré cómo guardarlos y cargarlos y las convenciones que existen.
  • Comparativa con Tensorflow y Keras. Como veras, Torch ofrece cuestiones muy similares a Tensorflow y Keras. En este apartado, te explico, bajo mi punto de vista, las ventajas de cada uno de ellos para usuarios de Python.

Como ves, este es un tutorial de Torch en R muy extenso, así que, si te parece, ¡vamos a ello!

Primeros pasos de Torch en R

Instalar Torch en R

Aunque Torch funcione correctamente con la versión 3.6.3 de R, si vuestro objetivo es Docker-izar el modelo y ponerlo en producción (como expliqué en este post), os recomendaría utilizar la versión 4.0 de R, ya que con versiones previas me ha dado error. (Nota: puedes comprobar tu versión de R visualizando el objeto version).

Además, si instaláis la versión 4.0.3 y sois usuarios de Windows, tendréis que instalar también RTools40. Esta herramienta permite compilar el código de C++, en el cual está escrito Torch. Hacerlo es muy fácil, simplemente es seguir este tutorial.

Una vez hecho esto, ya podemos descargar e instalar la librería. Si tienes la versión 3.6.3 te pedirá instalar los binarios. Si tenéis cualquier problema, probad a subir a la versión 4.0.3.

#install.packages("torch")
library(torch)

Usar Torch sobre CPU o sobre GPU

Ahora que ya tenemos R instalado, tenemos varias cuestiones previas. Lo primero de todo es decidir si queremos que los modelos se entrenen sobre GPU (si disponemos de una compatible con los drivers instalados) o si lo harán sobre CPU.

Si no sabes si disponemos de Cuda para poder usar Torch sobre la GPU, puedes ejecutar el siguiente comando:

cuda_is_available()
[1] FALSE

Si queremos usar Torch sobre GPU tendremos que indicar que los tensores se ejecuten sobre GPU. Para ello, hay que aplicar la clase $cuda() cada vez que crees un Tensor. Si bien es lo recomendado por Torch, no es lo más óptimo y es un poco engorroso.

Ahora que tenemos Torch instalado y sabemos cómo ejecutarlo sobre GPU, ya podemos meternos de lleno con la creación de nuestras redes neuronales. ¡Vamos a por ello!

Cómo programar la estructura de una red neuronal en R con Torch

Como comentaba al principio de este tutorial, hay dos grandes formas de programar una red neuronal en Torch (tanto en R como en Python):

  1. Crear un modelo secuencial. Esta forma de trabajar es muy similar a la forma de hacerlo en Keras. Simplemente debemos indicar los tensores de entrada y salida e ir añadiendo las capas al modelo. Simple y rápido.
  2. Crear un modelo desde cero. Este caso se parecería bastante a usar Tensorflow, ya que debemos indicar todos los elementos de la red y programar el forward pass manualmente. Esto puede hacerse bien en código o creando una clase (este último punto suele ser el más normal).

Cómo no, vamos a ver cómo funciona cada una de estas formas de programar una red neuronal en R con Torch:

Creando la estructura de la red secuencial

Para programar una red neuronal secuencial, debemos incluir las diferentes capas de la red dentro de la función nn_sequential. En este sentido, una diferencia importante respecto a Keras es que la definición de la función de activación se hace fuera de la declaración del tipo de capa.

Asimismo, en cada capa debemos indicar las dimensiones tanto de entrada como salida del modelo. Si bien esto es algo muy sencillo en redes neuronales densas o fully-connected, en el caso de las redes neuronales convolucionales, hay que tener claro cómo funciona el padding, los kernels y demás.

Si no es tu caso, no te preocupes, encontrarás todo lo que necesitas saber en mi post sobre las redes neuronales conovolucionales 😉

En cualquier caso, vamos a programar una red neuronal fully-connected con 4 capas y 4,8,16 y 3 neuronas por capa, respectivamente. Sin duda, es una red muy grande para el problema que resolveremos, pero nos sirve como ejemplo.

model = nn_sequential(

  # Layer 1
  nn_linear(4, 8),
  nn_relu(), 

  # Layer 2
  nn_linear(8, 16),
  nn_relu(),

  # Layee 3
  nn_linear(16,3),
  nn_softmax(2)
)

Como ves, esta forma de crear una red neuronal en Torch con R es muy muy sencilla y se parece mucho a la forma de hacerlo en Keras.

Vista la primera forma de crear una red neuronal en Torch, vamos a ver la segunda forma clásica de crear una red neuronal en Torch: creando una red neuronal mediante clase.

Creando la estructura de la red neuronal mediante clase en R con Torch

Otra forma clásica de programar una red neuronal en R con Torch es definiendo a nuestra red neuronal como una clase personalizada. En este caso, el modelo es mucho más flexible, pero tendremos que definir todas las capas que vamos a utilizar y hacer, manualmente, el forward-pass.

net = nn_module(
  "class_net",

  initialize = function(){

    self$linear1 = nn_linear(4,8)
    self$linear2 = nn_linear(8,16)
    self$linear3 = nn_linear(16,3)

  },

  forward = function(x){

    x %>%
      self$linear1() %>%
      nnf_relu() %>%
      self$linear2() %>%
      nnf_relu() %>%
      self$linear3() %>%
      nnf_softmax(2)

  }

)

model2 = net()

Ahora, ya podemos llamar a nuestro modelo como si de una función se tratara:

x = torch_randint(0,2,size = c(20,4)) # Creo un tensor de datos aleatorios

model2$forward(x)
torch_tensor
 0.2919  0.2975  0.4106
 0.2919  0.2975  0.4106
 0.3239  0.2722  0.4038
 0.3040  0.2849  0.4111
 0.2919  0.2975  0.4106
 0.3040  0.2849  0.4111
 0.3179  0.2785  0.4035
 0.3040  0.2849  0.4111
 0.2774  0.3067  0.4159
 0.3020  0.3107  0.3873
 0.2928  0.3011  0.4061
 0.3292  0.2722  0.3986
 0.3239  0.2722  0.4038
 0.3179  0.2785  0.4035
 0.2928  0.3011  0.4061
 0.3040  0.2849  0.4111
 0.3239  0.2722  0.4038
 0.3104  0.2889  0.4007
 0.3111  0.2719  0.4170
 0.3297  0.2680  0.4023
[ CPUFloatType{20,3} ]

Cuándo usar cada tipo de red neuronal en Torch

A primera vista podemos pensar que los módulos (o clases) no presentan grandes ventajas sobre los modelos secuenciales, pero no es así.

Y es que cuando nos ponemos a crear redes de cientos de capas, usar el modelo secuencial puede ser un poco caótico. Una forma mucho mejor de abordar esas redes es partir la red en distintas secciones y en cada sección usar un módulo. Así pues, la red final será un módulo de módulos, haciendo el código mucho más legible y accionable.

Además, otra caso de uso es cuando creamos una red está compuesta, a su vez, de varias redes, como vimos en post de cómo crear una GAN en Python. En estos casos, lo más sencillo es que cada red sea un módulo en sí mismo, y que la red completa sea un módulo de módulos.

En definitiva, como norma general, si vamos a crear una red neuronal simple, tenemos que hacer más que con las redes secuenciales. Sin embargo, para redes más complejas, mejor crear clases.

Ahora que sabes cómo crear la estructura de nuestra red… ¡vamos a ver cómo convertir nuestros datos en tensores que Torch los entienda!

Convertir los datos en Tensores

Ahora tenemos la estructura de nuestra red creada, pero claro, con esto ni siquiera somos capaces de obtener una predicción. Para ello, debemos convertir nuestros datos en Tensores, el formato de datos que Torch entiende.

Al igual que en el resto de frameworks, en Torch hay dos tipos principales de datos que podemos pasar: datos numéricos e imágenes. Este es un tutorial completo de Torch en R, así que no te preocupes, que veremos ambas cosas. ¡Empecemos con los datos numéricos!

Datos de entrada numéricos

Si trabajamos con datos numéricos tendremos que convertir nuestros datos de entrada y nuestros labels en tensores.

Para ello, Torch nos ofrece la función torch_tensor, a la cual debemos pasarle dos parámetros:

  1. Los datos que queremos convertir en tensor.
  2. El tipo de dato que debe interpretar Torch. Cada tipo de dato tienen una función, aunque generalmente usaremos números, que se definen con la función torch_float().

Como ves, es muy fácil, aunque hay dos detalles importantes que, si sabes, te ahorrarán tiempo:

Detalle 1. Tipos de datos que puedes convertir en Tensor

Generalmente al trabajar con R solemos trabajar con dataframes. Sin embargo, la función torch_tensor no admite dataframes, ya que únicamente admite tres tipos de datos: matrices, vectores y arrays (imágenes).

Así pues, si partes de un dataframe, asegúrate primero de convertir tus datos a estos tipos de dato.

Detalle 2. Tipos de datos según la función de coste

Como decía antes, lo más probable es que trabajemos con datos numéricos, por lo que definamos nuestros tensores como datos torch_float().

Sin embargo, diferentes funciones de coste pueden requerir diferentes tipos de datos, si no tienes los datos en el tipo adecuado, devolverá un error (con un mensaje bastante claro, eso sí). En mi caso, por ejemplo, la función de coste es Categorical Cross Entropy, que requiere que los labels tengan formato long.

Por suerte, esto es algo que se explica de forma clara en el mensaje de error, así que si es tu caso, será fácil que lo detectes.

Dicho esto, pongámoslo en práctica con un ejemplo.

Ejemplo de Convertir en Tensor datos de entrada numéricos

En nuestro caso vamos simular un caso real con el dataset Iris. Para ello, vamos a:

  1. Partir nuestros datos entre train y test.
  2. Convertir nuestros datos de entrada en matrices y las etiquetas en vectores.
  3. Convertir nuestros datos de entrada y etiquetas en tensores.
# 1. Partir nuestros datos entre train y test.
train_split = 0.8
sample_indices =sample(nrow(iris) * train_split)

# 2. Convertir  datos de entrada en matrices y las etiquetas en vectores.
x_train = as.matrix(iris[sample_indices, -5])
y_train = as.numeric(iris[sample_indices, 5])
x_test = as.matrix(iris[-sample_indices, -5])
y_test = as.numeric(iris[-sample_indices, 5])

# 3. Convertir datos de entrada  en tensores.
x_train = torch_tensor(x_train, dtype = torch_float())
y_train = torch_tensor(y_train, dtype = torch_long())
x_test = torch_tensor(x_test, dtype = torch_float())
y_test = torch_tensor(y_test, dtype = torch_long())

Una buena práctica para comprobar que la red está creada de forma correcta es comprobar que la predicción y los datos de salida tienen el mismo tamaño. En nuestro caso, al usar CrossEntropyLoss, no se va a cumplir, pero en la gran mayoría de casos, sí lo es:

pred_temp = model(x_train)
cat(
  " Dimensions Prediction: ", pred_temp$shape," - Object type Prediction: ", as.character(pred_temp$dtype), "\n",
  "Dimensions Label: ", y_train$shape," - Object type Label: ", as.character(y_train$dtype)
  )
 Dimensions Prediction:  120 3  - Object type Prediction:  Float 
 Dimensions Label:  120  - Object type Label:  Long

Ahora que ya sabemos cómo gestionar los datos de entrada numéricos, ¡vamos a ver cómo usar imágenes como datos de entrada!

Usar imágenes como datos de entrada en Torch R

Aplicando transformaciones sobre Imágenes

De cara al tratamiento de imágenes, Torch cuenta con un paquete adicional llamado torchvision que ayuda a realizar en la carga y transformación de las imágenes, entre otras cosas. Así pues, lo primero de todo será instalar y cargar el paquete.

#install.packages("torchvision")
library(torchvision)

Ahora que ya tenemos el paquete instalado, vamos a usar las una imagen simple para ver algunas de lastransformaciones que ofrece el paquete:

library(magick)

url_imagen = "https://c.files.bbci.co.uk/48DD/production/_107435681_perro1.jpg"
imagen = image_read(url_imagen)

plot(imagen)
Imagen base sobre el que aplicaremos el tratamiento de imágenes en Torch

Ahora que conocemos la imagen, podemos ver todo lo que podemos hacer con las transformaciones de Torchvision, entre las que encontramos:

  • transform_crop y transform_center_crop: permiten recortar la imagen. En el caso de transform_crop tenemos que indicar desde que pixel de abajo y a la izquierda empezar, mientras que en con center_crop se recorta empezando desde el medio.
  • transform_resizey transform_resized_crop: permiten re-escalar la imagen para que se ajuste al tamaño deseado. Además, en el caso de que queramos cortarla,contamos con la función resized_crop, que permite realizar ambos pasos en uno.
  • transform_hflip y transform_vflip permiten voltear la imagen tanto horizontal como verticalmente. Esta es una técnica muy habitual para aumentar de forma significativa el número de imágenes en el dataset.
  • transform_adjust: esta no es una función, sino una familia de funciones que permiten modificar diferentes aspectos de la imagen, como el brillo, contraste, gama o saturación.
  • transform_random: al igual que en el caso anterior, es una familia de funciones que permiten realizar, todas las funciones mencionadas anteriormente, pero únicamente a una muestra aleatoria de imágenes. De esta forma podemos incrementar el número de imágenes evitando que la red aprenda a “deshacer” las transformaciones generadas.

Dicho esto, veamos un ejemplo:

img_width = image_info(imagen)$width
img_height = image_info(imagen)$height

imagen_crop = transform_crop(imagen,0,0, img_height/2, img_width/2)
imagen_crop_center = transform_center_crop(imagen, c(img_height/2, img_width/2))
imagen_resize = transform_resize(imagen, c(img_height/2, img_width/2))
imagen_flip = transform_hflip(imagen)

image_grid = c(imagen_crop, imagen_crop_center, 
               imagen_resize, imagen_flip)

geometry = geometry_area(400, 200)

image_montage(image_grid, tile = '2', geometry = geometry)
Grid de imágenes tras aplicar algunas de las transformaciones diponibles en Torchvision para el tratamiento de imágenes en Torch

Aspectos a tener en cuenta de las transformaciones

Como vemos, las transformaciones son muy sencillas. Ahora bien, actualmente la versión 0.1.0 de torchvision no permite realizar todas las operaciones sobre todos los tipos de objeto que indican en la documentación. Por ejemplo, en la documentación de las funciones de ajustes de la imagen indican que funciona sobre objetos de clase magick-image, cuando, en realidad, no es así ya que devuelve un error.

Por suerte, cuando trabajamos con imágenes con Torch en R, generalmente no se quieren visualizar las imágenes que hemos creado. En su lugar, estas imágenes se quieren pasar a una red neuronal para entrenar a la red. Para ello, en Torch se utilizan el dataloader.

Cargar imágenes en Torch con dataloaders

Los dataloaders permiten tratar a las imágenes de forma agrupada. Por ejemplo, cuando entrenamos a una CNN, generalmente entrenamos a la red con grupos de imágenes llamados batch. Los dataloaders nos permiten definir, el tamaño del batch, si las imágenes son las mismas o si cada batch debe llevar una muestra aleatoria, etc.

Además, para ver cómo funciona el dataloader vamos a aprovechar para ver cómo descargar un dataset de torchvision, así como aprender cómo cargar un dataset que está guardado en una carpeta en local. Para ello, vamos a:

  1. Descargar un dataset precargado de torchvision.
  2. Cargar las imágenes desde la carpeta que acabamos de descargar.
  3. Crear el dataloader que nos permita cargar las imágenes en batches con un tamaño determinado.
  4. Cargar un batch.
# Descargo mnist usadno torchvision
tiny_imagenet_dataset("images", download = T)

# Cargo el dataset desde local
dataset = image_folder_dataset("images")

# Creo el data loader 
imagenes_dataloader = dataloader(dataset , batch_size =  10, shuffle = T)

# Guardo los batches
batch = imagenes_dataloader$.iter()$.next()

# Visualizo el primer batch.
batch[[1]]$size()
[1] 10 64 64  3

Como podemos ver, con Torch descargar datasets, cargar nuestros propios dataset y cargar esos datasets en batches es muy muy sencillo, aunque es cierto que aún tienen cosas por pulir.

Ahora, ya tenemos la estructura de la red, los datos de entrada y ya sabemos hacer el forward-pass para obtener una predicción. Ya solo queda una cosa: entrenar a la red. ¡Vamos a por ello!

Cómo entrenar una red neuronal con Torch en R

Independientemente de cómo hayamos creado la red, entrenar una red neuronal en Torch consta de cuatro pasos:

  1. Poner gradientes a cero.
  2. Definir y calcular el coste y el optimizador
  3. Propagar el error en la red.
  4. Aplicar las optimizaciones del gradiente.

Poner gradientes a cero

De cara a aplicar las optimizaciones, Torch acumula los gradientes de los pases hacia atrás. Nosotros no queremos que los gradientes se vayan acumulando, sino que en cada pase hacia atrás queremos dar un paso en la dirección hacia el mínimo, es decir, en casa iteración aplicar un único gradiente. Por tanto, en cada pase del entrenamiento debemos empezar poniendo los gradientes a cero.

Esto lo podemos hacer de forma muy sencilla aplicando el método zero_grad del optimizador que vayamos a definir. En nuestro caso, este punto lo haremos con la siguiente función:

optimizer$zero_grad()

Sí, se que parece una tontería, pero si te saltas este paso, tu red no entrenará bien.

Definir y calcular el coste y el optimizador

Al igual que en el caso de Keras, en Torch tenemos muchas funciones para ser utilizadas como funciones de coste, aunque básicamente las tres principales son:

  • nn_cross_entropy_loss: se utiliza con redes de clasificación con más de 2 categorías posibles.
  • nn_bce_loss: se utiliza en algoritmos de clasificación binaria, es decir, cuando predecimos entre dos clases.
  • nn_mse_loss: para los algoritmos utilizados en problemas de regresión.

Una vez hayamos definido la función de coste, debemos calcular el coste. Para ello, simplemente debemos pasar la predicción y los valores reales a nuestra función de coste.

Nota: En Torch es un estándar definir la función de coste en la variable criterion. No es algo obligatorio, pero seguramente en la documentación (sobre todo de PyTorch) lo verás así.

En este sentido, es importante remarcar una cuestión que ya he comentado: diferentes funciones de coste requieren que los datos de salida o los labels sean de una u otra forma. Por ejemplo, en mi caso que uso nn_cross_entropy_loss, los labels deben ser de tipo long, sino no funcionará.

Así pues, vamos a ver cómo crear y calcular el error:

# Creamos la funcion de coste y el optimizador
criterion = nn_cross_entropy_loss()  

# Calculamos el error
loss = criterion(pred_temp, y_train)
loss
torch_tensor
1.10872
[ CPUFloatType{} ]

Además, tenemos que definir el optimizador. En este caso también contamos con muchas opciones, aunque generalmente se usará el optimizador Adam, el cual obtiene buenos resultados.

Para crear nuestro optimizador, simplemente debemos pasar los parámetros de nuestro modelo a la función de optimizador que queramos. En nuestro caso, el optimizador Adam se define en la función optim_adam.

optimizer = optim_adam(model$parameters)

Propagar el error en la red

Ahora que ya tenemos calculado el error en la última capa y nuestro optimizador, tenemos que propagar el error por la red y después aplicar los gradientes.

Para propagar el error por la red, simplemente hay que aplicar el método backward de nuestro error.

loss$backward()

Aplicar las optimizaciones del gradiente

Por último, ahora que ya tenemos el error en cada neurona, hay que aplicar los gradientes. Para ello, simplemente hay que aplicar el método step de nuestro optimizador.

optimizer$step()

Estos serían todos los pasos para entrenar a una red neuronal con Torch. Pero, creo que quedará más claro si lo vemos todo junto en un ejemplo práctico, ¿no crees? ¡Pues vamos a ello!

Ejemplo de entrenamiento de una red neuronal en R con Torch

Ahora que ya conocemos todos los pasos por separado, vamos a ver cómo se haría todo junto. Como veréis, simplemente hay que “juntar” las distintas partes que hemos utilizado hasta ahora.


# Defino la red
model = nn_sequential(

  # Layer 1
  nn_linear(4, 8),
  nn_relu(), 

  # Layer 2
  nn_linear(8, 16),
  nn_relu(),

  # Layee 3
  nn_linear(16,3)
)

# Defino coste y optimizador
criterion = nn_cross_entropy_loss()  
optimizer = optim_adam(model$parameters, lr = 0.01)

epochs = 200

# Entreno la red
for(i in 1:epochs){

  optimizer$zero_grad()

  y_pred = model(x_train)
  loss = criterion(y_pred, y_train)
  loss$backward()
  optimizer$step()


  # Check Training
  if(i %% 10 == 0){

    winners = y_pred$argmax(dim=2)+1
    corrects = (winners == y_train)
    accuracy = corrects$sum()$item() / y_train$size()

    cat(" Epoch:", i,"Loss: ", loss$item()," Accuracy:",accuracy,"\n")
  }

}
 Epoch: 10 Loss:  0.7866052  Accuracy: 0.825 
 Epoch: 20 Loss:  0.4719219  Accuracy: 0.8333333 
 Epoch: 30 Loss:  0.3294412  Accuracy: 0.8416667 
 Epoch: 40 Loss:  0.2375475  Accuracy: 0.9333333 
 Epoch: 50 Loss:  0.1448946  Accuracy: 0.95 
 Epoch: 60 Loss:  0.08800832  Accuracy: 0.9666667 
 Epoch: 70 Loss:  0.06231292  Accuracy: 0.975 
 Epoch: 80 Loss:  0.04902349  Accuracy: 0.9833333 
 Epoch: 90 Loss:  0.0411404  Accuracy: 0.9833333 
 Epoch: 100 Loss:  0.03588869  Accuracy: 0.9833333 
 Epoch: 110 Loss:  0.03206198  Accuracy: 0.9833333 
 Epoch: 120 Loss:  0.02910745  Accuracy: 0.9833333 
 Epoch: 130 Loss:  0.02673575  Accuracy: 0.9833333 
 Epoch: 140 Loss:  0.02478053  Accuracy: 0.9833333 
 Epoch: 150 Loss:  0.02313195  Accuracy: 0.9833333 
 Epoch: 160 Loss:  0.02171922  Accuracy: 0.9833333 
 Epoch: 170 Loss:  0.02048633  Accuracy: 0.9916667 
 Epoch: 180 Loss:  0.01938933  Accuracy: 0.9916667 
 Epoch: 190 Loss:  0.01839607  Accuracy: 0.9916667 
 Epoch: 200 Loss:  0.01747981  Accuracy: 0.9916667 

¡Ya tenemos nuestra red neuronal creada con Torch en R! Bastante fácil, ¿verdad? Ya solo queda aprender una única cosa: aprender a guardar nuestros modelos para poder ponerlos en producción.

Guardar y Cargar modelos con Torch

Ahora que tenemos nuestro modelo entrenado, tendremos que ponerlo en producción (lo cual expliqué en este post). Para ello, hay que saber cómo guardar y cargar los modelos de Torch, lo cual tiene un par de cuestiones importantes.

Lo primero de todo, si tu modelo usa alguna capa de dropout o de batchnomarlization tenemos que poner esas capas en modo evaluación antes de hacer la predicción, ya que sino los resultados serán inconsistentes. Para ello, tenemos que aplicar el método eval de nuestro modelo.

Si nuestra red no cuenta con este tipo de capas, nos podemos saltar este paso.

model$eval()

Ahora que ya tenemos nuestras capas en “modo predicción”, vamos a guardar el modelo. Los modelos de Torch en Python suelen guardarse con las extensiones .pt o .pth. En nuestro caso, los modelos de Torch en R podemos guardarlos con esta extensión o, si contamos también con modelos de PyTorch (Torch en Python), podemos guardarlos con la extensión .rt, .rth.

torch_save(model, "modelo.rt")

Por último, para cargar el modelo simplemente debemos pasar la función torch_load y asignarlo a la variable que queramos:

modelo_cargado = torch_load("modelo.rt")
modelo_cargado
<nn_sequential>
  Inherits from: <nn_Module>
  Public:
    .apply: function (fn) 
    .classes: nn_sequential nn_module
    .load_from_state_dict: function (state_dict, prefix) 
    .save_to_state_dict: function (prefix, keepvars) 
    add_module: function (name, module) 
    apply: function (fn) 
    clone: function (deep = FALSE) 
    cpu: function () 
    cuda: function (device = NULL) 
    eval: function () 
    forward: function (input) 
    initialize: function (...) 
    load_state_dict: function (state_dict) 
    parameters: active binding
    register_buffer: function (name, tensor, persistent = TRUE) 
    register_parameter: function (name, param) 
    state_dict: function (prefix = "", keepvars = FALSE) 
    to: function (dtype = NULL, device = NULL, tensor = NULL, non_blocking = FALSE, 
    train: function (mode = TRUE) 
    training: TRUE
    zero_grad: function () 
  Private:
    buffers_: list
    modules_: list
    non_persistent_buffers_: 
    parameters_: list

¡Y con esto ya estaría todo! Ya sabes cómo crear redes neuronales con Torch en R (de dos formas diferentes), cómo crear los datos de entrada (incluso aplicando transformaciones por batch en caso de que sean imágenes), cómo entrenar a tus redes neuronales y, por último cómo guardarlas.

Por último, sí me gustaría hacer una pequeña comparativa entre Torch y Keras/Tensorflow, ya que creo es algo que merece la pena:

Comparación crear redes neuronales con Torch vs Keras/Tensorflow en R

Si queremos comparar Keras/Tensorflow y Torch en R, creo que hay dos cuestiones que tenemos que tener en cuenta:

  • Torch, actualmente (v0.1.1) está en un estado incipiente. A grandes rasgos funciona bien, pero sí que se nota en dos sentidos:
    • Falta de documentación. Falta mucha, muchísima documentación y ejemplos reales en R. Es cierto, que el framework es el mismo que en Python, por lo que puedes revisar la documentación de PyTorch, siempre y cuando sepas de Python. Sin duda, creo que es algo muy relevante.
    • Funciones incompletas: no todas las funciones tienen las características que se indican en la documentación. Si bien es algo que a corto plazo se arreglará, en el momento de escribir esto, es algo molesto.
  • Keras y Tensorflow requieren de Python, mientras que Torch funciona directamente con C++. De cara a poner los modelos en producción esto supone una gran diferencia, ya que si quieres poner un modelo de Tensorflow/Keras en producción con código R, el Docker pesará casi el doble que si lo haces con Torch.

Dicho esto, personalmente hay ciertas cosas que me gusta más en cada uno de ellos. En mi opinión, Torch es algo más intuitivo de utilizar, ya que parece que está más integrado en el lenguaje. Esto hace también que me resulte más flexible.

Por su parte, Tensorflow/Keras cuentan con funcionalidades no desarrolladas en Torch que te ahorran tener que escribir más líneas de código, como por ejemplo Tensorboard.

En cualquier caso, creo que ambos frameworks son muy interesantes, útiles y, a nivel conceptual, funcionan con abstracciones bastante parecidas. Así que, si puedes, te animaría a aprender ambos.

Como siempre, si este tutorial completo de Torch en R te ha resultado interesante, te animo a suscribirte para no perderte los posts que voy subiendo. Y si tienes cualquier duda, no dudes en escribirme. ¡Nos vemos en el siguiente!

¿Te gusta el contenido? ¡Apoya el blog!

Si te gusta el contenido, si quieres puedes apoyar mi blog con una pequeña donación. Así, podré cubrir los costes que supone crear y mantener este blog y podré utilizar más herramientas Cloud con las que seguir creando contenido gratuito para que más personas mejoren como Data Scientist. 

¡No te pierdas ningún post!

Si te gusta lo que lees... suscríbete para estar al día de los contenidos que subo.
¡Serás el primero en enterarte!