Retour au catalogue
Season 55 17 Épisodes 59 min 2026

PyTorch Fundamentals

v2.11 — Édition 2026. Un cours audio complet sur la création de modèles de deep learning avec PyTorch version 2.11. Couvre les Tensors, Autograd, Neural Networks, Optimizers, DataLoaders et le compilateur PyTorch.

Frameworks AI/ML Fondamentaux de Python Science des données
PyTorch Fundamentals
Lecture en cours
Click play to start
0:00
0:00
1
L'identité fondamentale de PyTorch
Découvrez l'objectif fondamental de PyTorch et ce qui le distingue des bibliothèques mathématiques traditionnelles. Cet épisode explique le rôle des Tensors, d'Autograd et de l'accélération GPU dans le deep learning moderne.
3m 30s
2
Comprendre les Tensors PyTorch
Plongez dans les Tensors, la structure de données fondamentale de PyTorch. Découvrez comment ils font le lien entre les données brutes et les Neural Networks, et partagent la mémoire de manière transparente avec les tableaux Numpy.
3m 47s
3
Opérations sur les Tensors et mémoire
Apprenez à manipuler les Tensors efficacement. Cet épisode couvre les opérations arithmétiques, la concaténation, les transferts d'appareils et les implications sur la mémoire des opérations in-place.
3m 24s
4
La magie d'Autograd
Découvrez le moteur qui rend le deep learning possible dans PyTorch. Apprenez comment Autograd suit dynamiquement les opérations et calcule automatiquement les dérivées complexes.
3m 33s
5
Contrôler le suivi des gradients
Découvrez comment désactiver le suivi des gradients de PyTorch pour économiser de la mémoire et accélérer les calculs. Essentiel pour exécuter l'inférence et geler les paramètres du modèle.
3m 36s
6
Datasets et gestion des données
Apprenez à séparer le traitement de vos données de l'architecture de votre modèle en utilisant la classe Dataset de PyTorch. Nous explorons le chargement paresseux (lazy loading) et les structures de Datasets personnalisés.
3m 11s
7
DataLoaders et batching
Libérez toute la vitesse de votre matériel en enveloppant vos Datasets dans des DataLoaders. Apprenez à regrouper en lots, mélanger et traiter en multiprocessus vos flux de données.
3m 22s
8
Transformations de données
Découvrez comment prétraiter les données brutes à la volée avant qu'elles n'atteignent votre Neural Network. Nous couvrons les Transforms de torchvision comme ToTensor et les fonctions Lambda personnalisées.
3m 45s
9
Concevoir des réseaux avec nn.Module
Explorez le plan structurel de chaque Neural Network PyTorch. Apprenez à créer une sous-classe de nn.Module, à définir les couches lors de l'initialisation et à acheminer les données dans la forward pass.
3m 59s
10
Couches Linear et activations
Regardez à l'intérieur du Neural Network. Nous analysons le module nn.Linear et expliquons pourquoi les fonctions d'activation non linéaires comme ReLU sont mathématiquement essentielles.
4m 23s
11
Le conteneur nn.Sequential
Simplifiez votre code PyTorch en utilisant le conteneur nn.Sequential. Apprenez à assembler proprement les couches et à inspecter les paramètres de votre modèle.
3m 21s
12
Comprendre les Loss Functions
Avant qu'une IA puisse apprendre, elle doit mesurer ses erreurs. Nous explorons les Loss Functions de PyTorch, en comparant CrossEntropyLoss pour la classification et MSELoss pour la régression.
3m 08s
13
Optimizers et descente de gradient
Découvrez comment l'Optimizer met à jour les poids du modèle pour réduire l'erreur. Apprenez la danse cruciale en trois étapes : zero_grad(), backward() et step().
3m 27s
15
Validation et inférence
Évaluez votre modèle objectivement. Apprenez à passer votre réseau en mode évaluation, à geler les gradients et à extraire des prédictions précises sur des données inédites.
3m 09s
16
Sauvegarder et charger des modèles
Ne perdez pas vos progrès durement acquis ! Nous discutons des moyens les plus sûrs de sérialiser les poids de votre modèle en utilisant state_dict et de les recharger en toute sécurité.
3m 14s
17
Booster la vitesse avec torch.compile
Débloquez la fonctionnalité phare de PyTorch 2.0. Apprenez comment le décorateur torch.compile JIT-compiles votre code Python en noyaux optimisés pour des gains de vitesse massifs.
2m 59s
18
Compilateurs et graph breaks
Plongez sous le capot du compilateur PyTorch. Nous explorons les graph breaks, le flux de contrôle dynamique et pourquoi torch.compile réussit là où les anciens systèmes ont échoué.
3m 24s

Épisodes

1

L'identité fondamentale de PyTorch

3m 30s

Découvrez l'objectif fondamental de PyTorch et ce qui le distingue des bibliothèques mathématiques traditionnelles. Cet épisode explique le rôle des Tensors, d'Autograd et de l'accélération GPU dans le deep learning moderne.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 1 sur 18. Tu écris un modèle mathématique complexe en Python, mais quand tu le scale, il sature complètement ton CPU. Tu dois faire tourner ces calculs sur du hardware parallèle et calculer en continu toutes leurs dérivées, mais tout réécrire dans un langage bas niveau prendrait des semaines. Cette tension est résolue par l'identité même de PyTorch. Quand tu regardes PyTorch pour la première fois, on dirait souvent exactement du NumPy. Tu crées des arrays, tu multiplies des matrices et tu manipules des nombres. Cette similarité visuelle crée pas mal de confusion au début. Les gens pensent que PyTorch est juste une autre library de maths standard. Ce n'est pas le cas. Alors que les libraries de maths standards sont conçues pour du calcul numérique CPU-bound, PyTorch est conçu depuis le début pour exploiter le hardware parallèle et construire des graphes de calcul dynamiques. La brique de base de ce framework, c'est le tensor. Un tensor est essentiellement un array multidimensionnel. Si tu as une grille de nombres qui représente une image, une onde sonore ou un bloc de texte, tu la stockes dans un tensor. La différence cruciale entre un array standard et un tensor PyTorch, c'est l'endroit où ces données peuvent vivre et s'exécuter. Les tensors peuvent passer sans problème de la mémoire système de ton ordi à un GPU. Prends une énorme multiplication de matrices. Tu as deux grilles contenant des millions de nombres. Si tu demandes à un CPU standard de les multiplier, il traite les maths de manière séquentielle ou par de très petits batches. Le processus galère et finit par ramer. Comme les tensors sont explicitement conçus pour l'accélération hardware, tu peux envoyer exactement les mêmes données à un GPU. Le GPU contient des milliers de petits cœurs conçus pour exécuter des opérations mathématiques en simultané. Un calcul massif qui prend des minutes sur un CPU se termine instantanément sur un GPU. PyTorch sert de pont, en traduisant ton code Python standard en instructions pour ce hardware parallèle. Un hardware rapide n'est que la moitié du prérequis pour le machine learning. Entraîner un réseau de neurones nécessite du calcul continu. Tu dois savoir exactement comment le fait de modifier une variable change ton output final, ce qui implique de calculer constamment des gradients. Faire ça manuellement pour un modèle avec des milliards de paramètres, c'est impossible. Ça nous amène au deuxième pilier de PyTorch, qui est Autograd. Autograd est un moteur de différentiation automatique. Quand tu fais des opérations mathématiques sur des tensors, PyTorch ne se contente pas de calculer le nombre final. Il construit silencieusement une carte en background. Il enregistre chaque addition, multiplication et transformation de données dans un graphe de calcul dynamique. Quand tu arrives à la fin de ton calcul, tu demandes simplement au framework de calculer les gradients. PyTorch parcourt ce graphe invisible à l'envers, en appliquant automatiquement la règle de la chaîne. Tu reçois les dérivées exactes pour chaque paramètre de ton modèle sans écrire le moindre code de calcul toi-même. Comme ce graphe est construit dynamiquement à la volée, il s'adapte à ton code. Si une loop Python standard ou un if-statement modifie le flux de tes données, le graphe s'ajuste immédiatement. La vraie puissance de PyTorch, ce n'est pas juste qu'il s'exécute rapidement ou qu'il fait du calcul. Il te donne la vitesse d'exécution d'un supercalculateur et la rigueur mathématique d'un moteur de calcul automatisé, le tout entièrement caché derrière du Python ordinaire et lisible. Si tu veux aider à ce que ces épisodes continuent, tu peux chercher DevStoriesEU sur Patreon et soutenir l'émission. C'est tout pour celui-ci. Merci d'avoir écouté, et continue à développer !
2

