1. Livres & vidéos
  2. Data Scientist et langage R
  3. Deep Learning avec PyTorch ou Keras/TensorFlow
Extrait - Data Scientist et langage R IA, Machine Learning et Statistiques, Forecast, Tenseur, Gradient, Pytorch, Keras, CNN, LLM, GPT, RAG… (4e édition)
Extraits du livre
Data Scientist et langage R IA, Machine Learning et Statistiques, Forecast, Tenseur, Gradient, Pytorch, Keras, CNN, LLM, GPT, RAG… (4e édition) Revenir à la page d'achat du livre

Deep Learning avec PyTorch ou Keras/TensorFlow

Considérations préliminaires sur l’utilité d’un framework puissant comme PyTorch ou TensorFlow

1. Croissance rapide du coût de l’entraînement des modèles du Deep Learning

Le coût d’entraînement des modèles ML à grande échelle augmente à un rythme de 2,5 fois par an. La mise au point de modèles les plus avancés coûte aujourd’hui des centaines de millions de dollars. Environ la moitié de ces dépenses est consacrée aux GPU, le reste étant consacré à d’autres matériels et à l’énergie.

Les ensembles de données nécessaires à l’entraînement des LMM augmentent à un rythme de trois fois par an. On utilise actuellement des fichiers de données contenant des dizaines de billions de mots (c.-à-d. mille milliards de mots, en français). Mais il va falloir faire encore mieux, car les plus grands ensembles de données publiques sont environ dix fois plus grands que cela, par exemple "Common Crawl" contient des centaines de billions de mots avant filtrage.

Il faut donc utiliser des frameworks "scalables" pour entraîner des modèles sur des ensembles de données de cette taille et pour optimiser l’usage des GPU en leur fournissant des données adaptées à leur architecture, c’est-à-dire des tensors (tableaux multidimensionnels) de grande taille et au bon format.

2. Formats de nombres optimisés pour l’IA

Dans le contexte de l’informatique de l’IA, plusieurs formats de nombres sont utilisés dans les calculs effectués par les GPU. Dans la mesure du possible, il convient de choisir le format le plus adapté à votre problème et il semble judicieux d’utiliser un framework vous permettant de gérer les tenseurs que vos GPU attendent.

En première approximation, choisissez toujours le format le moins coûteux en GPU accessible par votre technologie, mais compatible avec les exigences de votre application, car souvent la perte de précision sera limitée mais l’efficacité une fois en production sera plus importante. En effet, il faut toujours avoir en tête que pour de nombreuses applications, vous aurez à servir plusieurs...

Présentation de PyTorch dans l’environnement R

Comme Tensorlow, PyTorch est une bibliothèque open source de calcul tensoriel et de différenciation automatique utilisée dans le domaine du Deep Learning et dans toutes les disciplines qui nécessitent la manipulation de tenseurs. Le site https://pytorch.org/ propose une documentation complète et des tutoriels pour apprendre à utiliser cette bibliothèque. Son GitHub (https://github.com/pytorch/pytorch), bien conçu, vous permettra une éventuelle plongée dans ses arcanes. Cette bibliothèque comprend de très nombreux modules et fonctions qui permettent de faciliter la construction de réseaux de neurones de toutes sortes.

Nous allons utiliser Rtorch, un package R qui permet d’utiliser PyTorch dans l’environnement R. Ce package est disponible sur le CRAN. Sa documentation se trouve sur le site https://torch.mlverse.org/.

En ce qui concerne les composants du système et les packages Python, nous vous conseillons d’installer les versions : nvidia/cuda, nvidia/cudnn pytorch, tensorflow et les éléments qui vous seront utiles dans leurs écosystèmes via pip ou conda. Pour les installations locales de ces outils en ligne de commande, il est conseillé de créer un environnement virtuel pour ne pas perturber les autres installations de votre machine et de passer vos commandes dans le terminal de RStudio (deuxième onglet en bas à gauche de l’interface de RStudio).

Dans RStudio, vous pouvez stipuler la version de Python que vous allez utiliser pour l’ensemble de vos projets ou pour un projet particulier. Pour cela, allez dans les options de RStudio, puis dans l’onglet General et enfin dans Python où...

PyTorch en Python versus sa version R (torch/R)

