La structure de données utilisée dans PyTorch est basée sur les graphes et les tenseurs, il est important de comprendre les opérations de base et la définition des tenseurs.
Les tenseurs sont un moyen de représenter des données, en particulier des données multidimensionnelles, c'est-à-dire des données numériques.
On va donc s'entraîner sur les tenseurs et leurs opérations.
import torch # On importe pytorch.
print(torch.__version__) # On affiche sa version.Table des Contenus
Même si dans PyTorch, presque tout est appelé tenseur, il existe différents types de tenseurs.
Le premier type de tenseur que nous allons créer s'appelle un scalaire. Je sais que je vais te balancer un grand nombre de noms étranges de choses différentes, mais il est important que tu sois au courant de tous ces nomenclatures.
# on crée un scalaire
scalar = torch.tensor(10)
print(scalar)
# on affiche le nombre de dimensions
print(f"Nombre de dimension du scalaire : {scalar.ndim}")
# on affiche les éléments
print(f"Les elements du scalaire : {scalar.item()}")
Donc, comme tu le vois, un scalaire n'a pas de dimension (scalare.ndim = 0)
et n'a qu'un seul élément, c'est la valeur du nombre, ici le nombre 10.
On peut créer un tenseur à une ligne (communement appelé vecteur) à partir d'un simple tableau python.
vector = torch.tensor([2, 3, 90, 19, -8], dtype=torch.float32)
print(vector)Le paramètre dtype est souvent définisable pour la plupart des fonctions
qui produisent des tenseurs en sortie. Ce paramètre permet de définir le
type des éléments du tenseur. Dans notre exemple ici, dtype est définit à
torch.float32, donc les valeurs 2, 3, 90, 19,-8 contenues au
préalable dans le tableau python passé en argument à la fonction
torch.tensor() seront transformées en nombre réel simple précision
(float32).
On peut créer un tenseur depuis un tableau numpy.
import numpy as np
# On crée le tableau numpy à partir d'un tableau python.
numpy_tab = np.array([[1, -2], [3, 4]])
# On crée ensuite le tenseur à partir du tableau numpy.
print(torch.tensor(numpy_tab))Comme son nom l'indique, cette fonction permet de convertir un tableau numpy
en tenseur.
import numpy as np
import torch
x = np.array([12, 13, 34, 9, 89]) # On crée un tableau numpy.
t = torch.from_numpy(x)
# Ce tableau est ensuite passé à la fonction from_numpy()
print(t) # On affiche le tenseur obtenu.Cette fonction permet de définir un tenseur nul, c'est à dire un tenseur dont
tous les entrés sont à 0.
vector_null = torch.zeros((10,)) # pour définir un vecteur nul.
matrix_null = torch.zeros((2, 4)) # pour définir une matrice nulle.
tensor_null = torch.zeros((3, 2, 4)) # pour définir un tenseur nul.
print("vecteur nul :", vector_null)
print("matrice nulle :", matrix_null)
print("tenseur nul :", tensor_null)Cette fonction permet de définir un tenseur dont tous les entrés sont à 1.
vector_ones = torch.ones((10,)) # pour définir un vecteur rempli de 1.
matrix_ones = torch.ones((2, 4)) # pour définir une matrice rempli de 1.
tensor_ones = torch.ones((3, 2, 4)) # pour définir un tenseur rempli de 1.
print("vecteur nul :", vector_ones)
print("matrice nulle :", matrix_ones)
print("tenseur nul :", tensor_ones)Comme les opérations NumPy, la fonction eye() permet de créer une matrice
diagonale, dont les éléments diagonaux sont des uns (1), et les éléments qui
ne sont pas dans la diagonale sont des zéros (0).
# on crée une matrice de 3 lignes et 4 colonnes.
print(torch.eye(3, 4))Cette fonction permet de générer un tableau de valeurs discrettes comprises dans un intervalle donné. Ces valeurs sont équi-distantes d'un pas bien défini.
# On va générer un tableau de valeurs comprises
# entre 10 et 40 avec un pas de 2
values_2 = torch.arange(10, 40, 2)
print(values_2)Par défaut, le pas vaut 1, si tu ne le définis pas.
print(torch.arange(10, 40))Il faut remarquer que l'intervalle utilisé pour générer les valeurs du tableau
est
La fonction linspace() permet de générer
print(torch.linspace(2, 10, steps=25))Il s'agit donc d'une espace linéaire. Tout comme les espace linéaires,
les espaces logarithmiques peuvent être créés en utilisant la fonction
logspace().
print(torch.logspace(start=10, end=15, steps=15))On peut vérifier si un objet en Python est un objet tenseur en utilisant les
fonctions is_tensor() et is_storage(). En générale, ces deux fonctions
vérifient si l'objet est stocké en tant qu'objet tensoriel.
x = [12, 13, 34, 9, 89] # juste une liste python
print(torch.is_tensor(x)) # False
print(torch.is_storage(x)) # FalseMaintenant, créons un objet qui contient des nombres générés de façon
aléatoire à partir de torch, similaire à la bibliothèque NumPy.
y = torch.randn(1, 2, 3, 4, 5)
print(y)Ensuite, on vérifie le type de y.
print(torch.is_tensor(y)) # True
print(torch.is_storage(y)) # FalseL'objet y est un tenseur, mais il n'est pas stocké.
La fonction numpy() permet de récupérer un tenseur sous forme de tableau
numpy.
# On crée un simple tenseur.
t = torch.tensor([9., 4., 0.3, 9.])
print("Tenseur t :", t)
# On récupérer le tableau numpy du tenseur t.
numpy_tab = t.numpy()
print("Tableau numpy :", numpy_tab)
print(type(numpy_tab)) # on affiche le type.La fonction argmax() permet de trouver les indices respectifs des valeurs
maximales des éléments d'un tenseur. Elle renvoie uniquement les indices,
et non les valeurs des l'éléments.
import torch
T = torch.tensor([[-1.6729, 1.2613, -1.2882, -0.8133],
[ 0.9192, 0.9301, -0.2372, 0.0162],
[-0.4669, 0.6604, -0.7982, 0.2621],
[ 0.6436, 1.0328, 2.4573, 0.0606]])
# La valeur la plus élevée de tous les éléments de T est : 2.4573
# et son indice est : 14.
indices = torch.argmax(T)
print("argmax(T) =", indices)On peut aussi appliquer la fonction torch.argmax() pour calculer les indices
respectifs des valeurs maximales d'un tenseur à travers chacune de ses
dimensions.
import torch
T = torch.tensor([[-1.6729, 1.2613, -1.2882, -0.8133],
[ 0.9192, 0.9301, -0.2372, 0.0162],
[-0.4669, 0.6604, -0.7982, 0.2621],
[ 0.6436, 1.0328, 2.4573, 0.0606]])
# On récupère les indices maximales sur chaque colonne.
row_indices = torch.argmax(T, dim=0, keepdim=True)
print(row_indices)
# On récupère les indices maximales sur chacune ligne.
col_indices = torch.argmax(T, dim=1, keepdim=True)
print(col_indices)Lorsqu'on définit le paramètre keepdim à True, alors les indices sont
présentés dans un tenseur de même dimension que le tenseur T étudié.
Cette fonction est comme la fonction argmax(), à la différence
qu'elle permet de retrouver les indices respectifs des valeurs
minimales des éléments d'un tenseur. Elle permet aussi de retrouver les valeurs
minimales du tenseur suivant ces dimensions.
import torch
T = torch.tensor([[-1.6729, 1.2613, -1.2882, -0.8133],
[ 0.9192, 0.9301, -0.2372, 0.0162],
[-0.4669, 0.6604, -0.7982, 0.2621],
[ 0.6436, 1.0328, 2.4573, 0.0606]])
# La valeur la plus élevée de tous les éléments de T est : 2.4573
# et son indice est : 14.
indices = torch.argmin(T)
print("argmin(T) =", indices)
# On récupère les indices maximales sur chaque colonne.
row_indices = torch.argmin(T, dim=0)
print("argmin(T, dim=0) =", row_indices)
# On récupère les indices maximales sur chacune ligne.
col_indices = torch.argmin(T, dim=1)
print("argmin(T, dim=1) =", col_indices)La fonction Tensor.squeeze() est utilisée pour redimensionner un tenseur en
supprimant toutes ses dimensions de taille 1.
Par exemple : Si les dimensions du tenseur squeeze() sur ce tenseur, alors cette fonction te retournera
un autre tenseur
import torch
T = torch.randn(3, 1, 4, 1, 8, 1)
print("T.shape =", T.shape)
S = torch.squeeze(T)
print("S.shape =", S.shape)Prenons un exemple beaucoup plus simple. Si tu as une matrice, c'est à dire :
un tenseur de dimension 2, d'une ligne et squeeze() sur cette matrice,
tu obtiens en sortie un vecteur, c'est à dire : un tenseur de dimension 1 à
import torch
T = torch.tensor([[2.4,
0.9,
8.8,
1.03]])
print("T.shape =", T.shape)
print("T =", T)
S = torch.squeeze(T)
print("\nS.shape =", S.shape)
print("S =", S)On peut renseigner le deuxième paramètre de la fonction squeeze().
Ce paramètre permet de préciser à la fonction laquelle des dimensions de 1
il faut supprimer.
import torch
T = torch.randn(1, 2, 1, 3, 1, 4, 1, 5, 1)
S = [None] * 9
S[0] = torch.squeeze(T, 0) # ( 2, 1, 3, 1, 4, 1, 5, 1)
S[1] = torch.squeeze(T, 1) # (1, 2, 1, 3, 1, 4, 1, 5, 1) 2 != 1, pas d'effet.
S[2] = torch.squeeze(T, 2) # (1, 2, 3, 1, 4, 1, 5, 1)
S[3] = torch.squeeze(T, 3) # (1, 2, 1, 3, 1, 4, 1, 5, 1) 3 != 1, pas d'effet.
S[4] = torch.squeeze(T, 4) # (1, 2, 1, 3, 4, 1, 5, 1)
S[5] = torch.squeeze(T, 5) # (1, 2, 1, 3, 1, 4, 1, 5, 1) 4 != 1, pas d'effet.
S[6] = torch.squeeze(T, 6) # (1, 2, 1, 3, 1, 4, 5, 1)
S[7] = torch.squeeze(T, 7) # (1, 2, 1, 3, 1, 4, 1, 5, 1) 5 != 1, pas d'effet.
S[8] = torch.squeeze(T, 8) # (1, 2, 1, 3, 1, 4, 1, 5, )
print("T.shape =", T.shape, "\n")
for i in range(9):
print(f"S{i}.shape", S[i].shape)Concernant la fonction torch.unsqueeze(), on dira tout simplement le
contraire de la fonction squeeze().
torch.unsqueeze() renvoie un nouveau tenseur avec la dimension 1 insérée à la
position spécifiée en second argument.
import torch
# Prenons un exemple simple:
x = torch.tensor([1, 2, 3, 4])
print("x =", x)
print("x.shape =", x.shape)
# on cible les lignes:
y0 = torch.unsqueeze(x, 0)
print("\ny0 =", y0)
print("y0.shape =", y0.shape)
# on cible les colonnes:
y1 = torch.unsqueeze(x, 1)
print("\ny1 =", y1)
print("y1.shape =", y1.shape)Donc, si la dimension du tenseur t est (3, 4, 5) alors son unsqueeze(t, 0) donnera un tenseur de dimension (1, 3, 4, 5) et son unsqueeze(t, 1) donnera un tenseur de dimension (3, 4, 1, 5).
import torch
t = torch.randn(3, 4, 5)
print("t.shape =", t.shape)
t0 = torch.unsqueeze(t, 0)
print("t0.shape =", t0.shape)
t2 = torch.unsqueeze(t, 2)
print("t2.shape =", t2.shape)import torch
from torch.autograd import Variable
# Ici on crée une variable contenant un poid
W = torch.Tensor([3.0], requires_grad=True)
def forward(w, x):
# On calcule le produit des poinds W
# avec n'importe quelle variable
return w * x
# on va appeller forward() avec 4.
x = 4
y = forward(W, x)
print("predict (before training): for x = {}, y = {:.2f}".format(x, y[0]))x_data = [11.0, 22.0, 33.0]
y_data = [21.0, 14.0, 64.0]
for epoch in range(5):
print("\nProgress: ", epoch)
for x, y in zip(x_data, y_data):
y_pred = forward(W, x) # on calcule
y_pred.backward() # on calcule le gradient
print(
"\t* x, y: ({:6.2f}, {:6.2f}) \t grad: {:6.3}".format(
x, y, W.grad[0]
)
)
# on fait la correction d'erreur en utilisant
# la formule de la descente de gradient.
with torch.no_grad():
W -= 0.01 * W.grad
# Remettre manuellement les gradients à zéro
# après la mise à jour des poids.
W.grad.zero_()La génération de nombres aléatoires est très souvent utilisée en science des données. Les nombres aléatoires peuvent être générés à partir d'une distribution statistique, de deux valeurs quelconques ou d'une distribution prédéfinie.
La distribution uniforme est définie comme une distribution où
chaque résultat a la même probabilité de se produire. Tout comme les fonctions
NumPy, les nombres aléatoires peuvent être générés dans un tenseur à l'aide de
la fonction rand().
# On va générer 10 nombres aléatoires issus d'une distribution
# uniforme entre les valeurs 0 et 1.
uniform_random_numbers = torch.rand(10)
print(uniform_random_numbers)Les nombres aléatoires d'une distribution normale avec une moyenne
arithmétique de 0 et un écart type de 1 peuvent également être générés dans
un tenseur en utilisant la fonction randn().
# On va générer 10 nombres aléatoires issus d'une distribution normale,
# avec une moyenne = 0 et un écart-type = 1
normal_rand_numbers = torch.randn(10)
print(normal_rand_numbers)randn() génère un tableau à n dimensions en fonction du paramètrage. Si tu
renseigne un seul paramètre comme dans l'exemple ci-dessus, alors tu auras
un tableau à une dimension. Si tu renseigne deux paramètres alors tu auras un
tableau à deux dimensions, et ainsi de suite...
# Exemple de génération d'un tableau à 3 dimensions
tab_3d = torch.randn(2, 3, 5)
print(tab_3d)Dans ce résultat, on a 2 matrices à 3 lignes et 5 colonnes. Je pense que
tu as compris.
Cette fonction permet de sélectionner des valeurs de façon aléatoire
dans une plage ou intervalle de valeurs comprises entre 0 et n.
# On va générer des valeurs de façon aléatoire comprises entre 0 et 10.
random_values = torch.randperm(10)
print(random_values)Si tu recommence l'execution de torch.randperm(10), tu auras un résultat
différent du précédent. D'ailleur, je suis sûre que le résultat que tu as
obtenu chez toi est différent de celui qui ce trouve sur la capture ci-dessus.
# Allez, exécutons 3 fois.
print(torch.randperm(10))
print(torch.randperm(10))
print(torch.randperm(10))Je crois que tu as compris et si tu analyses bien les résultats obtenus avec
l'argument 10, tu remarqueras que randperm() ne fait que des permutations,
déjà, son nom nous donne un indice : rand perm, random permutation.
Du coup, le nombre de possibilités de tenseurs qu'on peut avoir avec
torch.randperm(10) est égal à 3 628 800.
Comment ça ? 🤔
C'est simple, j'ai juste calculé
import math
print(math.factorial(10))Donc avec l'argument 10 passé à torch.randperm() tu peux avoir 3 628 800
de tenseurs possibles. Donc, de façon générale, pour une valeur n passée
à torch.randperm(), tu peux obtenir

