Comprendre les Tensors PyTorch

3m 47s

Plongez dans les Tensors, la structure de données fondamentale de PyTorch. Découvrez comment ils font le lien entre les données brutes et les Neural Networks, et partagent la mémoire de manière transparente avec les tableaux Numpy.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 2 sur 18. Chaque image, onde sonore et document texte que tu fournis à un réseau de neurones finit par se transformer en la même data structure. Si ce n'est qu'une simple grille de nombres, tu te demandes peut-être pourquoi on a besoin d'un objet spécialisé au lieu d'utiliser des arrays de programmation standards. La réponse se trouve dans la compréhension des tensors PyTorch. Un tensor est une data structure spécialisée qui ressemble beaucoup à un array ou une matrice, et qui se comporte de la même manière. Dans PyTorch, les tensors sont la monnaie d'échange universelle. Ils contiennent tes inputs bruts, les outputs générés par ton modèle, et les paramètres internes du réseau de neurones lui-même. On suppose souvent que les tensors sont complètement identiques aux arrays NumPy. Ils se ressemblent effectivement, et partagent beaucoup des mêmes comportements. La différence cruciale, c'est ce que les tensors débloquent. Alors qu'un array standard réside dans ta mémoire système principale et tourne sur ton processeur central, un tensor est conçu pour être facilement transféré vers un Graphics Processing Unit, ou GPU, pour une accélération hardware massive. Les tensors intègrent aussi la plomberie nécessaire pour le gradient tracking, ce qui permet aux réseaux de neurones d'apprendre. Tu peux initialiser un tensor de plusieurs manières. La voie la plus directe, c'est de passer des données brutes, comme une liste Python standard de nombres, directement dans le constructeur du tensor. Tu peux aussi créer un nouveau tensor à partir d'un tensor existant. Quand tu fais ça, le nouveau tensor hérite automatiquement des propriétés de l'original, ce qui veut dire qu'il aura les mêmes dimensions et le même data type, sauf si tu les overrides explicitement. Alternativement, si tu as juste besoin d'un placeholder, tu peux définir une shape, qui est une simple collection de nombres représentant les dimensions que tu veux, et demander à PyTorch de générer un tensor rempli de nombres aléatoires, de 1 ou de 0, en fonction de cette shape. Une fois que tu as un tensor, tu vas souvent vérifier trois attributs principaux. Le premier, c'est la shape, qui t'indique la taille exacte du tensor sur chaque dimension. Le deuxième, c'est le data type, qui indique le genre de nombres stockés à l'intérieur, comme des floats 32 bits ou des integers. Le troisième, c'est l'attribut device. Ça t'indique où le tensor réside physiquement en ce moment, que ce soit sur le CPU ou sur un GPU spécifique. Tu dois garder un œil là-dessus, car PyTorch exige que les tensors soient sur le même device avant de pouvoir interagir. Les tensors et les arrays standards doivent souvent travailler ensemble, ce qui nous amène au bridge NumPy. Les tensors qui résident sur le CPU peuvent en fait partager leur mémoire sous-jacente avec un array NumPy. Disons que tu charges une photo haute résolution en utilisant une library Python standard de traitement d'image. Cette image se charge dans ta mémoire système comme un array NumPy standard. Tu peux passer cet array dans PyTorch en utilisant une fonction dédiée qui crée un tensor à partir de NumPy. PyTorch ne duplique pas les données des pixels sous-jacents dans un nouveau bloc de mémoire. Il wrap simplement sa propre interface de tensor autour de l'adresse mémoire existante. Changer une valeur dans le tensor change immédiatement la valeur dans l'array NumPy, et vice versa. Cette conversion zero-copy fait gagner à la fois de la mémoire et du temps de traitement. Quand tu as fini de passer les données dans ton modèle et que tu dois renvoyer les résultats à un outil de visualisation standard, tu appelles une seule méthode sur le tensor pour l'exposer à nouveau comme un array NumPy, en utilisant exactement la même mémoire partagée. La vraie puissance d'un tensor, ce n'est pas juste de stocker une grille de nombres, mais de transporter le contexte hardware spécifique et la structure mémoire nécessaires pour pousser des données brutes à travers un réseau de neurones sans friction. Merci d'avoir écouté, happy coding tout le monde !
3

Opérations sur les Tensors et mémoire

3m 24s

Apprenez à manipuler les Tensors efficacement. Cet épisode couvre les opérations arithmétiques, la concaténation, les transferts d'appareils et les implications sur la mémoire des opérations in-place.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 3 sur 18. Un simple underscore dans ton code peut te faire économiser des gigaoctets de mémoire, mais il peut aussi casser silencieusement tout ton réseau de neurones. Savoir quand utiliser cet underscore revient à comprendre les opérations sur les tenseurs et la mémoire. Imagine un scénario pratique. Tu as trois feature vectors séparés qui représentent des données de texte, d'audio et d'image. Tu veux les combiner et les multiplier avec une matrice de poids. Par défaut, PyTorch crée les tenseurs sur le CPU. Mais pour les gros calculs matriciels, tu veux utiliser un accélérateur matériel. Tu peux vérifier si un GPU est dispo en utilisant les vérifications intégrées du framework. Si c'est le cas, tu déplaces tes tenseurs en appelant la méthode to dessus. Tu passes le nom du device cible, comme la string cuda, à cette méthode. PyTorch copie ensuite le tenseur de la RAM système vers la mémoire dédiée de ta carte graphique. Avec tes tenseurs sur le bon hardware, tu dois combiner les trois feature vectors séparés en un seul. Tu fais ça en utilisant la fonction concatenate, souvent écrite cat. Tu lui passes une liste de tes tenseurs et tu spécifies une dimension. Si tu les combines le long de la dimension des colonnes, tes trois tenseurs étroits sont joints côte à côte pour former un tenseur plus large. Tu as maintenant un input unifié qui réside dans la mémoire du GPU. PyTorch gère plus d'une centaine d'opérations différentes, mais l'arithmétique est la base. Pour traiter ton feature vector combiné, tu dois le multiplier avec une matrice de poids. Tu peux utiliser la méthode matmul, ou juste utiliser le symbole arobase comme raccourci pratique. Ça effectue une vraie multiplication matricielle mathématique, en calculant les dot products des lignes et des colonnes, et ça retourne un tout nouveau tenseur qui contient les résultats. Parfois, tu as plutôt besoin de maths element-wise. Supposons que tu veuilles appliquer un masque binaire à ton tenseur, pour forcer certaines valeurs à zéro. Pour ça, tu utilises la méthode mul, ou l'opérateur astérisque standard. Ça ne fait pas de multiplication matricielle. Ça multiplie simplement le premier élément du tenseur A par le premier élément du tenseur B, le deuxième par le deuxième, et ainsi de suite. À chaque fois que tu lances des opérations comme la multiplication matricielle ou l'addition element-wise, PyTorch alloue de la nouvelle mémoire pour le résultat. Quand tu travailles sur des millions de paramètres, ça consomme rapidement la mémoire disponible de ton hardware. C'est là que tu dois faire attention. PyTorch propose des opérations in-place pour gérer l'overhead de mémoire. N'importe quelle opération qui se termine par un underscore fonctionne in-place. Si tu utilises la méthode add standard, tu obtiens un nouveau tenseur. Si tu utilises la méthode add avec un underscore, PyTorch écrase directement les valeurs à l'intérieur du tenseur existant. L'empreinte mémoire reste exactement la même. Même si les opérations in-place sont très efficaces pour la mémoire, elles sont aussi dangereuses. Quand tu écrases un tenseur, tu effaces ses valeurs précédentes. Les réseaux de neurones s'appuient sur un historique complet des états passés pour calculer les dérivées pendant la phase d'apprentissage. Si tu écrases un tenseur en utilisant une opération in-place, tu détruis l'historique de calcul dont le système a besoin pour mettre à jour le modèle. Réserve les opérations in-place pour le formatage des données avant qu'elles n'entrent dans ton modèle, et tiens-t'en aux opérations standards pendant l'entraînement pour garder ton historique de calcul intact. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
4

La magie d'Autograd

3m 33s