Si vous êtes développeur Python ou si vous lisez une documentation Python alors que vous êtes développeur R, vous ne serez pas perdu en vous référant à ce chapitre.

En effet, il y n’a pas de différence logique ou conceptuelle entre les deux implémentations, puisque l’implémentation R s’appuie sur l’implémentation Python. On trouve évidemment quelques légères différences syntaxiques entre les implémentations Python et R de PyTorch, résultant évidemment des différences syntaxiques entre les deux langages (abordées plus haut dans l’ouvrage). Voici les différences les plus courantes :

  • En Python (PyTorch), on invoque les méthodes via tensor.method(...).

  • En R (torch), on appelle les méthodes via tensor$method(...).

  • Les arguments de formes (dimensions) de R s’expriment souvent en les compactant dans un vecteur R : c(dim1, dim2,...).

  • Les indices de slicing en R commencent à la valeur 1, comme en mathématique (versus 0-based en Python).

  • Parfois, des fonctions PyTorch sous Python comportant un " ." dans leur nom voient ce point se transformer en " _" dans R.

Opérations tensorielles fondamentales, illustrées avec torch

Cette section est très importante, car vous y trouverez non seulement les syntaxes des tenseurs torch/R, mais aussi de nombreuses mécaniques de manipulation de données numériques tensorielles utilisées en intelligence artificielle.

Prenez le temps nécessaire pour assimiler le sens de ces diverses opérations, quitte à survoler les syntaxes.

1. Création d’un tenseur

On peut créer un tenseur à partir de données brutes (par exemple, un vecteur ou une matrice) :

  • Tenseur 1D : (1,2,3,4).

  • Tenseur 2D : ([1,2), [3,5)).

library(torch)  
 
# Création d'un tenseur 1D  
t1 <- torch_tensor(c(1, 2, 3, 4), dtype = torch_float())  
t1  
##>  torch_tensor  
##>   1  
##>   2  
##>   3  
##>   4  
##>  [ CPUFloatType{4} ] 

# Création d'un tenseur 2D  
t2 <- torch_tensor(matrix(c(1, 2, 3, 5), nrow = 2, byrow = TRUE),   
                   dtype = torch_float())  
t2  
##>  torch_tensor  
##>   1  2  
##>   3  5  
##>  [ CPUFloatType{2,2} ] 

On peut accéder aux valeurs de ce tenseur comme on le ferait d’une autre structure R.

t2[2,2] # valeur deuxième ligne, deuxième colonne  
##>  torch_tensor  
##>  5  
##>  [ CPUFloatType{} ]  
t2[2, ] # valeurs deuxième ligne  
##>  torch_tensor  
##>   3  
##>   5  
##>  [ CPUFloatType{2} ]  
t2[ ,2] # valeurs deuxième colonne  
##>  torch_tensor  
##>   2  
##>   5  
##>  [ CPUFloatType{2} ]  
 
 
# affectation de la valeur 4 en deuxième ligne, deuxième colonne  
t2[2,2] <- 4   
t2  
##>  torch_tensor  
##>   1  2  
##>   3  4  
##>  [ CPUFloatType{2,2}...

Notation d’Einstein

La notation d’Einstein est un formalisme très puissant et facile à apprendre, permettant de décrire de nombreuses opérations entre tenseurs.

La notation d’Einstein (Einstein summation convention, ou einsum) est un formalisme pratique pour spécifier des opérations de somme et de multiplication sur plusieurs tenseurs, sans avoir à écrire explicitement les symboles de sommes.

Par exemple, quand on écrit :

images/eq73N.png

On effectue une multiplication matricielle entre A d’indices (i,j) et B d’indices (j,k) pour produire C d’indices (i,k). La notation d’Einstein nous permet d’écrire ceci plus succinctement sous la forme :

images/eq74N.png

Ici, on indique explicitement quelles dimensions sont "contractées" (j), et lesquelles subsistent dans la sortie : (i) et (k).

Dans notre ouvrage sur TensorFlow aux Éditions ENI, nous montrons comment l’utiliser avec TF. Ici, la syntaxe en R Torch est très proche. On utilise pour cela la fonction torch_einsum.

Soit deux matrices :

images/eq75N.png

Le résultat de leur multiplication sera tel que :

images/eq76N.png

