Tenseur + TD1 + TD2 ******************* PyTorch permet d’exécuter des opérations complexes sur des tableaux multidimensionnels appelés tenseurs ceci en une seule instruction. Cette syntaxe permet d'éviter l'écriture de boucles ce qui rend la lecture du code pour compacte. Les tenseurs sont l’héritage direct des tableaux multidimensionnels introduits en calcul scientifique notamment par la librairie **NumPy**. Ainsi, une grande partie de la syntaxe de PyTorch est héritée de celle de NumPy. Comme nous allons travailler 95% du temps avec des tenseurs, nous choisissons de ne plus présenter Numpy et d'introduire directement les tenseurs Pytorch. Convention ========== Dans la suite, nous supposerons que nous avons importé la librairie PyTorch ainsi : .. code-block:: python import torch Les tenseurs PyTorch ==================== Les tenseurs PyTorch sont des structures de données implémentées en C++, stockant des valeurs de même type dans un bloc mémoire contigu, ce qui leur permet d’atteindre des performances élevées sur CPU et GPU. Ils constituent la structure de base de PyTorch. Dans nos applications, nous devons stocker des données (images, sons, textes...). Par exemple, un lot de 50000 images en niveaux de gris de résolution 36x36 pourra être stocké dans un tenseur de dimensions 50000x36x36. Les tenseurs sont très similaires aux tableaux NumPy, mais ils offrent en plus : * l’accélération GPU * le calcul automatique des gradients (autograd) * l’intégration directe avec les modèles de deep learning Liens utiles : * `La documentation des tenseurs PyTorch `_ * `PyTorch fundamentals `_ .. warning:: Si vous ne précisez pas le type des éléments lors de la création d'un tenseur, PyTorch choisira un type par défaut (généralement **torch.float32** ou **torch.int64**). En deep learning, il est recommandé de toujours préciser explicitement le type pour éviter toute ambiguïté et contrôler la consommation mémoire. Fonctions de création --------------------- .. list-table:: :widths: 35 65 * - torch.zeros(size=(2,3), dtype=torch.int32) - Crée un tenseur 2x3 de valeurs entières nulles * - torch.ones(size=(5,5), dtype=torch.int32) - Crée un tenseur 5x5 d'entiers valant 1 * - torch.full(size=(3,3), fill_value=4) - Crée un tenseur 3x3 initialisé avec la valeur 4 * - torch.arange(4) - Crée le tenseur [0,1,2,3] * - torch.arange(0, 10, 2) - Crée le tenseur 1D [0, 2, 4, 6, 8] * - torch.linspace(0, 1, steps=5) - Séquence régulière : tensor([0.00, 0.25, 0.50, 0.75, 1.00]) * - torch.empty(size=(3,2), dtype=torch.float32) - Crée un tenseur de flottants sans initialiser leurs valeurs * - T.clone() - Duplique un tenseur T Fonctions de génération aléatoire --------------------------------- .. list-table:: :widths: 35 65 * - torch.randint(low=3, high=8, size=(2,4)) - Tenseur 2x4 avec valeurs aléatoires parmi 3, 4, 5, 6 et 7 * - torch.rand(size=(3,2)) - Tenseur 3x2 avec valeurs aléatoires dans [0,1[ suivant une distribution uniforme * - torch.normal(mean=0, std=1, size=(3,2)) - Tenseur avec valeurs aléatoires suivant une loi normale(0,1) Conversion ---------- Pour Tensor ↔ Python : .. list-table:: * - torch.tensor([1,2,3]) - liste Python → tenseur * - T.tolist() - tenseur → liste Python * - T.item() - tenseur contenant 1 seule valeur → valeur Python Pour Tensor ↔ NumPy : .. list-table:: * - torch.from_numpy(A) - NumPy → tenseur (partage mémoire) * - T.numpy() - tenseur CPU → NumPy Conversion de type ------------------ .. list-table:: * - x = x.to(torch.float32) - Conversion vers float32 * - x = x.to(torch.int32) - Conversion vers int32 Propriétés du tenseur --------------------- Pour un tenseur de **taille** (2, 4, 5), 3 correspond au **nombre de dimensions** et 4 à **la taille de la dimension 1/l’axe 1**. .. list-table:: * - T.size() ou T.shape - Taille du tenseur : (2,4,5) * - T.dim() ou T.ndim - Nombre de dimensions : 3 * - T.size(1) ou T.shape[1] - Taille de la dimension 1/axe 1 : 4 * - T.numel() - Nombre de valeurs dans T : 40 * - T.dtype - Type des données de T Tenseur scalaire ---------------- .. list-table:: * - T = torch.tensor(3.14) - Crée un tenseur scalaire Un tenseur scalaire est un tenseur de dimension 0 représentant un nombre. Il est utilisé pour effectuer des opérations comme la multiplication ou l’addition d'un tenseur avec une valeur. Exercice ======== .. quiz:: pytorch_tensors_creation :title: Création des tenseurs PyTorch :quiz:`{"type":"FB","answer":"dtype"}` Quelle propriété retourne le type des données ? :quiz:`{"type":"FB","answer":"dim"}` Quelle propriété retourne le nombre de dimensions d’un tenseur ? :quiz:`{"type":"FB","answer":"zeros"}` Quelle fonction crée un tenseur initialisé avec des 0 ? :quiz:`{"type":"FB","answer":"rand"}` Quelle fonction génère des valeurs aléatoires uniformes à l'intérieur d'un intervalle ? :quiz:`{"type":"FB","answer":"tensor"}` Quelle fonction crée un tenseur PyTorch depuis une liste Python ? :quiz:`{"type":"FB","answer":"to"}` Quelle fonction convertit les éléments d'un tenseur vers un autre type ? :quiz:`{"type":"FB","answer":"5"}` Pour un tenseur de dimensions (3,4,5), l'axe 2 a quelle taille ? :quiz:`{"type":"FB","answer":"dtype"}` Quel argument permet de sélectionner le type des données ? :quiz:`{"type":"FB","answer":"tolist"}` Quelle fonction transforme un tenseur en une liste Python ? :quiz:`{"type":"FB","answer":"ones"}` Quelle fonction crée un tenseur initialisé avec des 1 ? :quiz:`{"type":"TF","answer":"F"}` Peut-on effectuer reshape((2,3)) sur T = torch.arange(4) ? :quiz:`{"type":"TF","answer":"T"}` Pour un tenseur vecteur de taille 5, T[2] et T[-3] sont-ils équivalents ? Indexation ========== Indices ------- Les tenseurs PyTorch peuvent être indexés : * Vecteur : T[i] * Matrice : T[i,j] * Volume : T[i,j,k] L'ordre de lecture des éléments d'un tenseur suit le style du langage C : l’indice situé le plus à droite correspond à l'alignement en mémoire. Ainsi, l’élément qui suit T[0,0] est T[0,1], puis T[0,2], et ainsi de suite. .. image:: tbll.png :align: center .. note:: Il est possible d'indexer en partant de la fin en utilisant des valeurs négatives. Ainsi le dernier élément a pour indice **-1**, l'avant-dernier **-2** et ainsi de suite. .. image:: array.png :align: center :scale: 70% On utilise le même ordre pour l'indiçage : **T[axe 0, axe 1, axe 2]** : * axe 0 avec 2 indices : 0 et 1 * axe 1 avec 4 indices : 0, 1, 2 et 3 * axe 2 avec 5 indices : 0, 1, 2, 3, et 4 Sous-tenseur ------------ Si le nombre d'indices est inférieur au nombre de dimensions, alors l’indexation permet d’extraire un sous-tenseur : .. code-block:: python T = torch.tensor([[1,2,3],[4,5,6]]) print(T[0]) # tensor([1,2,3]) print(T[1]) # tensor([4,5,6]) print(T[-2]) # tensor([1,2,3]) .. warning:: Rappelez-vous que l’indexation PyTorch s’effectue avec des virgules : T[0,1,2]. Il est possible d’écrire T[0][1][2], mais cela effectue plusieurs extractions successives, ce qui est moins efficace. Changement de dimensions ------------------------ Pour changer les dimensions d'un tenseur, les nouvelles dimensions doivent contenir le même nombre d'éléments. Avec *T1 = torch.tensor([[1,2,3],[4,5,6]])* : .. list-table:: :widths: 35 65 * - T = T1.reshape(3,2) - Renvoie un nouveau tenseur avec les dimensions demandées Par conséquent, les éléments d’un tenseur réorganisé conservent le même ordre en mémoire que le tenseur original. .. image:: tbl.png :align: center Un mot sur les GPU ================== Transfert vers le GPU --------------------- On peut transférer un tenseur entre la mémoire GPU et la mémoire du CPU : .. list-table:: * - tgpu = tcpu.to("cuda") * - tcpu = tgpu.to("cpu") Dans les exemples que vous croiserez, vous trouverez cette ligne pour détecter si un gpu est présent et ainsi envoyer les tenseurs vers la mémoire du GPU s'il existe : .. code-block:: python import torch target_device = torch.device("cuda" if torch.cuda.is_available() else "cpu") t = t.to(target_device) Création dans le GPU -------------------- Il est possible de créer directement un tenseur sur le GPU en utilisant le paramètre *device* : Voici un exemple : .. list-table:: * - torch.zeros( (2,2), dtype=torch.float32, device=target_device) Cette facilité vaut pour les fonctions : zeros, ones, empty, rand, randn, full, arange, linspace... Vue === Pour pouvoir travailler efficacement sur de grands tenseurs sans multiplier les copies en mémoire, PyTorch utilise le mécanisme des vues. Ainsi, certaines opérations essayent en priorité de retourner une **vue**, c'est à dire, un tenseur qui ne possède pas ses propres données mais qui les partage avec le tenseur d'origine. C'est par exemple la stratégie de la fonction *reshape*. Lorsqu'une vue est retournée, toute modification de la vue modifie également le tenseur original : .. code-block:: python x = torch.arange(4) # tensor([0, 1, 2, 3]) y = x.reshape(2,2) # tensor([[0, 1],[2, 3]]) y[0,0] = 9 print(x) # tensor([9, 1, 2, 3]) print(y) # tensor([[9, 1],[2, 3]]) Il faut connaître ce mécanisme pour éviter des effets inattendus. Si vous souhaitez créer une copie indépendante, vous devez utiliser la fonction **clone()** : .. code-block:: python y = x.clone() Les vues sont particulièrement importantes en deep learning, car elles permettent d’optimiser l’utilisation de la mémoire, notamment sur GPU. .. quiz:: pytorch_views :title: Vue des tenseurs PyTorch :quiz:`{"type":"TF","answer":"T"}` La fonction reshape peut retourner une vue. :quiz:`{"type":"TF","answer":"F"}` La fonction reshape crée toujours une copie. :quiz:`{"type":"TF","answer":"T"}` Les vues permettent d'optimiser l'utilisation de la mémoire. :quiz:`{"type":"TF","answer":"F"}` Le mécanisme des vues n'existe pas dans PyTorch. Le paramètre dim ================ mean ---- La fonction *mean()* des tenseurs PyTorch calcule la moyenne des valeurs du tenseur. Cependant, grâce à l'argument *dim*, on peut effectuer le calcul dans une direction donnée. Pour un tenseur *T* de dimension (2,4,3), si on écrit *T.mean(dim=2)*, PyTorch retire cette dimension du résultat en effectuant la moyenne sur cet axe : * T.mean(dim=0) => shape : (4,3) * T.mean(dim=1) => shape : (2,3) * T.mean(dim=2) => shape : (2,4) Pour un tenseur *T* de dimension (2,4,3,6,5), si on donne plusieurs dimensions : * T.mean(dim=(1,2)) => shape : (2,6,5) * T.mean(dim=(0,4)) => shape : (4,3,6) Voici une interprétation graphique : .. image:: tbl2.png :align: center Le paramètre *dim* est utilisé par de nombreuses autres fonctions comme *sum*, *max*, *min*, *std*, *var*... Il existe également le paramètre **keepdim=True**, qui permet de conserver le nombre de dimensions du tenseur original. Ainsi, pour un tenseur *T* de dimension (2,4,3,6,5), on obtient : * T.mean(dim=(0,4), keepdim=True) => shape = (1,4,3,6,1) La fonction *mean()* fonctionne uniquement sur des tenseurs de type flottant. max --- La logique reste la même, cependant la fonction *max* doublée du paramètre *dim* retourne un tuple nommé : - values : les valeurs max - indices : les indices des max .. quiz:: pytorch_dim :title: Paramètre dim .. rubric:: Question 1 .. code-block:: python T = torch.tensor([ [1,2,3,4], [5,6,7,8] ]) print(T.max(dim=1)) Quel est le résultat obtenu ? - Réponse A : [5, 6, 7, 8] - Réponse B : [4, 8] .. rubric:: Question 2 :quiz:`{"type":"FB","answer":"B"}` .. code-block:: python T = torch.tensor([ [0,1,2], [3,4,5], [6,7,8] ]) Que faut-il écrire pour obtenir : [2, 5, 8] ? - Réponse A : T.max(dim=0).values - Réponse B : T.max(dim=1).values :quiz:`{"type":"FB","answer":"B"}` .. rubric:: Question 3 .. code-block:: python T = torch.tensor([ [[11,12],[13,14]], [[21,22],[23,24]], [[31,32],[33,34]] ]) Que faut-il écrire pour obtenir [ [13,14], [23,24], [33,34] ] ? - Réponse A : T.max(dim=0).values - Réponse B : T.max(dim=1).values - Réponse C : T.max(dim=2).values :quiz:`{"type":"FB","answer":"B"}` Que faut-il écrire pour obtenir [33, 34] avec le même tenseur T ? - Réponse A : T.max(dim=(0,1)).values - Réponse B : T.max(dim=(1,2)).values - Réponse C : T.max(dim=(0,2)).values :quiz:`{"type":"FB","answer":"A"}` Plage d’indices - slicing ========================= Il est possible d'utiliser la syntaxe **start:stop** ou **start:stop:step** avec les tenseurs PyTorch. Ce mécanisme, appelé **slicing**, permet d'éviter l'utilisation de boucles et facilite la lecture du code. .. list-table:: :widths: 25 50 * - T[0:5] - Indices de 0 à 4 * - T[0:8:2] - Indices : 0, 2, 4, 6 * - T[:5] - Jusqu'à l'indice 4 compris * - T[5:] - De l'indice 5 jusqu'à la fin * - T[:] - Tous les indices Avec des tenseurs 2 dimensions : .. code-block:: python A = torch.tensor([ [10,11,12,13], [20,21,22,23], [30,31,32,33], [40,41,42,43] ]) A[1:3,1:3] # sélection des A[i,j] avec 1≤i<3 et 1≤j<3 >> tensor([[21, 22], [31, 32]]) A[1:3,1:3] = 99 # affectation des indices sélectionnés >> tensor([[10, 11, 12, 13], [20, 99, 99, 23], [30, 99, 99, 33], [40, 41, 42, 43]]) Ou encore : .. code-block:: python A = torch.tensor([ [1,2,3,4], [6,7,8,9] ]) A[:,:2] # toutes les lignes, tous les indices < 2 >> tensor([[1, 2], [6, 7]]) Ou encore : .. code-block:: python A = torch.tensor([ [10,11,12,13,14,15], [20,21,22,23,24,25], [30,31,32,33,34,35], [40,41,42,43,44,45] ]) A[:, 0:6:2] = 99 # affectation des colonnes 0, 2, 4 >> tensor([[99, 11, 99, 13, 99, 15], [99, 21, 99, 23, 99, 25], [99, 31, 99, 33, 99, 35], [99, 41, 99, 43, 99, 45]]) Ou encore : .. code-block:: python A = torch.tensor([ [1,2,3,4], [6,7,8,9] ]) A[1] # 2ème ligne A[1,:] # idem >> tensor([6, 7, 8, 9]) A[0,2:] # 1ère ligne, indices >= 2 >> tensor([3, 4]) A[1,:2] # 2ème ligne, indices < 2 >> tensor([6, 7]) Indexage avancé =============== Par listes d'index ------------------ Cette technique se déclenche lorsque l'indexation se fait à partir de listes (ou tenseurs d'indices). Elle permet de sélectionner les éléments :math:`(x_i,y_i)` d'un tenseur en transmettant les deux listes :math:`(x_i)` et :math:`(y_i)` : .. code-block:: python A = torch.tensor([ [10,11,12,13,14,15], [20,21,22,23,24,25], [30,31,32,33,34,35], [40,41,42,43,44,45] ]) A[[0,0,3,3],[0,5,0,5]] # extrait les valeurs aux positions [0,0], [0,5], [3,0] et [3,5] >> tensor([10, 15, 40, 45]) A[[0,0,3,3],[0,5,0,5]] = 99 # affecte les valeurs aux positions [0,0], [0,5], [3,0] et [3,5] >> tensor([[99, 11, 12, 13, 14, 99], [20, 21, 22, 23, 24, 25], [30, 31, 32, 33, 34, 35], [99, 41, 42, 43, 44, 99]]) On peut coupler l'indexage avancé avec d'autres : .. code-block:: python A = torch.tensor([ [10,11,12,13,14,15], [20,21,22,23,24,25], [30,31,32,33,34,35], [40,41,42,43,44,45] ]) A[[1,2],:] # extrait la 2ème et la 3ème ligne du tenseur >> tensor([[20, 21, 22, 23, 24, 25], [30, 31, 32, 33, 34, 35]]) A[:,[1,2]] # extrait la 2ème et la 3ème colonne du tenseur >> tensor([[11, 12], [21, 22], [31, 32], [41, 42]]) Par booléens ------------ Un tenseur *A* peut être indexé à l’aide d'un masque correspondant à un tenseur de booléens. Le masque doit être de même dimension que le tenseur *A*, il sert ainsi à indiquer quels éléments sélectionner (True) et lesquels ignorer (False). La syntaxe **A[mask]** extrait les éléments sélectionnés vers un tenseur vecteur 1D : .. code-block:: python A = torch.arange(12).reshape(3,4) mask = A > 5 vector = A[mask] ----- A ----- tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) ----- mask ----- tensor([[False, False, False, False], [False, False, True, True], [ True, True, True, True]]) ----- vector ----- tensor([ 6, 7, 8, 9, 10, 11]) La syntaxe **A[mask] = ...** applique l'affectation uniquement sur les éléments sélectionnés. On peut par exemple écrire : .. code-block:: python A[A>4] = 0 ----- A ----- tensor([[0, 1, 2, 3], [4, 0, 0, 0], [0, 0, 0, 0]] .. quiz:: pytorch_advanced_indexing :title: Utilisation des tenseurs PyTorch .. rubric:: Question 1 .. code-block:: python A = torch.tensor([ [0,1],[2,3],[4,5] ]) Ecrivez sous la forme **A[x,y]** l'indexation de A retournant la valeur 2 : :quiz:`{"type":"FB","answer":"A[1,0]"}` .. rubric:: Question 2 .. code-block:: python A = torch.tensor( [ [ [0,1], [2,3], [4,5] ] ] ) Donnez l'indexation de A retournant la valeur 5 : :quiz:`{"type":"FB","answer":"A[0,2,1]"}` .. rubric:: Question 3 .. code-block:: python A = torch.tensor([[0,1,2],[4,5,6],[7,8,9]]) print(A[[0,2,0,2],[0,0,2,2]]) Donnez les chiffres affichés séparés par des espaces : :quiz:`{"type":"FB","answer":"0 7 2 9"}` .. rubric:: Question 4 Pour un tenseur 1D, si A[-3] désigne la même cellule que A[3], quelle est la taille de ce tenseur ? :quiz:`{"type":"FB","answer":"6"}` Broadcasting ============ Introduction ------------ Les tenseurs PyTorch permettent des opérations qui seraient interdites dans l’algèbre matricielle classique, comme additionner un vecteur ligne à un vecteur colonne. Ce mécanisme s'appelle le **broadcasting**. .. image:: vecligne.png :align: center :scale: 80% Si vous ajoutez un tenseur de taille :math:`(4,1)` avec un tenseur de taille :math:`(1,3)`, tout se comporte comme si chaque tenseur était étendu automatiquement pour obtenir deux tenseurs de taille :math:`(4,3)`. Le résultat est alors un tenseur de taille :math:`(4,3)`. Exemple : .. code-block:: python import torch A = torch.tensor([[0],[10],[20],[30]]) B = torch.tensor([0,1,2]) C = A + B print(C) >> tensor([[ 0, 1, 2], [10, 11, 12], [20, 21, 22], [30, 31, 32]]) Règle du broadcasting --------------------- Notons :math:`(a_0, \ldots, a_n)` et :math:`(b_0, \ldots, b_n)` les dimensions des deux tenseurs. Les dimensions sont compatibles si, pour chaque dimension :math:`i` : .. math:: a_i = b_i \quad \text{ou} \quad a_i = 1 \quad \text{ou} \quad b_i = 1 La taille du tenseur résultat est donnée par : .. math:: \boxed{s_i = \max(a_i,b_i)} .. quiz:: pytorch_broadcast1 :title: Compatibilité des dimensions .. csv-table:: :header: "Taille des tenseurs", "Résultat" :delim: ! (3,4) et (3,2) ! :quiz:`{"type":"TF","answer":"F"}` (3,4) et (1,4) ! :quiz:`{"type":"TF","answer":"T"}` (1,5) et (3,1) ! :quiz:`{"type":"TF","answer":"T"}` Cas général ----------- Si les deux tenseurs n'ont pas le même nombre de dimensions, PyTorch ajoute automatiquement des dimensions de taille 1 à gauche du plus petit tenseur. .. image:: ext.png :align: center :scale: 80% Exemple : .. image:: schema.png :align: center :scale: 80% Le tenseur entouré en bleu est un tenseur de dimensions :math:`(2,2)`, le vecteur colonne sur la gauche a pour dimensions :math:`(3,1,1)`. Après broadcasting, le résultat est un tenseur de taille :math:`(3,2,2)`. Exemple : Un tenseur de dimension :math:`(2,2)` combiné avec un tenseur de dimension :math:`(3,1,1)` produit un tenseur de dimension : .. math:: (3,2,2) Exercice ======== .. quiz:: pytorch_broadcast2 :title: Dimensions du tenseur résultat .. csv-table:: :header: :math:`a`, :math:`b`, :math:`s` :delim: ! :math:`(3,4)` ! :math:`(1)` ! :math:`(3,4)` :math:`(3,4)` ! :math:`(1,1)` ! :quiz:`{"type":"FB","answer":"(3,4)"}` :math:`(3,1)` ! :math:`(4)` ! :quiz:`{"type":"FB","answer":"(3,4)"}` :math:`(3,1)` ! :math:`(1,4)` ! :quiz:`{"type":"FB","answer":"(3,4)"}` :math:`(1,1,4)` ! :math:`(1,3,1)` ! :quiz:`{"type":"FB","answer":"(1,3,4)"}` :math:`(1,1,4)` ! :math:`(3,2,1)` ! :quiz:`{"type":"FB","answer":"(3,2,4)"}` .. quiz:: pytorch_broadcast3 :title: Dimensions des tenseurs .. csv-table:: :header: Tenseur, Dimension :delim: ! :math:`[ 1, 2 ]` ! :math:`(2)` :math:`[ [1, 2] ]` ! :quiz:`{"type":"FB","answer":"(1,2)"}` :math:`[ [1, 2], [3, 4], [5, 6] ]` ! :quiz:`{"type":"FB","answer":"(3,2)"}` :math:`[ [[1]] ]` ! :quiz:`{"type":"FB","answer":"(1,1,1)"}` :math:`[ [1], [1], [1]]` ! :quiz:`{"type":"FB","answer":"(3,1)"}` :math:`[ [[1]], [[1]], [[1]] ]` ! :quiz:`{"type":"FB","answer":"(3,1,1)"}` .. quiz:: pytorch_broadcast4 :title: Résultat du broadcasting .. code-block:: python A = torch.tensor([ [1, 2, 3], [4, 5, 6] ]) B = torch.tensor([ [0], [1] ]) print(A+B) Donnez le résultat obtenu : * A : [ [1], [2], [3], [5], [6], [7] ] * B : [ [1, 2, 3], [4, 5, 6], [2, 3, 4], [5, 6, 7] ] * C : [ [1, 2, 3], [5, 6, 7] ] * D : [ [ [1, 2, 3], [4, 5, 6] ], [ [2, 3, 4], [5, 6, 7] ] ] Réponse : :quiz:`{"type":"FB","answer":"C"}` .. warning:: Le broadcasting est très puissant, car il évite la duplication réelle des données et optimise les performances. Cependant, il peut masquer certaines erreurs si les dimensions ne sont pas celles attendues. Il est donc essentiel de toujours vérifier la propriété ``shape`` des tenseurs. Pour finir ========== Quelques fonctions utiles que vous rencontrerez fréquemment avec les tenseurs PyTorch : * *T.view(-1)* ou *T.reshape(-1)* : retourne un tenseur 1D contenant tous les éléments de T (aplatissement, vue si possible) * *T.flatten()* : retourne un tenseur 1D contenant tous les éléments * *T.transpose(dim0, dim1)* : retourne le tenseur transposé selon deux axes * *T.argmin()* : retourne l’indice de la plus petite valeur * *T.swapdims(dim0, dim1)* : échange deux axes du tenseur (sans copie) * *T.unsqueeze(dim)* : ajoute une dimension de taille 1 * *T.squeeze()* : retire les dimensions de taille 1 * *torch.cat(...)* : concatène plusieurs tenseurs selon un axe existant * *torch.stack(...)* : empile plusieurs tenseurs en créant un nouvel axe TD1 PyTorch Tensor ================== `Le source du Notebook TD1 `_ Vous devez effectuer ce TD et le faire valider par votre responsable de salle. TD2 Slicing et images avec PyTorch ================================== `Le source du Notebook TD2 `_ Vous devez effectuer ce TD et le faire valider par votre responsable de salle.