Découvrez le moteur qui rend le deep learning possible dans PyTorch. Apprenez comment Autograd suit dynamiquement les opérations et calcule automatiquement les dérivées complexes.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 4 sur 18. Entraîner un réseau de neurones, ça veut dire calculer la dérivée de ton erreur par rapport à des millions de paramètres. Faire ça à la main te demanderait des pages de calculs et de tout réécrire à chaque fois que tu changes l'architecture de ton modèle. PyTorch résout ça en gardant une trace de tes calculs en background, un concept qu'on appelle la magie d'Autograd. Autograd, c'est le moteur de différentiation intégré de PyTorch. Il calcule automatiquement les gradients pour n'importe quel computational graph. Pour voir comment ça marche, imagine une transformation linéaire standard. Tu as un input tensor qui contient tes data, une weight matrix, et un bias vector. Le but, c'est de calculer un output, de le comparer à la target value réelle, et de calculer l'erreur, ou la loss. Tes input data sont fixes, donc tu n'as pas besoin de leurs dérivées. Mais les tensors de weight et de bias devront être mis à jour plus tard, ce qui veut dire que tu as absolument besoin de leurs gradients. Tu signales ça à PyTorch en passant un flag appelé requires grad à true quand tu crées ces tensors de paramètres. Ça dit au moteur autograd de commencer à les surveiller. Quand tu fais des opérations sur ces tensors surveillés, comme multiplier l'input par les weights, ajouter le bias, et calculer la loss finale, PyTorch fait deux choses en même temps. Il calcule le résultat numérique réel, et en même temps, il construit un Directed Acyclic Graph, ou DAG. Dans ce graphe, tes tensors de départ sont les feuilles, et les opérations mathématiques que tu as appliquées sont les racines. Chaque nouveau tensor créé par une opération a un attribut qui stocke une référence vers la fonction qui l'a créé. Ça dit à autograd exactement comment calculer la dérivée pour cette étape mathématique spécifique. Ce graphe n'est pas une structure statique définie au début de ton script. PyTorch construit le DAG dynamiquement de zéro à chaque itération. Quand tu lances une forward pass, un tout nouveau graphe est construit à la volée. Cette exécution dynamique veut dire que ton réseau peut changer de comportement à chaque étape. Tu peux utiliser le control flow standard de Python, comme des if statements ou des boucles, et le moteur va traquer proprement le chemin que les data ont vraiment pris pendant ce run spécifique. Une fois que ta forward pass produit le tensor de loss final, tu déclenches le calcul du gradient en appelant la méthode backward sur cette loss. Autograd parcourt immédiatement le graphe à l'envers. Il utilise la chain rule pour calculer les dérivées de la loss par rapport à chaque tensor qui a requires grad défini sur true. Il prend ensuite ces valeurs calculées et les stocke dans l'attribut grad de tes tensors de weight et de bias. Les calculs complexes sont complètement abstraits. Il y a des moments où tu veux juste faire passer des data à travers le modèle sans calculer les gradients, comme quand tu évalues un modèle entraîné. Traquer l'historique demande plus de mémoire et de calcul. Tu peux complètement empêcher autograd de construire le graphe en wrappant ton bloc de code dans le context manager torch no grad. Ça arrête temporairement le tracking et exécute les maths beaucoup plus vite. La vraie puissance d'autograd, c'est qu'il transforme du code Python arbitraire en une structure mathématique entièrement différentiable, sans que tu aies jamais à écrire manuellement les formules de dérivées. Merci de m'avoir écouté. Prenez soin de vous, tout le monde.
5

Contrôler le suivi des gradients

3m 36s

Découvrez comment désactiver le suivi des gradients de PyTorch pour économiser de la mémoire et accélérer les calculs. Essentiel pour exécuter l'inférence et geler les paramètres du modèle.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 5 sur 18. Si tu fais tourner un modèle fraîchement entraîné sur un gros batch d'images de test sans changer un paramètre spécifique, ton application finira par planter avec une erreur out-of-memory. Même si tu fais juste des prédictions, le modèle accumule secrètement de la mémoire pour retenir chaque opération mathématique qu'il effectue, jusqu'à ce que le système crashe. Contrôler le Gradient Tracking, c'est comme ça que tu évites ce problème. Par défaut, les tensors PyTorch sont conçus pour apprendre. Si un tensor a son gradient requirement défini sur true, PyTorch tracke chaque opération effectuée dessus. Il construit un computation graph en arrière-plan, reliant les inputs, les weights et les outputs pour pouvoir calculer les gradients plus tard pendant la backpropagation. Ce moteur de tracking est génial pour le training, mais il demande beaucoup d'overhead. Une fois le training terminé, tes priorités changent. Disons que tu viens de terminer le training d'un classifieur d'images et que tu dois lancer des prédictions sur un million de nouvelles images. Tu n'as plus besoin de mettre à jour les weights du modèle. Tu veux juste la forward pass. Si tu laisses la machinerie de tracking tourner, PyTorch construit un computation graph massif et inutile pour ce million d'images, ce qui va exploser ta RAM et ralentir tes cycles de compute. Pour arrêter ça, tu as deux outils principaux. Le premier est un context manager appelé torch dot no grad. Tu l'utilises pour wrapper des blocs de code entiers. Quand tu places ta forward pass dans un bloc no grad, tu dis à PyTorch de couper temporairement le moteur de tracking. Toutes les opérations effectuées à l'intérieur de ce bloc ne seront pas enregistrées. Même si les tensors en input sont normalement trackés, les outputs créés dans le bloc auront leur gradient requirement défini sur false. C'est ton outil pour lancer l'évaluation, le testing ou des prédictions en batch. Il désactive le graph pour tout ce qui se trouve dans son scope. Le deuxième outil, c'est la méthode detach. Alors que no grad gère des blocs de code, detach gère les tensors individuellement. Appeler detach sur un tensor renvoie un nouveau tensor qui partage exactement la même data sous-jacente que l'original, mais qui est complètement déconnecté du computation graph. Il n'a aucun historique. On confond souvent quand utiliser l'un ou l'autre. Utilise le context manager torch dot no grad quand tu veux couper le tracking pour une séquence d'opérations, comme quand tu passes du training à l'inference. Utilise la méthode detach quand tu construis activement un computation graph pendant le training, mais que tu as besoin de sortir un tensor spécifique de ce graph. Un use case courant pour detach, c'est quand tu dois passer un tensor à une autre librairie Python, comme NumPy, qui ne comprend pas les computation graphs de PyTorch. Tu détaches d'abord le tensor, ce qui enlève tout le bagage de tracking, et ensuite tu passes les nombres bruts. Désactiver le gradient tracking est aussi une technique de base pour freezer des paramètres. Si tu fais du fine-tuning sur un énorme modèle pré-entraîné, tu n'as probablement pas envie de tout entraîner from scratch. Tu peux boucler sur les layers de base du modèle et définir leur gradient requirement sur false. PyTorch arrête complètement de les tracker. Pendant la backward pass, ces layers freezés ne calculeront pas de gradients et ne se mettront pas à jour, ce qui économise énormément de mémoire et accélère considérablement ton processus de fine-tuning. Le gradient tracking, c'est de la machinerie industrielle lourde conçue strictement pour l'apprentissage. Dès qu'un tensor n'a pas besoin d'apprendre, coupe la machinerie pour récupérer ta mémoire et ta vitesse. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
6

Datasets et gestion des données

3m 11s