Ce qui donne ceci en R :

A <- torch_randn(c(2,3))  
B <- torch_randn(c(3,4))  
C <- torch_einsum("ij,jk->ik", list(A,B))  
C$size()  
##>  [1] 2 4 

Nous allons parcourir différents exemples, sachant que dans la convention d’Einstein, lorsque le même indice apparaît deux fois, cela signifie que l’on somme sur cet indice.

La fonction torch_einsum est puissante et permet d’indiquer sur quels indices effectuer la contraction et comment elle sera spécifiée par l’indice résultat. À gauche de la flèche ->, on met les indices respectifs des tenseurs, et à droite, le résultat. Pour un produit scalaire, cela donne :

  • "i,i" pour stipuler les indices de xi et yi ;

  • rien après la flèche pour stipuler que l’on obtient un scalaire (sans indice).

Par exemple :

library(torch)  
 
x <- torch_tensor(c(1, 1, 1))  
y <- torch_tensor(c(2, 2, 2))  
 
s <- torch_einsum("i,i->", list(x, y))  
s  
##>  torch_tensor  
##>  6  
##>  [ CPUFloatType{} ] 

produit scalaire x.y  
--> tenseur de forme...

Création d’un réseau de neurones simple (NN “vanilla”) avec PyTorch

Afin de vous rassurer immédiatement sur la lisibilité du code torch/R, nous allons définir un petit réseau de neurones. Ce réseau de neurones sera composé de deux couches linéaires.

library(torch)  
 
# Définition du modèle  
model <- nn_module(  
  "SmallModel",  
  initialize = function() {  
    self$fc1 <- nn_linear(10, 5)  
    self$fc2 <- nn_linear(5, 1)  
  },  
  forward = function(x) {  
    x %>% self$fc1() %>% nnf_relu() %>% self$fc2()  
  }  
)  
 
# Créer une instance du modèle  
my_model <- model() 

La première couche linéaire a une entrée de taille 10 et une sortie de taille 5, tandis que la deuxième couche linéaire a une entrée de taille 5 et une sortie de taille 1. La fonction forward prend en entrée un tenseur x et applique les couches linéaires et la fonction d’activation ReLU. Il conviendrait maintenant d’entraîner ce modèle sur des données réelles pour voir comment il se comporte.

Un réseau de neurones effectuant une régression, avec Neuralnet puis avec Torch

Nous allons créer un réseau de neurones effectuant une régression en utilisant successivement deux outils : Neuralnet et torch/R. Nous sommes dans le cas de réseaux neuronaux de petites dimensions sur des données tabulaires où Neuralnet peut être très crédible. Pour le Deep Learning, vous devrez utiliser directement PyTorch, TensorFlow (et/ou Keras).

Neuralnet est une solution très efficace qui effectue une grande partie du travail à votre place, alors que Torch est beaucoup plus puissant et versatile, mais vous demandera plus d’efforts de conception. Le réseau conçu par Neuralnet a donc toutes les chances d’être plus pertinent que le vôtre en torch/R : en fait, il faut considérer le réseau Neuralnet comme la base de référence que vous chercherez à dépasser de passe en passe. Mais avant toute conclusion hâtive, il faut aussi s’assurer que votre réseau neuronal fait mieux qu’un simple algorithme de régression linéaire, infiniment moins consommateur en puissance informatique !

Nous allons utiliser un exemple de données nommées Friedman2 qui représente un problème de régression synthétique utilisé comme benchmark pour évaluer les algorithmes d’apprentissage automatique. Ce problème a été introduit par Jerome H. Friedman dans un article de 1991.

Les données contiennent quatre variables d’entrées indépendantes qui sont générées de manière uniforme dans les plages suivantes :

  • 0≤x_1≤100 ;

  • 40π≤x_2≤560π ;

  • 0≤x_3≤1 ;

  • 1≤x_4≤11.

La variable de sortie y est calculée au travers de l’expression suivante :

images/eq80N.png

où e est une erreur aléatoire suivant une distribution normale N(0,"sd").

Les données Friedman2 sont souvent utilisées pour tester les performances des modèles de régression, en particulier dans des contextes où les relations entre les variables sont non linéaires et complexes. Elles permettent d’évaluer la capacité des modèles à capturer des interactions non triviales...