Apprenez à séparer le traitement de vos données de l'architecture de votre modèle en utilisant la classe Dataset de PyTorch. Nous explorons le chargement paresseux (lazy loading) et les structures de Datasets personnalisés.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 6 sur 18. Ton modèle est à l'image des données que tu lui donnes, mais que fais-tu quand ton dataset fait un téraoctet et que ta machine n'a que seize gigaoctets de RAM ? La réponse se trouve dans la façon dont tu récupères ces données. Cet épisode parle des Datasets et du Data Handling. La logique de traitement des données peut vite devenir un vrai bazar. Si tu mélanges ton code de lecture de fichiers, de décodage et de formatage directement dans ta training loop, ton projet devient fragile et difficile à maintenir. PyTorch t'encourage à découpler ces aspects. Tu veux que la préparation de tes données soit complètement séparée de ton algorithme d'entraînement. Pour y arriver, PyTorch fournit une primitive appelée Dataset, qui se trouve dans le module torch point utils point data. La classe Dataset agit comme un wrapper standardisé autour de tes données brutes. Pour gérer tes propres fichiers, tu crées une classe custom qui hérite de cette primitive. Quand tu construis un dataset custom, tu dois implémenter trois méthodes spécifiques. Ce sont init, len, et getitem. La méthode init s'exécute exactement une fois quand tu crées l'objet dataset. C'est là que tu configures tes répertoires et tes paths. Une erreur fréquente chez les débutants, c'est d'essayer de charger toutes les données réelles en mémoire juste ici. Ne fais surtout pas ça. Si tu as cinquante mille images haute résolution, les lire toutes en mémoire pendant l'initialisation va faire crasher ta machine immédiatement. À la place, utilise init pour charger un index léger. Par exemple, tu pourrais lire un fichier CSV qui contient les noms de fichiers des images dans une colonne et leurs labels textuels correspondants dans une autre. Tu construis juste la carte, tu ne stockes pas le territoire. Ensuite, il y a la méthode len. Elle renvoie simplement le nombre total de samples dans ton dataset. Si ton fichier CSV a cinquante mille lignes, cette méthode renvoie le nombre cinquante mille. Le système se base là-dessus pour connaître les limites absolues de tes données disponibles, pour ne pas demander un index qui n'existe pas. Le gros du travail se passe dans la méthode getitem. Cette fonction est conçue pour charger et renvoyer un seul sample à un index spécifique demandé. Quand le système a besoin du sample numéro quarante-deux, il appelle getitem et lui passe ce numéro. Ton code cherche la ligne quarante-deux dans le CSV que tu as chargé plus tôt. Il lit la string du path du fichier à partir de cette ligne. Ensuite, et seulement à ce moment-là, il accède au disque, trouve le fichier, et décode les vrais pixels de l'image en mémoire. Il récupère le label depuis cette même ligne CSV, et renvoie l'image et le label ensemble sous forme de tuple. Cette technique s'appelle le lazy loading. Tu ne consommes de la mémoire que pour la donnée spécifique dont tu as besoin, au moment exact où tu es prêt à la traiter. En isolant cette logique dans la méthode getitem, ton code d'entraînement n'a jamais besoin de savoir si les données viennent d'un disque dur local, d'un network stream, ou d'une base de données complexe. Il demande juste un index et reçoit un output standardisé. Séparer le mécanisme de comment la donnée est récupérée de comment elle est consommée, c'est la base d'un code de machine learning scalable. Si tu trouves ces épisodes utiles et que tu veux soutenir l'émission, tu peux chercher DevStoriesEU sur Patreon. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de coder !
7

DataLoaders et batching

3m 22s

Libérez toute la vitesse de votre matériel en enveloppant vos Datasets dans des DataLoaders. Apprenez à regrouper en lots, mélanger et traiter en multiprocessus vos flux de données.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 7 sur 18. Les GPU sont incroyablement rapides, mais ils resteront complètement inactifs si ton CPU n'arrive pas à leur fournir des données assez vite. Dans les training loops, le bottleneck n'est souvent pas les maths, mais le chargement du prochain set de fichiers depuis le disque. La solution, c'est de séparer la récupération des données de l'exécution du modèle en utilisant des DataLoaders et du batching. C'est facile de confondre les rôles d'un Dataset et d'un DataLoader. Un Dataset a un seul job : récupérer un item unique et son label. Il ne sait rien du processus de training global. Le DataLoader est un wrapper autour de ce Dataset. Il agit comme le manager responsable d'organiser ces items individuels en groupes, de randomiser leur ordre, et d'utiliser plusieurs process pour les charger efficacement. Pendant le training, les modèles regardent rarement un seul data point à la fois. Ils mettent à jour leurs poids internes en se basant sur un groupe d'items évalués simultanément, ce qu'on appelle un minibatch. Cette approche rend le processus de training plus stable et exploite à fond la puissance de calcul parallèle du hardware. Pour construire un minibatch manuellement, tu devrais écrire une boucle pour récupérer les samples individuels, les empiler dans une structure de tensor plus grande, et gérer les edge cases comme le dernier batch qui serait plus petit que les autres. Le DataLoader gère tout ça automatiquement. Tu initialises un DataLoader en lui passant ton objet Dataset et un paramètre appelé batch size. Si tu mets le batch size à 64, le DataLoader va récupérer 64 items distincts du Dataset, les consolider dans un seul tensor, et te les servir d'un coup. Dans ton code, le DataLoader se comporte comme un itérable Python standard. Tu fais une boucle dessus. À chaque fois que la boucle avance, le DataLoader yield le prochain batch complet de données et le batch de labels correspondant. Tu lui passes aussi un paramètre shuffle. Si un réseau de neurones traite les données de training exactement dans le même ordre à chaque fois, il risque de mémoriser cette séquence spécifique au lieu d'apprendre les vraies features. Mettre shuffle à true indique au DataLoader de randomiser l'ordre des éléments du dataset au début de chaque epoch. Une fois que le DataLoader a yield chaque batch et que le dataset est épuisé, la boucle se termine. La prochaine fois que tu itères sur le DataLoader, il génère une toute nouvelle séquence randomisée. C'est la partie qui compte. Le DataLoader accepte aussi un paramètre pour le nombre de worker processes. Quand tu utilises plusieurs workers, le DataLoader lance des process CPU en background pour récupérer les données. Imagine que tu donnes ces 64 images à un réseau de neurones. Pendant que ton GPU est occupé à calculer les gradients pour le batch actuel, les workers CPU en background sont simultanément en train de lire, décoder et empiler les 64 images suivantes. Le temps que le GPU termine son étape mathématique actuelle, le prochain batch de données attend déjà en mémoire. Le GPU ne tourne jamais à vide. Une training loop haute performance isole la réalité lente et imprévisible des opérations disque, de la réalité rapide et structurée du training de modèle. Le DataLoader fournit cette isolation, en transformant une collection de fichiers indépendants en un pipeline continu et parallélisé de minibatches. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
8

Transformations de données

3m 45s

Découvrez comment prétraiter les données brutes à la volée avant qu'elles n'atteignent votre Neural Network. Nous couvrons les Transforms de torchvision comme ToTensor et les fonctions Lambda personnalisées.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 8 sur 18. Les réseaux de neurones ne calculent que des nombres, mais tes données réelles sont souvent une collection chaotique de fichiers images bruts et de catégories textuelles. Si tu écris des boucles manuelles pour convertir chaque image et chaque label avant de commencer le training, ton code va vite devenir un bazar fragile et illisible. Les Data Transformations sont le mécanisme qui résout ça, en convertissant automatiquement tes données brutes dans un format prêt pour le modèle, exactement au moment où tu en as besoin. Les données arrivent rarement prêtes pour le machine learning. Tu dois les manipuler pour les mettre dans un format tensor spécifique avant de les passer à ton réseau. PyTorch gère ça proprement en appliquant des transforms à la volée pendant le processus de data loading. Quand tu initialises un dataset, surtout dans des librairies comme torchvision, tu définis ces modifications avec deux arguments spécifiques. Tu utilises l'argument transform exclusivement pour tes input features, comme tes images brutes. Tu utilises l'argument target transform exclusivement pour tes labels. C'est crucial de bien séparer les deux, car ils opèrent indépendamment sur différentes moitiés de tes données. Regardons d'abord les input features. Disons que tu as un dataset d'images PIL brutes. Un réseau de neurones ne peut pas lire un objet image PIL directement. Pour corriger ça, tu passes un transform torchvision natif appelé ToTensor dans l'argument transform. Quand le dataset charge une image, ToTensor exécute automatiquement deux étapes. D'abord, il convertit l'image PIL en un float tensor PyTorch. Ensuite, il met à l'échelle les valeurs d'intensité des pixels. Les pixels d'une image brute vont généralement de zéro à deux cent cinquante-cinq. L'opération ToTensor normalise ces valeurs vers une plage en virgule flottante entre zéro et un. Le dataset applique cette opération strictement au moment où chaque image est récupérée. Ça couvre les inputs, mais qu'en est-il des outputs ? Les labels de ton dataset peuvent être de simples entiers qui représentent différentes catégories. Par exemple, le chiffre trois pourrait signifier un chien. Mais pour calculer la loss pendant le training, ton modèle a souvent besoin que ces labels soient des vecteurs one-hot encoded, plutôt que de simples entiers. Ça veut dire que tu as besoin d'un array où toutes les valeurs sont à zéro, sauf pour l'index qui représente la bonne classe, qui est mis à un. Pour gérer une logique custom comme ça, PyTorch fournit les Lambda transforms. Un Lambda transform englobe n'importe quelle fonction définie par l'utilisateur pour qu'elle puisse être appliquée pendant le data loading. Tu écris une petite fonction qui prend ton label entier en input. À l'intérieur de cette fonction, tu crées un tensor de zéros qui correspond au nombre total de catégories dans ton dataset. Ensuite, tu utilises une opération interne de PyTorch pour appliquer un scatter avec une valeur de un à l'index spécifique qui correspond à ton label entier. Tu passes cette fonction custom dans un Lambda transform, et ensuite tu l'assignes à l'argument target transform de ton dataset. Ça crée un pipeline super efficace. Un worker thread récupère un seul enregistrement brut depuis ton disque. L'image arrive dans l'argument transform, passe par ToTensor, et ressort comme un float tensor normalisé. Simultanément, la catégorie entière arrive dans l'argument target transform, exécute ta fonction Lambda custom, et se transforme en un vecteur one-hot encoded. Les deux éléments sont maintenant formatés mathématiquement et passés directement à ton modèle. La vraie puissance de cette architecture, c'est la separation of concerns. En attachant ces data transformations directement à la définition du dataset, ta training loop reste complètement aveugle à la réalité chaotique de tes fichiers bruts. C'est tout pour cet épisode. Merci d'avoir écouté, et keep building !
9

Concevoir des réseaux avec nn.Module

3m 59s

Explorez le plan structurel de chaque Neural Network PyTorch. Apprenez à créer une sous-classe de nn.Module, à définir les couches lors de l'initialisation et à acheminer les données dans la forward pass.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 9 sur 18. Chaque réseau de neurones dans PyTorch, du simple classificateur d'images au modèle de langage massif, partage exactement la même architecture de base. Si tu ne comprends pas comment cette architecture organise les données et la logique, tu vas finir par te battre avec le framework à chaque étape. Concevoir des réseaux avec nn.Module, c'est comme ça que tu maîtrises cette structure. nn.Module est la classe de base pour tous les composants de réseaux de neurones dans PyTorch. Elle agit comme un conteneur universel. Quand tu construis un custom model, tu crées une classe qui hérite de nn.Module. Cet héritage donne automatiquement à ta classe la capacité de tracker ses propres paramètres, de calculer les gradients, et de s'intégrer de façon fluide avec le reste de l'écosystème PyTorch. Ça permet aussi des architectures imbriquées. Tu peux placer des modules à l'intérieur d'autres modules, ce qui crée un arbre de layers que le module parent tracke et gère comme une seule unité. Imagine que tu mettes en place le squelette vide d'un tout nouveau classificateur d'images. Construire ce squelette nécessite de définir deux méthodes spécifiques : la méthode initialize, et la méthode forward. PyTorch impose une stricte séparation des responsabilités entre ces deux étapes. D'abord, il y a la méthode initialize. Vois ça comme ton inventaire. Quand la classe est instanciée, cette méthode s'exécute exactement une fois. Tu l'utilises pour déclarer tous les layers individuels et les opérations mathématiques dont ton modèle aura besoin par la suite. Tu ne traites aucune donnée réelle ici. Tu prends simplement des composants structurels sur l'étagère, tu configures leurs input et output shapes, et tu les sauvegardes comme variables internes dans ta classe. Ensuite, il y a la méthode forward. C'est ta chaîne de montage active. La méthode forward prend un input tensor et dicte exactement comment il voyage à travers l'inventaire que tu viens de déclarer. Tu écris la séquence d'opérations étape par étape. Tu prends le tensor de l'image en input, tu le passes à une opération de flattening, tu envoies ce résultat dans une série de dense layers, et enfin tu retournes les prédictions en output. Chaque custom model doit définir cette méthode forward pour établir le data flow. Ça nous amène à un piège courant. Parce que tu as explicitement écrit la logique du data flow dans une méthode nommée forward, l'instinct naturel c'est de passer tes données en appelant model dot forward. Ne fais pas ça. Tu dois appeler le modèle directement comme si c'était une fonction classique, en passant ton input directement à l'objet modèle instancié. En interne, exécuter l'objet modèle directement déclenche plusieurs hooks d'arrière-plan critiques dont PyTorch a besoin pour gérer l'état du réseau. Appeler la méthode forward directement bypasse ces hooks et va causer un comportement inattendu pendant ta training loop. Une fois que ta classe est définie et que tu as créé un objet modèle, tu as un réseau fonctionnel. Cependant, par défaut, PyTorch crée cet objet et tous ses weights internes dans la mémoire CPU de ton système. Pour t'entraîner à des vitesses réalistes, tu dois envoyer cette architecture vers un accélérateur. Tu accomplis ça en vérifiant si un GPU CUDA ou une puce spécialisée comme le MPS d'Apple est disponible, et en assignant cette cible matérielle à une variable device. Ensuite, tu appelles la méthode to sur ton modèle, en lui passant cette variable device. Cette simple commande déplace immédiatement tous les paramètres initialisés du modèle hors de la mémoire standard pour les mettre dans la mémoire haute vitesse de ton accélérateur matériel. La caractéristique principale de nn.Module, c'est la façon dont il force une frontière architecturale propre entre les composants statiques que ton modèle possède en mémoire, et le chemin dynamique que tes données empruntent pour être traitées. Merci d'avoir écouté. Prenez soin de vous, tout le monde.
10

Couches Linear et activations

4m 23s

Regardez à l'intérieur du Neural Network. Nous analysons le module nn.Linear et expliquons pourquoi les fonctions d'activation non linéaires comme ReLU sont mathématiquement essentielles.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 10 sur 18. Si tu empiles une centaine de layers de neural network ensemble sans une astuce mathématique spécifique, tout le network s'effondre mathématiquement en une seule ligne droite. Le coupable, c'est l'algèbre linéaire, et la solution demande de comprendre les linear layers et les activations. Un neural network, c'est fondamentalement une séquence d'opérations mathématiques sur des tensors. L'opération la plus courante, c'est le linear layer, défini dans PyTorch par nn.Linear. Ce module applique une transformation affine aux données entrantes. Il contient deux tensors internes qu'il apprend avec le temps : les weights et les biases. Quand la data passe à travers, le layer multiplie l'input par la matrice de weights et ajoute le bias. Prends une image standard en niveaux de gris de 28 par 28 pixels. Avant qu'un linear layer puisse traiter ça, tu dois flatten la grille en deux dimensions pour en faire un array en une dimension de 784 nombres. Tu passes cet array de 784 valeurs dans un layer nn.Linear configuré pour sortir 512 features. Sous le capot, PyTorch crée une matrice de weights qui mappe les 784 inputs vers les 512 outputs. Il multiplie tes valeurs de pixels par ces weights, fait la somme, ajoute un terme de bias pour décaler le résultat, et sort 512 nouveaux nombres. Pendant le training, PyTorch met continuellement à jour ces weights et ces biases. Ils forment la véritable mémoire de ton modèle. Tu pourrais te dire qu'un deep neural network, c'est juste une longue séquence de ces linear layers empilés les uns à la suite des autres. C'est la partie qui compte. Si tu enchaînes plusieurs opérations nn.Linear ensemble sans rien entre les deux, les maths se simplifient. La matrice A multipliée par la matrice B, c'est juste une autre matrice, C. Empiler dix linear layers a exactement la même capacité mathématique que de calculer un seul linear layer. Ton deep network est réduit à une équation linéaire plate, complètement incapable d'apprendre des patterns complexes du monde réel. Pour stopper cet effondrement mathématique, tu introduis une non-linéarité juste après le linear layer. C'est ce qu'on appelle les activation functions. L'activation la plus utilisée dans PyTorch, c'est nn.ReLU, qui signifie Rectified Linear Unit. Une fois que le linear layer a calculé ses 512 outputs, tu passes ce tensor directement dans une fonction ReLU. La logique de ReLU est brutalement simple. Elle regarde chaque nombre dans le tensor. Si un nombre est inférieur à zéro, ReLU le change en exactement zéro. Si un nombre est zéro ou positif, ReLU le laisse complètement tranquille. Cette simple cassure à zéro détruit la linéarité. Ça empêche le linear layer suivant de fusionner mathématiquement avec le précédent. En forçant les valeurs négatives à zéro, ReLU crée aussi des représentations sparse. Ça veut dire que seul un sous-ensemble spécifique de neurones s'active pour un input donné, ce qui rend le network super efficace. Le data flow est cohérent. Ton image flattened rentre dans le linear layer, se fait transformer par les weights et les biases, puis atteint l'activation ReLU où les outputs négatifs sont retirés. Tu peux ensuite passer ce tensor activé en toute sécurité dans un deuxième linear layer pour extraire des patterns plus profonds et plus abstraits. Un linear layer détermine quelle importance mathématique donner à chaque input, mais l'activation function donne au network la véritable géométrie requise pour apprendre les formes imprévisibles des données réelles. Merci d'avoir passé quelques minutes avec moi. À la prochaine, prends soin de toi.
11

Le conteneur nn.Sequential

3m 21s

Simplifiez votre code PyTorch en utilisant le conteneur nn.Sequential. Apprenez à assembler proprement les couches et à inspecter les paramètres de votre modèle.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 11 sur 18. Écrire des méthodes forward custom pour chaque réseau de neurones devient vite fastidieux quand tu te contentes d'empiler des layers standards. Tu n'as pas toujours besoin de router manuellement les données d'une fonction à l'autre. Parfois, tu as juste besoin d'assembler les layers comme des briques LEGO. C'est exactement ce que fait le container nn.Sequential. Le container nn.Sequential est un pipeline ordonné de modules de réseau de neurones. Quand tu passes des données dans ce container, elles circulent à travers les modules internes dans l'ordre exact où ils ont été ajoutés. Imagine que tu assembles un Multilayer Perceptron standard à trois layers. Normalement, tu définirais tes linear layers et tes fonctions d'activation dans une méthode d'initialisation, puis tu écrirais une méthode forward custom. Dans cette méthode forward, tu prendrais explicitement l'input, tu le passerais au layer un, tu l'envelopperais dans une activation ReLU, tu passerais ce résultat au layer deux, tu appliquerais un autre ReLU, et tu passerais ça au layer final. Avec Sequential, tu bypasses complètement la méthode forward. Tu instancies le container et tu lui passes tes modules directement en arguments. Tu fournis un module Linear, suivi d'un module ReLU, d'un deuxième module Linear, d'un autre ReLU, et d'un module Linear final. PyTorch gère automatiquement le data routing. L'output du premier module devient instantanément l'input du deuxième, et ça descend automatiquement le long de la chain. Ce container est super efficace, mais il a une grosse limitation. Il est strictement fait pour un data flow linéaire, en ligne droite. Il ne peut pas gérer les architectures complexes qui nécessitent du branching, des inputs multiples, ou des skip connections. Si tu construis un truc comme un Residual Network où les données bypassent certains layers et sont rajoutées plus tard, Sequential ne marchera pas. Pour toute topologie non linéaire, tu dois quand même écrire un module custom avec une méthode forward dédiée. Une fois que tu as enchaîné tes layers, tu as souvent besoin d'inspecter ce que tu viens de construire. Chaque layer de ton container Sequential est une subclass de nn.Module, ce qui veut dire que PyTorch enregistre et track automatiquement tout le state sous-jacent. Pour voir ce state, tu utilises la méthode named_parameters. Appeler named_parameters sur ton modèle te donne un itérateur sur tous les weights et les biais à l'intérieur. Chaque élément qu'il renvoie est une simple paire : le nom du paramètre et le tensor du paramètre lui-même. Comme tu as utilisé un container Sequential sans nommer explicitement tes layers, PyTorch génère des noms numériques basés sur leur index. Tu verras des noms comme zero dot weight pour les weights du premier linear layer, ou zero dot bias pour ses termes de biais. Le tensor qui l'accompagne contient les valeurs numériques réelles, la shape de la matrice, et indique s'il nécessite le calcul du gradient. Boucler sur named_parameters est la façon standard de vérifier ton architecture. Tu peux rapidement faire un print de la taille de chaque matrice de weights pour confirmer que tes dimensions d'input et d'output s'alignent parfaitement sur toute la chain, avant même de commencer à faire passer de vraies données dans le système. La vraie puissance du container Sequential combiné au tracking des paramètres, c'est que PyTorch absorbe toute la logistique du state management et du data routing, te laissant te concentrer entièrement sur la shape de ton réseau. C'est tout pour cet épisode. À la prochaine !
12

Comprendre les Loss Functions

3m 08s

Avant qu'une IA puisse apprendre, elle doit mesurer ses erreurs. Nous explorons les Loss Functions de PyTorch, en comparant CrossEntropyLoss pour la classification et MSELoss pour la régression.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 12 sur 18. Pour apprendre à un réseau de neurones à avoir raison, tu dois d'abord mesurer rigoureusement à quel point il se trompe. Si tu ne peux pas quantifier l'échec, ton modèle ne peut pas en tirer de leçons. C'est ce qui nous amène à comprendre les loss functions. Quand un réseau non entraîné traite des données, son output est essentiellement une supposition. Une loss function évalue cette supposition. Elle mesure le degré de différence entre le résultat produit par le modèle et la vérité absolue de la valeur cible. L'output d'une loss function est toujours un simple nombre scalaire. Tout ton processus de training sert à pousser ce nombre unique le plus près possible de zéro. Parce que différentes tâches de machine learning ont différentes définitions de ce qui est faux, PyTorch propose plusieurs loss functions. Si tu construis un modèle de régression pour prédire une valeur continue, comme la température de demain, tu mesures la distance entre ta supposition et la vraie température. Pour ça, tu utilises la Mean Square Error, qui s'appelle nn.MSELoss dans PyTorch. Mais la classification, c'est différent. Imagine que tu as un modèle qui catégorise des images de vêtements en dix catégories de mode. Le modèle regarde l'image d'un manteau et sort dix scores bruts, un pour chaque catégorie possible. Ces scores bruts, non normalisés, s'appellent des logits. La vraie réponse est juste un simple entier, qui représente la bonne classe. Tu ne peux pas juste soustraire un index de classe d'un score brut. À la place, tu as besoin d'une fonction qui pénalise le modèle s'il donne des scores faibles à la bonne classe et des scores élevés aux mauvaises classes. Pour la classification, l'outil standard c'est nn.CrossEntropyLoss. Tu initialises ta loss function, tu lui passes les dix logits bruts de ton modèle avec le bon label entier, et elle te retourne ta pénalité scalaire. C'est la partie qui compte. Il y a un énorme piège ici pour les développeurs. Dans beaucoup de livres de machine learning, un réseau de classification se termine par une layer softmax. Softmax force les logits bruts dans une belle distribution de probabilités où tous les scores s'additionnent pour faire exactement un. À cause de ça, les développeurs ajoutent souvent manuellement une opération softmax tout à la fin de leur modèle PyTorch. Si tu utilises nn.CrossEntropyLoss, faire ça est une erreur. Dans PyTorch, nn.CrossEntropyLoss applique automatiquement une fonction LogSoftmax en interne avant de calculer la negative log likelihood. Elle est conçue pour accepter directement les logits bruts, non normalisés. Si ton modèle sort des probabilités parce que tu as déjà appliqué softmax, les passer dans nn.CrossEntropyLoss signifie que tu appliques les maths deux fois. Ça compresse tes gradients, ralentit considérablement le training, et ruine la capacité de ton modèle à apprendre efficacement. La règle à retenir, c'est que ton réseau de neurones doit juste sortir des nombres bruts. Garde les outputs de ton modèle bruts, passe-les directement à nn.CrossEntropyLoss, et laisse PyTorch faire le gros du travail pour transformer ces logits en une pénalité significative. Merci d'avoir écouté, happy coding tout le monde !
13

Optimizers et descente de gradient

3m 27s

Découvrez comment l'Optimizer met à jour les poids du modèle pour réduire l'erreur. Apprenez la danse cruciale en trois étapes : zero_grad(), backward() et step().

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 13 sur 18. Le bug le plus fréquent lors du training avec PyTorch, ce n'est pas des mauvaises données ou une mauvaise architecture. C'est d'oublier d'effacer tes anciens calculs, ce qui fait que ton réseau part complètement en vrille. Aujourd'hui, on parle des optimizers et de la Gradient Descent, qui gèrent exactement comment ton modèle apprend de ses erreurs. Ton modèle fait une prédiction, et tu calcules la loss pour voir à quel point il était loin du compte. Maintenant, tu dois ajuster les weights internes du réseau de neurones pour que la prochaine prédiction soit un peu plus précise. Ce processus d'ajustement des paramètres pour minimiser la loss s'appelle l'optimization. L'optimizer, c'est l'algorithme spécifique qui gère la façon dont ces weights changent. Pour configurer un optimizer, tu dois lui donner deux choses. D'abord, tu lui passes un iterable qui contient les paramètres du modèle que tu veux ajuster. Ensuite, tu lui fournis un learning rate. Le learning rate est un hyperparameter fondamental qui contrôle l'amplitude des changements appliqués aux weights. Si le learning rate est trop petit, l'optimizer fait des pas microscopiques, ce qui rend le training horriblement lent. Si le learning rate est trop grand, l'optimizer dépasse les valeurs optimales, ce qui donne un comportement complètement imprévisible. Un algorithme standard pour ça, c'est la Stochastic Gradient Descent, ou SGD. Il évalue la pente de ta fonction de loss et fait un pas dans la direction opposée pour descendre vers l'erreur la plus basse possible. Une fois que tu as initialisé ton optimizer SGD avec tes paramètres et ton learning rate, les mises à jour se font selon une séquence stricte en trois étapes. La première étape, c'est de faire table rase. Tu appelles la commande zero grad sur l'optimizer. C'est là que se cache ce fameux bug. PyTorch accumule les gradients par défaut. Quand il calcule de nouveaux gradients, il n'écrase pas les anciens ; il ajoute simplement les nouveaux nombres aux totaux existants. Si tu sautes cette étape zero grad, les calculs de ton batch actuel sont corrompus par les restes du batch précédent. Remets toujours les gradients à zéro avant de faire quoi que ce soit d'autre. La deuxième étape, c'est le calcul des nouveaux gradients. Tu prends ta valeur de loss calculée et tu appelles la commande backward dessus. Ça déclenche la backpropagation. PyTorch parcourt l'architecture de ton réseau à l'envers. Il calcule la dérivée de la loss par rapport à chaque paramètre. En gros, il détermine exactement combien chaque weight individuel a contribué à l'erreur globale. Ces gradients calculés sont stockés directement dans les objets paramètres. La troisième étape, c'est d'appliquer la correction. Tu appelles la commande step sur l'optimizer. L'optimizer regarde les gradients stockés dans chaque paramètre pendant la backward pass. Il multiplie ces gradients par le learning rate pour trouver la taille exacte de l'ajustement, puis il met à jour les weights réels en mémoire. Ce cycle se répète pour chaque batch. Remets les gradients à zéro, calcule la loss en backward, et fais un step sur l'optimizer. Le détail critique à retenir, c'est que l'optimizer met à jour uniquement les paramètres qu'on lui a explicitement donnés lors du setup. Si tu as besoin de freeze un layer dans ton réseau, tu exclus simplement ses paramètres quand tu initialises l'optimizer, et ces weights resteront fixes en permanence. Au fait, si tu veux soutenir l'émission, tu peux chercher DevStoriesEU sur Patreon. Merci pour ton écoute. Prenez soin de vous tous.
15

Validation et inférence

3m 09s

Évaluez votre modèle objectivement. Apprenez à passer votre réseau en mode évaluation, à geler les gradients et à extraire des prédictions précises sur des données inédites.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 15 sur 18. Ton modèle marche peut-être parfaitement sur ses training data, mais le vrai test pour une IA, c'est comment elle gère l'inconnu. Si tu te fies uniquement au feedback que voit ton optimizer, tu risques juste de construire une banque de mémoire très chère. Pour voir si ton modèle généralise vraiment au monde réel, tu as besoin de la validation et de l'inference. Pendant la phase de training, tu regardes la training loss. Ce nombre est là pour guider l'optimizer interne. Il force le modèle à ajuster ses weights jusqu'à ce que l'erreur mathématique diminue. Mais une training loss très basse ne veut pas dire que tu as un bon modèle. Ça veut simplement dire que le modèle est très doué pour répondre aux questions qu'il a déjà vues. La validation accuracy est une métrique complètement à part qui dit aux humains si le modèle peut faire des prédictions correctes sur de toutes nouvelles données. Pour obtenir cette métrique, tu dois faire tourner une validation loop sur un test dataset dédié. Avant d'envoyer la moindre test data dans le réseau, tu dois changer l'état du modèle. Tu fais ça en appelant la méthode eval sur ton objet modèle. Appeler eval passe le réseau en mode évaluation. Certaines layers internes se comportent différemment pendant le training que pendant l'inference. Appeler eval les force à verrouiller leur comportement pour que tes prédictions restent cohérentes. Si tu sautes cette étape, tes résultats de test ne seront absolument pas fiables. Voilà pour l'état du modèle. Ensuite, tu dois contrôler le moteur lui-même en désactivant le gradient tracking. Tu fais ça en englobant ton code de validation dans un context manager no grad. Pendant le training, PyTorch construit constamment un computational graph en mémoire, en stockant l'historique de chaque opération pour pouvoir calculer les gradients plus tard. Dans une validation loop, tu as complètement terminé le training. Tu ne veux pas mettre à jour les weights. Le bloc no grad dit à PyTorch d'arrêter de tracker l'historique. C'est la partie qui compte. Désactiver le tracking empêche les mises à jour accidentelles de ton modèle, mais ça libère aussi une quantité massive de mémoire et ça accélère drastiquement le calcul. À l'intérieur de ce bloc no grad, la logique est simple. Tu itères sur ton test dataset par batches. Pour chaque batch, tu passes les input data dans le modèle. Le modèle calcule la forward pass et renvoie ses prédictions brutes. Si tu fais de la classification, le modèle ne sort pas un text label bien propre. À la place, il sort une liste de scores numériques pour chaque catégorie qu'il connaît. Pour savoir quelle catégorie le modèle a vraiment choisie, tu as besoin de la fonction argmax. Argmax regarde la liste des scores bruts et trouve le nombre le plus élevé. Elle renvoie ensuite la position de l'index de ce score le plus élevé. Cet index est ta prédiction de classe choisie. Une fois que tu as les prédictions du modèle, tu les compares directement aux vrais labels fournis par le test dataset. Tu comptes exactement combien de prédictions correspondent aux vrais labels. Tu gardes un total cumulé de ces bonnes correspondances sur tous les batches. Quand la loop se termine, tu divises le nombre total de prédictions correctes par le nombre total d'éléments dans le test dataset. Le résultat est ton pourcentage d'accuracy final. La training loop force ton modèle à s'adapter aux données historiques, mais les contraintes strictes de la validation loop prouvent si ce modèle est vraiment utile. Merci d'avoir écouté. Prenez soin de vous, tout le monde.
16

Sauvegarder et charger des modèles

3m 14s

Ne perdez pas vos progrès durement acquis ! Nous discutons des moyens les plus sûrs de sérialiser les poids de votre modèle en utilisant state_dict et de les recharger en toute sécurité.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 16 sur 18. Entraîner un image classifier pendant cinquante epochs peut prendre des semaines et engloutir des milliers de dollars en compute. Pourtant, dès que ton script Python a fini de s'exécuter, tous ces patterns durement acquis disparaissent complètement de la mémoire. Pour protéger cet investissement, tu as besoin d'un moyen de persister ta progression sur le disque. C'est exactement ce que permet de résoudre la sauvegarde et le chargement des modèles. À l'intérieur de chaque modèle PyTorch se trouve un dictionnaire interne appelé le state dict. Ce dictionnaire mappe chaque layer de ton réseau à ses tensors de paramètres correspondants. Il contient les weights et les biases que ton modèle a appris pendant l'entraînement. La structure du modèle n'est que du code, mais le state dict, c'est l'intelligence. Pour persister ton modèle, tu extrais ce dictionnaire et tu l'écris dans un fichier. Tu fais ça en utilisant une fonction appelée torch dot save. Tu lui passes deux choses. Premièrement, le state dict de ton modèle. Deuxièmement, le file path où tu veux le stocker, qui utilise traditionnellement une extension point pth. En une seule ligne de code, ton run d'entraînement de cinquante epochs est stocké en toute sécurité sur ton disque dur sous la forme d'un seul fichier ne contenant rien d'autre que des données de tensors brutes. Tu verras peut-être des exemples en ligne qui skippent complètement le state dict et passent juste l'objet modèle directement à torch dot save. Ne fais surtout pas ça. Sauvegarder le modèle entier repose fortement sur la sérialisation pickle de Python. Ça lie le fichier sauvegardé à la structure de répertoires et aux définitions de classes exactes qui étaient présentes quand le fichier a été créé. Si tu refactores ton code plus tard ou que tu déplaces un fichier, le modèle plantera au chargement. S'en tenir au state dict est beaucoup plus sûr et nettement plus robuste, parce que tu ne sauvegardes que les données, pas le code. Quand il est temps de deploy ton classifier pour de l'inférence en production, tu dois inverser le processus. Parce que tu n'as sauvegardé que les weights, PyTorch a besoin de savoir à quoi ressemble la structure du réseau. Tu commences par instancier une version complètement vide de ta classe de modèle. Ça te donne le shell architectural. Ensuite, tu appelles torch dot load et tu lui donnes ton file path pour relire le dictionnaire en mémoire. Quand tu appelles torch dot load, il y a une best practice moderne cruciale que tu dois suivre. Passe toujours l'argument weights only défini sur true. Les fichiers pickle Python peuvent contenir du code exécutable arbitraire. Si tu télécharges un modèle pré-entraîné sur Internet et que tu le charges à l'aveugle, il pourrait exécuter des scripts malveillants sur ta machine. Définir weights only sur true restreint le loader à ne désérialiser que des tensors PyTorch standards, ce qui garde ton système sécurisé. Enfin, avec ton modèle vide prêt et ton dictionnaire sécurisé chargé, tu appelles load state dict sur le modèle et tu lui passes le dictionnaire. PyTorch mappe les weights chargés aux layers correspondants dans le shell vide. Ton modèle est maintenant entièrement restauré et prêt à faire des prédictions. Ne confie jamais ton investissement d'entraînement à un objet sérialisé fragile ; sépare toujours l'architecture dans ton code des paramètres appris sur ton disque. Merci d'avoir passé ce moment avec moi. J'espère que tu as appris quelque chose de nouveau.
17

Booster la vitesse avec torch.compile

2m 59s

Débloquez la fonctionnalité phare de PyTorch 2.0. Apprenez comment le décorateur torch.compile JIT-compiles votre code Python en noyaux optimisés pour des gains de vitesse massifs.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 17 sur 18. Tu passes des semaines à peaufiner l'architecture d'un modèle pour un gain de vitesse de 5 %. Mais souvent, le vrai bottleneck, ce ne sont pas tes calculs. C'est Python lui-même, et les transferts de données constants et inefficaces entre ta mémoire et le GPU. Régler ça ne nécessite pas de réécrire ta codebase. Aujourd'hui, on va voir comment booster la vitesse avec torch dot compile. Introduite dans PyTorch 2.0, cette feature fait passer ton code d'une exécution standard à un workflow hautement optimisé. On pense souvent qu'accélérer PyTorch pour la production implique d'écrire des kernels C plus plus custom ou de changer fondamentalement ton architecture. Ce n'est pas le cas. Tu ne changes rien à l'intérieur de ton modèle. Tu wrap simplement ton modèle existant dans un seul appel de fonction. Pour comprendre pourquoi ça crée un bond de performance énorme, tu dois regarder comment PyTorch tourne normalement. Le PyTorch standard fonctionne en eager mode. Il exécute exactement ce que tu lui dis, exactement quand tu le demandes, une opération à la fois. Si ton code dit au GPU d'additionner deux tensors, de multiplier le résultat par un autre tensor, et d'appliquer une fonction d'activation, l'eager mode traite ça comme trois événements isolés. Pour chaque étape, le GPU lit les données depuis sa mémoire principale, fait les calculs, et réécrit le résultat intermédiaire. La bande passante de la mémoire GPU est limitée. Ce va-et-vient constant de données prend beaucoup plus de temps que les calculs eux-mêmes. Quand tu passes ton modèle dans la fonction compile, PyTorch change de tactique. Il utilise un outil interne appelé TorchDynamo pour capturer tes opérations dans un computation graph avant de les exécuter. En regardant la séquence plus large, il trouve les inefficacités. Ensuite, il utilise un backend compiler pour générer une nouvelle version, lourdement optimisée, de tes opérations. La technique principale qu'il utilise est la kernel fusion. Au lieu de lire et d'écrire en mémoire trois fois séparément, le code compilé fusionne ces étapes. Le GPU lit les données une fois, les garde dans ses registres internes les plus rapides, fait l'addition, la multiplication et l'activation à la suite, puis écrit le résultat final une seule fois. L'overhead de Python disparaît, et le bottleneck de la mémoire est contourné. L'implémentation est simple. Tu instancies ton modèle comme d'habitude. Ensuite, tu appelles torch dot compile, tu lui passes ton modèle, et tu assignes l'output à une nouvelle variable. Tu routes tes données à travers cette version compilée. Si TorchDynamo rencontre un construct Python obscur qu'il ne peut pas optimiser de façon sûre, il ne casse pas ton programme. Il laisse simplement cette petite section en eager mode standard et compile le reste. Quand tu benchmark ça, fais attention au premier run. La passe initiale prend beaucoup plus de temps car la compilation elle-même se produit exactement quand les premières données arrivent. Mais à la deuxième passe, le temps d'inférence chute drastiquement. Tu n'as plus à choisir entre la flexibilité de l'eager mode pendant le développement et la vitesse brute d'un backend compilé en production. C'est tout pour cet épisode. Merci d'avoir écouté, et continue à développer !
18

Compilateurs et graph breaks

3m 24s

Plongez sous le capot du compilateur PyTorch. Nous explorons les graph breaks, le flux de contrôle dynamique et pourquoi torch.compile réussit là où les anciens systèmes ont échoué.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. PyTorch Fundamentals, épisode 18 sur 18. Les anciens compilateurs d'IA exigeaient des chemins d'exécution parfaitement prévisibles et plantaient lamentablement si tu intégrais des constructions Python complexes et dynamiques dans ton modèle. Il te suffisait de modifier une seule instruction conditionnelle, et tout le processus de compilation plantait. PyTorch deux point zéro gère exactement ce même code arbitraire sans broncher. Le moteur derrière cette flexibilité repose sur la façon dont le compilateur gère les graph breaks. Si tu as bossé avec l'ancien outil de compilation, TorchScript, tu sais qu'il exigeait des structures de code rigides. TorchScript s'appuyait sur un typage statique strict et une exécution prévisible. Si ton modèle avait un control flow très dynamique, s'appuyait sur des dictionnaires Python standards, ou faisait appel à des librairies externes non-tensor, TorchScript le rejetait souvent. Les ingénieurs devaient souvent réécrire des parties entières de l'architecture de leur modèle juste pour satisfaire le compilateur. PyTorch deux point zéro aborde ça de manière totalement différente. Au lieu d'exiger du code statique en amont, le compilateur natif analyse dynamiquement ton exécution Python. Il capture toutes les opérations mathématiques qu'il peut optimiser de façon sûre et les regroupe dans un computational graph ultra performant. Inévitablement, le compilateur va tomber sur du code qu'il ne peut pas facilement mapper à une structure de graphe optimisée. Quand il tombe sur cette logique imprévisible, il déclenche un graph break. Un graph break n'est pas une erreur, et ce n'est pas un crash. C'est simplement un mécanisme de fallback. Ça veut dire que le compilateur rend proprement la main à l'eager execution standard de PyTorch pour ce segment de code spécifique. Prends l'exemple d'une fonction où tu lances une série de lourdes multiplications matricielles, suivies d'un if-statement Python qui vérifie la valeur moyenne d'un tensor pour décider de la prochaine opération. Cette condition est dépendante des données. Le chemin d'exécution est totalement inconnu jusqu'au moment précis où les valeurs du tensor sont calculées au runtime. Quand tu traces cette fonction avec le compilateur, il analyse le flow. Il prend les multiplications matricielles qui se passent avant la condition et les compile dans un sub-graph rapide et optimisé. Ensuite, il tombe sur le fameux if-statement. Comme il ne peut pas prédire le résultat, il crée un graph break. Le compilateur laisse le Python standard exécuter la condition en eager mode. Une fois la condition évaluée et le chemin choisi, le compilateur reprend le contrôle, en prenant les opérations restantes et en les compilant dans un deuxième sub-graph optimisé. Le système segmente automatiquement ton code. Tu obtiens des îlots de calculs rapides compilés, séparés par des ponts Python standards. Ton modèle continue de tourner de façon transparente. C'est ça le plus important. Tu verras sûrement des logs de performance pointer des graph breaks dans ton architecture. Même si tu veux généralement les minimiser pour tirer le maximum de vitesse d'exécution, ils sont juste là comme filet de sécurité. Ils garantissent que ton code produit toujours le bon résultat mathématique, même si chaque ligne ne peut pas être fusionnée dans un seul kernel. Le principal changement de design dans la compilation PyTorch moderne, c'est de privilégier une exécution sans accroc plutôt qu'une optimisation totale, pour s'assurer que le moteur s'adapte à ta logique arbitraire plutôt que de forcer ta logique à s'adapter au moteur. Ça conclut notre série sur PyTorch. Je t'encourage vivement à explorer la documentation officielle, à tester ces outils de compilation en pratique, et à visiter DEV STORIES DOT EU pour suggérer des sujets pour les prochaines séries. Je voudrais prendre un moment pour te remercier de ton écoute — ça nous aide beaucoup. Passe une super journée !