One place for hosting & domains

      Des conteneurs à Kubernetes avec Node.js eBook


      Télécharger le eBook complet

      Des conteneurs à Kubernetes avec Node.js eBook sous format EPUB

      Des conteneurs à Kubernetes avec Node.js eBook sous format PDF

      Présentation de l’eBook

      Ce livre a été conçu pour vous expliquer de quelle manière on peut utiliser des conteneurs et Kubernetes pour développer une pile complète. Vous apprendrez à développer une application de pile complète à l’aide de Node.js et MongoDB et à les gérer – d’abord avec Docker, puis avec Docker Compose et, pour finir, avec Kubernetes.

      Ce livre est basé sur la série de tutoriels Des conteneurs à Kubernetes avec Node.js disponible sur DigitalOcean Community. Les sujets traités sont les suivants :

      1. Créer une application Node.js à l’aide de Docker pour le développement

      2. Intégrer une base de données NoSQL à votre application Node.js à l’aide de MongoDB

      3. Gérer votre environnement de développement avec Docker Compose

      4. Migrer votre flux de travail Docker Compose vers Kubernetes

      5. Faire évoluer vos applications Node.js et MongoDB à l’aide de Helm et Kubernetes

      6. Sécuriser votre application Node.js conteneurisée avec Nginx, Let’s Encrypt et Docker Compose

      Chaque chapitre est conçu pour que vous puissiez progressivement faire votre construction à partir du début. Cependant, si vous connaissez un sujet ou si vous êtes plus intéressé par une section particulière, n’hésitez pas à passer au chapitre qui répond au mieux à votre objectif.

      Télécharger le eBook

      Vous pouvez télécharger le eBook au format EPUB ou PDF en suivant les liens ci-dessous.

      Télécharger le eBook complet

      Des conteneurs à Kubernetes avec Node.js eBook sous format EPUB

      Des conteneurs à Kubernetes avec Node.js eBook sous format PDF

      Si vous souhaitez en savoir plus sur le développement d’applications à l’aide de Node.js, consultez la section Node.js de la communauté DigitalOcean. Ou si vous souhaitez continuer à en apprendre plus sur les conteneurs, Docker et Kubernetes, nous vous conseillons de suivre le cours autoguidé Kubernetes pour les développeurs de pile complète.



      Source link

      Comment construire un réseau neuronal pour traduire la langue des signes en anglais


      L’auteur a choisi Code Org ​​ pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      La vision par ordinateur est un sous-domaine de l’informatique qui vise à extraire une compréhension supérieure des choses à partir d’images et de vidéos. On la retrouve dans les technologies comme les filtres de chat vidéo amusants, l’authentification de visage sur votre appareil mobile et les voitures autonomes.

      Dans ce tutoriel, vous utiliserez la vision par ordinateur pour créer un traducteur de la langue des signes américaine pour votre webcam. Au cours de ce tutoriel, vous utiliserez OpenCV, une bibliothèque de vision par ordinateur, PyTorch pour créer un réseau neuronal profond et onnx pour exporter votre réseau neuronal. Vous appliquerez également les concepts suivants lors de la création d’une application de vision par ordinateur :

      • Vous utiliserez la même méthode en trois étapes qui est utilisée dans le tutoriel Comment appliquer la vision par ordinateur pour créer un filtre pour chiens basé sur les émotions : pré-traiter un ensemble de données, former un modèle et évaluer le modèle.
      • Vous allez également aller plus loin dans chacune de ces étapes : utiliser l’augmentation des données pour traiter les aiguilles tournées ou non centrées, modifier les horaires de fréquence d’apprentissage pour améliorer la précision du modèle et exporter des modèles pour une vitesse d’inférence plus rapide.
      • En cours de route, vous explorerez également les concepts liés à l’apprentissage automatique.

      À la fin de ce tutoriel, vous aurez à la fois un traducteur de la langue des signes américaine et le savoir-faire fondamental sur l’apprentissage profond. Vous pouvez également accéder au code source complet de ce projet.

      Conditions préalables

      Pour terminer ce tutoriel, vous aurez besoin des éléments suivants :

      Étape 1 – Création du projet et installation des dépendances

      Créons un espace de travail pour ce projet et installons les dépendances dont nous aurons besoin.

      Sur les distributions Linux, commencez par préparer votre gestionnaire de packages système et installez le package virtualenv de Python3. Utilisez :

      • apt-get update
      • apt-get upgrade
      • apt-get install python3-venv

      Nous allons appeler notre espace de travail SignLanguage :

      Naviguez jusqu’au répertoire SignLanguage :

      Ensuite, créez un nouvel environnement virtuel pour le projet :

      • python3 -m venv signlanguage

      Activez votre environnement :

      • source signlanguage/bin/activate

      Installez ensuite PyTorch, un framework d’apprentissage profond pour Python que nous utiliserons au cours de ce tutoriel.

      Sous macOS, installez Pytorch avec la commande suivante :

      • python -m pip install torch==1.2.0 torchvision==0.4.0

      Sous Linux et Windows, utilisez les commandes suivantes pour une construction du CPU uniquement :

      • pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
      • pip install torchvision

      Installez maintenant les binaires préfilmés pour OpenCV, numpy et onnx, des bibliothèques destinée à la vision par ordinateur, l’algèbre linéaire, l’exportation de modèle AI et l’exécution de modèle AI, respectivement. OpenCV propose des utilitaires tels que les rotations d’images et numpy fournit des utilitaires d’algèbre linéaire comme l’inversion d’une matrice :

      • python -m pip install opencv-python==3.4.3.18 numpy==1.14.5 onnx==1.6.0 onnxruntime==1.0.0

      Sur les distributions Linux, vous devrez installer libSM.so :

      • apt-get install libsm6 libxext6 libxrender-dev

      Une fois les dépendances installées, construisons la première version de notre traducteur de la langue des signes : un système de classification de la langue des signes.

      Étape 2 – Préparation de l’ensemble de données de classification de la langue des signes

      Au cours des trois prochaines sections, vous allez construire un système de classification de la langue des signes à l’aide d’un réseau neuronal. Votre objectif est de produire un modèle qui accepte une image d’une main en entrée et génère une lettre.

      Vous devez suivre les trois étapes suivantes pour créer un modèle de classification d’apprentissage automatique :

      1. Pré-traitez les données : appliquez one-hot encoding​​​​​​ à vos étiquettes et sauvegardez vos données dans PyTorch Tensors. Entraînez votre modèle sur des données augmentées pour le préparer à une saisie “inhabituelle”, comme une main sur le côté ou tournée.
      2. Spécifiez et entraînez le modèle : configurez un réseau neuronal à l’aide de PyTorch. Définissez les hyper-paramètres d’entraînement (comme la durée d’entraînement) et exécutez la descente de gradient stochastique. Vous modifierez également un hyper-paramètre d’entraînement spécifique, qui correspond au calendrier de fréquence d’apprentissage. Ils optimisent la précision du modèle.
      3. Exécutez une prédiction à l’aide du modèle : évaluez le réseau neuronal sur vos données de validation pour comprendre sa précision. Ensuite, exportez le modèle dans un format appelé ONNX pour avoir des vitesses d’inférence plus rapides.

      Dans cette section du tutoriel, vous allez effectuer l’étape 1 sur 3. Vous allez télécharger les données, créer un objet Dataset pour itérer sur vos données et enfin appliquer l’augmentation des données. À la fin de cette étape, vous aurez un moyen d’accéder par programme aux images et aux étiquettes de votre ensemble de données qui viendront alimenter votre modèle.

      Tout d’abord, téléchargez l’ensemble de données dans votre répertoire de travail actuel :

      Note : sous macOS, par défaut, wget n’est pas disponible. Pour qu’il le soit, installez Homebrew en suivant ce tutoriel de DigitalOcean. Ensuite, exécutez brew install wget.

      • wget https://assets.digitalocean.com/articles/signlanguage_data/sign-language-mnist.tar.gz

      Décompressez le fichier zip, qui contient un répertoire data/ :

      • tar -xzf sign-language-mnist.tar.gz

      Créez un nouveau fichier que vous nommerez step_2_dataset.py :

      Comme précédemment, importez les utilitaires nécessaires et créez la classe qui contiendra vos données. Ici, pour le traitement des données, vous allez créer des ensembles de données d’entraînement et de test. Vous allez implémenter l’interface de Dataset de PyTorch, qui vous permettra de charger et d’utiliser le pipeline de données intégré de PyTorch pour votre ensemble de données de classification de la langue des signes :

      step_2_dataset.py

      from torch.utils.data import Dataset
      from torch.autograd import Variable
      import torch.nn as nn
      import numpy as np
      import torch
      
      import csv
      
      
      class SignLanguageMNIST(Dataset):
          """Sign Language classification dataset.
      
          Utility for loading Sign Language dataset into PyTorch. Dataset posted on
          Kaggle in 2017, by an unnamed author with username `tecperson`:
          https://www.kaggle.com/datamunge/sign-language-mnist
      
          Each sample is 1 x 1 x 28 x 28, and each label is a scalar.
          """
          pass
      

      Supprimez l’espace réservé pass dans la catégorie SignLanguageMNIST. À sa place, ajoutez une méthode pour générer un mappage d’étiquette :

      step_2_dataset.py

          @staticmethod
          def get_label_mapping():
              """
              We map all labels to [0, 23]. This mapping from dataset labels [0, 23]
              to letter indices [0, 25] is returned below.
              """
              mapping = list(range(25))
              mapping.pop(9)
              return mapping
      

      Les étiquettes vont de 0 à 25. Cependant, les lettres J (9) et Z (25) sont exclues. Cela signifie qu’il n’existe que 24 valeurs d’étiquette valables. Pour que l’ensemble de toutes les valeurs d’étiquette à partir de 0 soit contigu, nous mappons toutes les étiquettes de [0 à 23]. Ce mappage des étiquettes de [0 à 23] et des indices de lettre de [0 à 25] de l’ensemble de données est fourni par cette méthode get_label_mapping.

      Ensuite, ajoutez une méthode pour extraire les étiquettes et les échantillons d’un fichier CSV. Ce qui suit suppose que chaque ligne commence par l’étiquette, ensuite suivie des valeurs 784 pixels. Ces valeurs 784 pixels représentent une image 28x28 :

      step_2_dataset.py

          @staticmethod
          def read_label_samples_from_csv(path: str):
              """
              Assumes first column in CSV is the label and subsequent 28^2 values
              are image pixel values 0-255.
              """
              mapping = SignLanguageMNIST.get_label_mapping()
              labels, samples = [], []
              with open(path) as f:
                  _ = next(f)  # skip header
                  for line in csv.reader(f):
                      label = int(line[0])
                      labels.append(mapping.index(label))
                      samples.append(list(map(int, line[1:])))
              return labels, samples
      

      Pour avoir une explication sur la façon dont ces 784 valeurs représentent une image, voir Créer un filtre pour chien basé sur les émotions, étape 4.

      Notez que chaque ligne de l’itérable csv.reader est une liste de chaînes. Les invocations int et map (int, ...) transforment toutes les chaînes en entiers. Juste en dessous de notre méthode statique, ajoutez une fonction qui initialisera notre support de données :

      step_2_dataset.py

          def __init__(self,
                  path: str="data/sign_mnist_train.csv",
                  mean: List[float]=[0.485],
                  std: List[float]=[0.229]):
              """
              Args:
                  path: Path to `.csv` file containing `label`, `pixel0`, `pixel1`...
              """
              labels, samples = SignLanguageMNIST.read_label_samples_from_csv(path)
              self._samples = np.array(samples, dtype=np.uint8).reshape((-1, 28, 28, 1))
              self._labels = np.array(labels, dtype=np.uint8).reshape((-1, 1))
      
              self._mean = mean
              self._std = std
      

      Cette fonction commence par charger les échantillons et les étiquettes. Ensuite, elle sauvegarde les données dans des tableaux NumPy. Les informations sur l’écart moyen et l’écart-type seront expliquées sous peu, dans la section __getitem__ suivante.

      Juste après la fonction __init__, ajoutez une fonction __len__ Le Dataset requiert cette méthode pour déterminer à quel moment arrêter l’itération sur les données :

      step_2_dataset.py

      ...
          def __len__(self):
              return len(self._labels)
      

      Enfin, ajoutez une méthode __getitem__, qui renvoie un dictionnaire qui contient l’échantillon et l’étiquette :

      step_2_dataset.py

          def __getitem__(self, idx):
              transform = transforms.Compose([
                  transforms.ToPILImage(),
                  transforms.RandomResizedCrop(28, scale=(0.8, 1.2)),
                  transforms.ToTensor(),
                  transforms.Normalize(mean=self._mean, std=self._std)])
      
              return {
                  'image': transform(self._samples[idx]).float(),
                  'label': torch.from_numpy(self._labels[idx]).float()
              }
      

      Vous utilisez la technique que l’on appelle data augmentation, dans laquelle les échantillons sont perturbés pendant l’entraînement, pour augmenter la robustesse du modèle face à ces perturbations. En particulier, zoomez de façon aléatoire sur l’image en variant les quantités et sur différents emplacements, via RandomResizedCrop. Notez que le zoom avant ne devrait pas affecter la catégorie finale de la langue des signes. Ainsi, l’étiquette n’est pas transformée. Vous normalisez encore plus les entrées de sorte que les valeurs d’image soient remises à l’échelle dans la plage [0 à 1] dans les valeurs attendues, au lieu de [0 à 25]5. Pour ce faire, utilisez l’ensemble de données _mean et _std lors de la normalisation.

      La catégorie SignLanguageMNIST que vous venez de terminer ressemblera à ce qui suit :

      step_2_dataset.py

      from torch.utils.data import Dataset
      from torch.autograd import Variable
      import torchvision.transforms as transforms
      import torch.nn as nn
      import numpy as np
      import torch
      
      from typing import List
      
      import csv
      
      
      class SignLanguageMNIST(Dataset):
          """Sign Language classification dataset.
      
          Utility for loading Sign Language dataset into PyTorch. Dataset posted on
          Kaggle in 2017, by an unnamed author with username `tecperson`:
          https://www.kaggle.com/datamunge/sign-language-mnist
      
          Each sample is 1 x 1 x 28 x 28, and each label is a scalar.
          """
      
          @staticmethod
          def get_label_mapping():
              """
              We map all labels to [0, 23]. This mapping from dataset labels [0, 23]
              to letter indices [0, 25] is returned below.
              """
              mapping = list(range(25))
              mapping.pop(9)
              return mapping
      
          @staticmethod
          def read_label_samples_from_csv(path: str):
              """
              Assumes first column in CSV is the label and subsequent 28^2 values
              are image pixel values 0-255.
              """
              mapping = SignLanguageMNIST.get_label_mapping()
              labels, samples = [], []
              with open(path) as f:
                  _ = next(f)  # skip header
                  for line in csv.reader(f):
                      label = int(line[0])
                      labels.append(mapping.index(label))
                      samples.append(list(map(int, line[1:])))
              return labels, samples
      
          def __init__(self,
                  path: str="data/sign_mnist_train.csv",
                  mean: List[float]=[0.485],
                  std: List[float]=[0.229]):
              """
              Args:
                  path: Path to `.csv` file containing `label`, `pixel0`, `pixel1`...
              """
              labels, samples = SignLanguageMNIST.read_label_samples_from_csv(path)
              self._samples = np.array(samples, dtype=np.uint8).reshape((-1, 28, 28, 1))
              self._labels = np.array(labels, dtype=np.uint8).reshape((-1, 1))
      
              self._mean = mean
              self._std = std
      
          def __len__(self):
              return len(self._labels)
      
          def __getitem__(self, idx):
              transform = transforms.Compose([
                  transforms.ToPILImage(),
                  transforms.RandomResizedCrop(28, scale=(0.8, 1.2)),
                  transforms.ToTensor(),
                  transforms.Normalize(mean=self._mean, std=self._std)])
      
              return {
                  'image': transform(self._samples[idx]).float(),
                  'label': torch.from_numpy(self._labels[idx]).float()
              }
      

      Comme précédemment, vous allez maintenant vérifier les fonctions de notre utilitaire d’ensemble de données en chargeant l’ensemble de données SignLanguageMNIST. Ajoutez le code suivant à la fin de votre fichier après la catégorie SignLanguageMNIST :

      step_2_dataset.py

      def get_train_test_loaders(batch_size=32):
          trainset = SignLanguageMNIST('data/sign_mnist_train.csv')
          trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True)
      
          testset = SignLanguageMNIST('data/sign_mnist_test.csv')
          testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False)
          return trainloader, testloader
      

      Ce code initialise l’ensemble de données avec la catégorie SignLanguageMNIST. Ensuite, pour les ensembles d’entraînement et de validation, il sauvegarde l’ensemble de données dans un DataLoader. Cela traduira l’ensemble de données en un itérable à utiliser plus tard.

      Vous allez maintenant vérifier que les utilitaires d’ensemble de données fonctionnent bien. Créez un exemple de chargeur de jeu de données à l’aide DataLoader et imprimez le premier élément de ce chargeur. Ajoutez ce qui suit à la fin de votre fichier :

      step_2_dataset.py

      if __name__ == '__main__':
          loader, _ = get_train_test_loaders(2)
          print(next(iter(loader)))
      

      Vous pouvez vérifier si votre fichier correspond au fichier step_2_dataset dans ce (référentiel). Quittez votre éditeur et exécutez le script avec les éléments suivants :

      Cela génère la paire de vecteurs contravariants suivante. Notre pipeline de données génère deux échantillons et deux étiquettes. Cela indique que notre pipeline de données est opérationnel et prêt à être utilisé :

      Output

      {'image': tensor([[[[ 0.4337, 0.5022, 0.5707, ..., 0.9988, 0.9646, 0.9646], [ 0.4851, 0.5536, 0.6049, ..., 1.0502, 1.0159, 0.9988], [ 0.5364, 0.6049, 0.6392, ..., 1.0844, 1.0844, 1.0673], ..., [-0.5253, -0.4739, -0.4054, ..., 0.9474, 1.2557, 1.2385], [-0.3369, -0.3369, -0.3369, ..., 0.0569, 1.3584, 1.3242], [-0.3712, -0.3369, -0.3198, ..., 0.5364, 0.5364, 1.4783]]], [[[ 0.2111, 0.2796, 0.3481, ..., 0.2453, -0.1314, -0.2342], [ 0.2624, 0.3309, 0.3652, ..., -0.3883, -0.0629, -0.4568], [ 0.3309, 0.3823, 0.4337, ..., -0.4054, -0.0458, -1.0048], ..., [ 1.3242, 1.3584, 1.3927, ..., -0.4054, -0.4568, 0.0227], [ 1.3242, 1.3927, 1.4612, ..., -0.1657, -0.6281, -0.0287], [ 1.3242, 1.3927, 1.4440, ..., -0.4397, -0.6452, -0.2856]]]]), 'label': tensor([[24.], [11.]])}

      Vous avez maintenant vérifié si votre pipeline de données fonctionne bien. Ceci conclut la première étape, le prétraitement de vos données, qui comprend désormais une augmentation des données pour un module plus robuste. Vous allez ensuite définir le réseau neuronal et l’optimiseur.

      Étape 3 – Création et formation du un système de classification de la langue des signes à l’aide de l’apprentissage profond

      Maintenant que vous avez un pipeline de données fonctionnel, vous allez définir un modèle et le former sur les données. Vous allez tout particulièrement construire un réseau neuronal à six couches, définir une perte, un optimiseur et enfin optimiser la fonction de perte pour les prédictions de votre réseau neuronal. À la fin de cette étape, vous disposerez d’un système de classification de la langue des signes fonctionnel.

      Créez un nouveau fichier appelé step_3_train.py :

      Importez les utilitaires dont vous avez besoin :

      step_3_train.py

      from torch.utils.data import Dataset
      from torch.autograd import Variable
      import torch.nn as nn
      import torch.nn.functional as F
      import torch.optim as optim
      import torch
      
      from step_2_dataset import get_train_test_loaders
      

      Définissez un réseau neuronal PyTorch comprenant trois couches convolutives, suivies de trois couches entièrement connectées. Ajoutez ce qui suit à la fin de votre script existant :

      step_3_train.py

      class Net(nn.Module):
          def __init__(self):
              super(Net, self).__init__()
              self.conv1 = nn.Conv2d(1, 6, 3)
              self.pool = nn.MaxPool2d(2, 2)
              self.conv2 = nn.Conv2d(6, 6, 3)
              self.conv3 = nn.Conv2d(6, 16, 3)
              self.fc1 = nn.Linear(16 * 5 * 5, 120)
              self.fc2 = nn.Linear(120, 48)
              self.fc3 = nn.Linear(48, 24)
      
          def forward(self, x):
              x = F.relu(self.conv1(x))
              x = self.pool(F.relu(self.conv2(x)))
              x = self.pool(F.relu(self.conv3(x)))
              x = x.view(-1, 16 * 5 * 5)
              x = F.relu(self.fc1(x))
              x = F.relu(self.fc2(x))
              x = self.fc3(x)
              return x
      

      Maintenant, initialisez le réseau neuronal, définissez une fonction de perte et configurez les hyperparamètres d’optimisation en ajoutant le code suivant à la fin du script :

      step_3_train.py

      def main():
          net = Net().float()
          criterion = nn.CrossEntropyLoss()
          optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9)
      

      Enfin, vous vous entraînerez sur deux epochs :

      step_3_train.py

      def main():
          net = Net().float()
          criterion = nn.CrossEntropyLoss()
          optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9)
      
          trainloader, _ = get_train_test_loaders()
          for epoch in range(2):  # loop over the dataset multiple times
              train(net, criterion, optimizer, trainloader, epoch)
          torch.save(net.state_dict(), "checkpoint.pth")
      

      Vous configurez une epoch comme une itération de l’entraînement au cours de laquelle chaque échantillon d’entraînement a été utilisé exactement une fois. À la fin de la fonction principale, les paramètres du modèle seront enregistrés dans un fichier nommé "checkpoint.pth".

      Ajoutez le code suivant à la fin de votre script pour extraire l’image et l'étiquette du chargeur d’ensemble de données, puis sauvegardez-les tous dans une variable PyTorch :

      step_3_train.py

      def train(net, criterion, optimizer, trainloader, epoch):
          running_loss = 0.0
          for i, data in enumerate(trainloader, 0):
              inputs = Variable(data['image'].float())
              labels = Variable(data['label'].long())
              optimizer.zero_grad()
      
              # forward + backward + optimize
              outputs = net(inputs)
              loss = criterion(outputs, labels[:, 0])
              loss.backward()
              optimizer.step()
      
              # print statistics
              running_loss += loss.item()
              if i % 100 == 0:
                  print('[%d, %5d] loss: %.6f' % (epoch, i, running_loss / (i + 1)))
      

      Ce code exécutera également la passe avant, puis la rétropropagera à travers le réseau de perte et neuronal.

      À la fin de votre fichier, ajoutez ce qui suit pour appeler la fonction main :

      step_3_train.py

      if __name__ == '__main__':
          main()
      

      Vérifiez que les éléments de votre fichier correspondent à ce qui suit :

      step_3_train.py

      from torch.utils.data import Dataset
      from torch.autograd import Variable
      import torch.nn as nn
      import torch.nn.functional as F
      import torch.optim as optim
      import torch
      
      from step_2_dataset import get_train_test_loaders
      
      
      class Net(nn.Module):
          def __init__(self):
              super(Net, self).__init__()
              self.conv1 = nn.Conv2d(1, 6, 3)
              self.pool = nn.MaxPool2d(2, 2)
              self.conv2 = nn.Conv2d(6, 6, 3)
              self.conv3 = nn.Conv2d(6, 16, 3)
              self.fc1 = nn.Linear(16 * 5 * 5, 120)
              self.fc2 = nn.Linear(120, 48)
              self.fc3 = nn.Linear(48, 25)
      
          def forward(self, x):
              x = F.relu(self.conv1(x))
              x = self.pool(F.relu(self.conv2(x)))
              x = self.pool(F.relu(self.conv3(x)))
              x = x.view(-1, 16 * 5 * 5)
              x = F.relu(self.fc1(x))
              x = F.relu(self.fc2(x))
              x = self.fc3(x)
              return x
      
      
      def main():
          net = Net().float()
          criterion = nn.CrossEntropyLoss()
          optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9)
      
          trainloader, _ = get_train_test_loaders()
          for epoch in range(2):  # loop over the dataset multiple times
              train(net, criterion, optimizer, trainloader, epoch)
          torch.save(net.state_dict(), "checkpoint.pth")
      
      
      def train(net, criterion, optimizer, trainloader, epoch):
          running_loss = 0.0
          for i, data in enumerate(trainloader, 0):
              inputs = Variable(data['image'].float())
              labels = Variable(data['label'].long())
              optimizer.zero_grad()
      
              # forward + backward + optimize
              outputs = net(inputs)
              loss = criterion(outputs, labels[:, 0])
              loss.backward()
              optimizer.step()
      
              # print statistics
              running_loss += loss.item()
              if i % 100 == 0:
                  print('[%d, %5d] loss: %.6f' % (epoch, i, running_loss / (i + 1)))
      
      
      if __name__ == '__main__':
          main()
      

      Sauvegardez et fermez. Ensuite, lancez notre entraînement de validation de concept en exécutant :

      Lorsque votre réseau neuronal s’entraîne, vous aurez un résultat semblable à ce qui suit :

      Output

      [0, 0] loss: 3.208171 [0, 100] loss: 3.211070 [0, 200] loss: 3.192235 [0, 300] loss: 2.943867 [0, 400] loss: 2.569440 [0, 500] loss: 2.243283 [0, 600] loss: 1.986425 [0, 700] loss: 1.768090 [0, 800] loss: 1.587308 [1, 0] loss: 0.254097 [1, 100] loss: 0.208116 [1, 200] loss: 0.196270 [1, 300] loss: 0.183676 [1, 400] loss: 0.169824 [1, 500] loss: 0.157704 [1, 600] loss: 0.151408 [1, 700] loss: 0.136470 [1, 800] loss: 0.123326

      Pour obtenir une perte plus faible, vous pouvez augmenter le nombre d’époques de 5 à 10 ou même 20. Cependant, après une certaine période d’entraînement, la perte de réseau ne pourra plus diminuer avec l’augmentation du temps d’entraînement. Pour contourner ce problème, à mesure que le temps d’entraînement augmente, vous introduirez un calendrier de taux d’apprentissage, qui viendra faire baisser le taux d’apprentissage au fil du temps. Pour comprendre pourquoi cela fonctionne, voir la présentation de Distill “Pourquoi Momentum fonctionne réellement”

      Modifiez votre fonction main avec les deux lignes suivantes, configurant un scheduler et invoquant scheduler.step. De plus, configurez le nombre d’époques sur 12 :

      step_3_train.py

      def main():
          net = Net().float()
          criterion = nn.CrossEntropyLoss()
          optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9)
          scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)
      
          trainloader, _ = get_train_test_loaders()
          for epoch in range(12):  # loop over the dataset multiple times
              train(net, criterion, optimizer, trainloader, epoch)
              scheduler.step()
          torch.save(net.state_dict(), "checkpoint.pth")
      

      Vérifiez que votre fichier correspond au fichier de l’étape 3 dans ce référentiel. L’entraînement durera environ 5 minutes. Votre résultat ressemblera à ce qui suit:

      Output

      [0, 0] loss: 3.208171 [0, 100] loss: 3.211070 [0, 200] loss: 3.192235 [0, 300] loss: 2.943867 [0, 400] loss: 2.569440 [0, 500] loss: 2.243283 [0, 600] loss: 1.986425 [0, 700] loss: 1.768090 [0, 800] loss: 1.587308 ... [11, 0] loss: 0.000302 [11, 100] loss: 0.007548 [11, 200] loss: 0.009005 [11, 300] loss: 0.008193 [11, 400] loss: 0.007694 [11, 500] loss: 0.008509 [11, 600] loss: 0.008039 [11, 700] loss: 0.007524 [11, 800] loss: 0.007608

      La perte finale obtenue est de 0.007608, soit 3 ordres de grandeur plus petite que la perte de départ de 3.20. Ceci conclut la deuxième étape de notre flux de travail, au cours duquel nous configurons et entraînons le réseau neuronal. Cela dit, aussi petite que soit cette valeur de perte, elle n’a que peu de sens. Pour mettre les performances du modèle en perspective, nous calculerons sa précision, c’est à dire le pourcentage d’images correctement classées par le modèle.

      Étape 4 – Évaluation du système de classification de la langue des signes

      Vous allez maintenant évaluer votre système de classification de la langue des signes en calculant sa précision sur le validation set, un ensemble d’images que le modèle n’a pas vu pendant l’entraînement. Cela vous donnera une meilleure idée des performances du modèle que la valeur de perte finale. De plus, vous ajouterez des utilitaires pour enregistrer notre modèle entraîné à la fin de l’entraînement et charger notre modèle pré-formé lors de l’inférence.

      Créez un nouveau fichier que vous appellerez step_4_evaluate.py.

      Importez les utilitaires dont vous avez besoin :

      step_4_evaluate.py

      from torch.utils.data import Dataset
      from torch.autograd import Variable
      import torch.nn as nn
      import torch.nn.functional as F
      import torch.optim as optim
      import torch
      import numpy as np
      
      import onnx
      import onnxruntime as ort
      
      from step_2_dataset import get_train_test_loaders
      from step_3_train import Net
      

      Ensuite, configurer un utilitaire pour évaluer les performances du réseau neuronal. La fonction suivante compare la lettre prédite par le réseau neuronal avec la vraie lettre, pour une seule image :

      step_4_evaluate.py

      def evaluate(outputs: Variable, labels: Variable) -> float:
          """Evaluate neural network outputs against non-one-hotted labels."""
          Y = labels.numpy()
          Yhat = np.argmax(outputs, axis=1)
          return float(np.sum(Yhat == Y))
      

      outputs liste les catégories probables pour chaque échantillon. Par exemple, les outputs pour un seul échantillon peuvent être [0.1, 0.3, 0.4, 0.2]. labels est une liste de catégories d’étiquettes. Par exemple, la catégorie d’étiquette peut être 3.

      Y = ... convertit les étiquettes en un tableau NumPy. Ensuite, Yhat = np.argmax (...) convertit les catégories probables des outputs en prédictions de catégories. Par exemple, la liste de catégories probables [0.1, 0.3, 0.4, 0.2] donnerait la prédiction de catégorie 2 prédite, car la valeur d’indice 2 de 0,4 est la plus grande valeur.

      Maintenant que Y et Yhat sont des catégories, vous pouvez les comparer. Yhat == Y vérifie si la prédiction de catégorie correspond à la catégorie d’étiquette, et np.sum (...) est une astuce qui calcule le nombre de valeurs de truth-y. En d’autres termes, np.sum affichera le nombre d’échantillons correctement classés.

      Ajoutez la deuxième fonction batch_evaluate, qui applique la première fonction evaluate à toutes les images :

      step_4_evaluate.py

      def batch_evaluate(
              net: Net,
              dataloader: torch.utils.data.DataLoader) -> float:
          """Evaluate neural network in batches, if dataset is too large."""
          score = n = 0.0
          for batch in dataloader:
              n += len(batch['image'])
              outputs = net(batch['image'])
              if isinstance(outputs, torch.Tensor):
                  outputs = outputs.detach().numpy()
              score += evaluate(outputs, batch['label'][:, 0])
          return score / n
      

      batch est un groupe d’images stockées comme un seul vecteur contravariant. Tout d’abord, vous devez augmenter le nombre total d’images à évaluer (n) en fonction du nombre d’images de ce lot. Ensuite, exécutez l’inférence sur le réseau neuronal avec ce lot d’images, outputs = net(...). La vérification type if isinstance (...) convertit les sorties dans un tableau NumPy au besoin. Enfin, utilisez evaluate pour calculer le nombre d’échantillons correctement classés. À la fin de la fonction, vous calculez le pourcentage d’échantillons que vous avez correctement classés, score / n.

      Enfin, ajoutez le script suivant pour tirer parti des utilitaires précédents :

      step_4_evaluate.py

      def validate():
          trainloader, testloader = get_train_test_loaders()
          net = Net().float()
      
          pretrained_model = torch.load("checkpoint.pth")
          net.load_state_dict(pretrained_model)
      
          print('=' * 10, 'PyTorch', '=' * 10)
          train_acc = batch_evaluate(net, trainloader) * 100.
          print('Training accuracy: %.1f' % train_acc)
          test_acc = batch_evaluate(net, testloader) * 100.
          print('Validation accuracy: %.1f' % test_acc)
      
      
      if __name__ == '__main__':
          validate()
      

      Cela charge un réseau neuronal pré-entraîné et évalue ses performances sur l’ensemble de données en langue des signes fourni. Plus précisément, le script donne ici une précision sur les images que vous avez utilisées pour la formation et un ensemble distinct d’images que vous mettez de côté à des fins de test, appelé validation set.

      Vous allez ensuite exporter le modèle PyTorch vers un fichier binaire ONNX. Ce fichier binaire peut ensuite être utilisé en production pour exécuter l’inférence avec votre modèle. Plus important encore, le code exécutant ce binaire n’a pas besoin d’une copie de la configuration du réseau d’origine. À la fin de la fonction de valide, ajoutez ce qui suit :

      step_4_evaluate.py

          trainloader, testloader = get_train_test_loaders(1)
      
          # export to onnx
          fname = "signlanguage.onnx"
          dummy = torch.randn(1, 1, 28, 28)
          torch.onnx.export(net, dummy, fname, input_names=['input'])
      
          # check exported model
          model = onnx.load(fname)
          onnx.checker.check_model(model)  # check model is well-formed
      
          # create runnable session with exported model
          ort_session = ort.InferenceSession(fname)
          net = lambda inp: ort_session.run(None, {'input': inp.data.numpy()})[0]
      
          print('=' * 10, 'ONNX', '=' * 10)
          train_acc = batch_evaluate(net, trainloader) * 100.
          print('Training accuracy: %.1f' % train_acc)
          test_acc = batch_evaluate(net, testloader) * 100.
          print('Validation accuracy: %.1f' % test_acc)
      

      Cela exporte le modèle ONNX, vérifie le modèle exporté, puis exécute l’inférence avec le modèle exporté. Vérifiez que votre fichier correspond au fichier de l’étape 4 dans ce référentiel :

      step_4_evaluate.py

      from torch.utils.data import Dataset
      from torch.autograd import Variable
      import torch.nn as nn
      import torch.nn.functional as F
      import torch.optim as optim
      import torch
      import numpy as np
      
      import onnx
      import onnxruntime as ort
      
      from step_2_dataset import get_train_test_loaders
      from step_3_train import Net
      
      
      def evaluate(outputs: Variable, labels: Variable) -> float:
          """Evaluate neural network outputs against non-one-hotted labels."""
          Y = labels.numpy()
          Yhat = np.argmax(outputs, axis=1)
          return float(np.sum(Yhat == Y))
      
      
      def batch_evaluate(
              net: Net,
              dataloader: torch.utils.data.DataLoader) -> float:
          """Evaluate neural network in batches, if dataset is too large."""
          score = n = 0.0
          for batch in dataloader:
              n += len(batch['image'])
              outputs = net(batch['image'])
              if isinstance(outputs, torch.Tensor):
                  outputs = outputs.detach().numpy()
              score += evaluate(outputs, batch['label'][:, 0])
          return score / n
      
      
      def validate():
          trainloader, testloader = get_train_test_loaders()
          net = Net().float().eval()
      
          pretrained_model = torch.load("checkpoint.pth")
          net.load_state_dict(pretrained_model)
      
          print('=' * 10, 'PyTorch', '=' * 10)
          train_acc = batch_evaluate(net, trainloader) * 100.
          print('Training accuracy: %.1f' % train_acc)
          test_acc = batch_evaluate(net, testloader) * 100.
          print('Validation accuracy: %.1f' % test_acc)
      
          trainloader, testloader = get_train_test_loaders(1)
      
          # export to onnx
          fname = "signlanguage.onnx"
          dummy = torch.randn(1, 1, 28, 28)
          torch.onnx.export(net, dummy, fname, input_names=['input'])
      
          # check exported model
          model = onnx.load(fname)
          onnx.checker.check_model(model)  # check model is well-formed
      
          # create runnable session with exported model
          ort_session = ort.InferenceSession(fname)
          net = lambda inp: ort_session.run(None, {'input': inp.data.numpy()})[0]
      
          print('=' * 10, 'ONNX', '=' * 10)
          train_acc = batch_evaluate(net, trainloader) * 100.
          print('Training accuracy: %.1f' % train_acc)
          test_acc = batch_evaluate(net, testloader) * 100.
          print('Validation accuracy: %.1f' % test_acc)
      
      
      if __name__ == '__main__':
          validate()
      

      Pour utiliser et évaluer le point de contrôle de la dernière étape, exécutez ce qui suit :

      • python step_4_evaluate.py

      Cela générera une sortie similaire à la suivante, affirmant que votre modèle exporté non seulement fonctionne, mais le fait également en accord avec votre modèle PyTorch d’origine :

      Output

      ========== PyTorch ========== Training accuracy: 99.9 Validation accuracy: 97.4 ========== ONNX ========== Training accuracy: 99.9 Validation accuracy: 97.4

      Votre réseau neuronal atteint une précision d’entraînement de 99,9 % et une précision de validation de 97,4 %. Cet écart entre la précision d’entraînement et de la validation indique que votre modèle souffre d’un ajustement excessif. Cela signifie qu’au lieu d’apprendre des modèles généralisables, votre modèle a mémorisé les données d’entraînement. Pour comprendre les implications et les causes du sur-ajustement, consultez Comprendre les compromis entre le biais et la variance.

      À ce stade, nous avons terminé de concevoir un système de classification de la langue des signes En substance, notre modèle peut correctement lever une ambiguïté entre les signes presque tout le temps. Nous avons un modèle plutôt acceptable, nous pouvons donc passer à l’étape finale de notre application. Nous utiliserons ce système de classification de la langue des signes dans une application webcam en temps réel.

      Étape 5 – Liaison du flux de la caméra

      Votre prochain objectif est de relier l’appareil photo de l’ordinateur à votre système de classification de la langue des signes. Vous allez collecter les entrées de la caméra, classer la langue des signes affichée, puis signaler le signe classifié à l’utilisateur.

      Créez maintenant un script Python pour le détecteur de visages. Créez le fichier step_6_camera.py en utilisant nano ou votre éditeur de texte favori :

      Ajoutez le code suivant dans le fichier :

      step_5_camera.py

      """Test for sign language classification"""
      import cv2
      import numpy as np
      import onnxruntime as ort
      
      def main():
          pass
      
      if __name__ == '__main__':
          main()
      

      Ce code importe OpenCV, qui contient vos utilitaires d’image, et le runtime ONNX, tout ce dont vous avez besoin pour exécuter l’inférence avec votre modèle. Le reste du code est un texte standard type du programme Python.

      Remplacez maintenant pass dans la fonction main par le code suivant, qui initialise un système de classification de la langue des signes en utilisant les paramètres que vous avez précédemment entraînés. Ajoutez également un mappage des index aux lettres et aux statistiques d’images :

      step_5_camera.py

      def main():
          # constants
          index_to_letter = list('ABCDEFGHIKLMNOPQRSTUVWXY')
          mean = 0.485 * 255.
          std = 0.229 * 255.
      
          # create runnable session with exported model
          ort_session = ort.InferenceSession("signlanguage.onnx")
      

      Vous utiliserez des éléments de ce test script de la documentation officielle d’OpenCV. Plus précisément, vous mettrez à jour le corps de la fonction main. Commencez par initialiser un objet VideoCapture configuré pour capturer le flux en direct à partir de la caméra de votre ordinateur. Placez-le à la fin de la fonction main :

      step_5_camera.py

      def main():
          ...
          # create runnable session with exported model
          ort_session = ort.InferenceSession("signlanguage.onnx")
      
          cap = cv2.VideoCapture(0)
      

      Ajoutez ensuite une boucle while pour que la lecture se fasse à partir de la caméra à chaque intervalle de temps :

      step_5_camera.py

      def main():
          ...
          cap = cv2.VideoCapture(0)
          while True:
              # Capture frame-by-frame
              ret, frame = cap.read()
      

      Écrivez une fonction utilitaire qui prend le recadrage central comme cadre de caméra. Placez cette fonction avant main :

      step_5_camera.py

      def center_crop(frame):
          h, w, _ = frame.shape
          start = abs(h - w) // 2
          if h > w:
              frame = frame[start: start + w]
          else:
              frame = frame[:, start: start + h]
          return frame
      

      Ensuite, prenez le recadrage central comme cadre de la caméra, convertissez-le en niveaux de gris, normalisez -le et redimensionnez-le en 28x28. Placez-le dans la boucle while de la fonction main :

      step_5_camera.py

      def main():
          ...
          while True:
              # Capture frame-by-frame
              ret, frame = cap.read()
      
              # preprocess data
              frame = center_crop(frame)
              frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
              x = cv2.resize(frame, (28, 28))
              x = (frame - mean) / std
      

      Toujours dans la boucle while, exécutez l’inférence avec le runtime ONNX. Convertissez les sorties en un index de catégorie, puis en une lettre :

      step_5_camera.py

              ...
              x = (frame - mean) / std
      
              x = x.reshape(1, 1, 28, 28).astype(np.float32)
              y = ort_session.run(None, {'input': x})[0]
      
              index = np.argmax(y, axis=1)
              letter = index_to_letter[int(index)]
      

      Affichez la lettre prédite à l’intérieur du cadre et affichez le cadre à l’utilisateur :

      step_5_camera.py

              ...
              letter = index_to_letter[int(index)]
      
              cv2.putText(frame, letter, (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 2.0, (0, 255, 0), thickness=2)
              cv2.imshow("Sign Language Translator", frame)
      

      À la fin de la boucle while, ajoutez ce code pour vérifier si lorsque l’utilisateur frappe le caractère q il quitte bien l’application. Cette ligne arrête le programme pendant 1 milliseconde. Ajoutez ce qui suit :

      step_5_camera.py

              ...
              cv2.imshow("Sign Language Translator", frame)
      
              if cv2.waitKey(1) & 0xFF == ord('q'):
                  break
      

      Enfin, relâchez la capture et fermez toutes les fenêtres. Placez-la en dehors de la boucle while pour terminer la fonction main.

      step_5_camera.py

      ...
      
          while True:
              ...
              if cv2.waitKey(1) & 0xFF == ord('q'):
                  break
      
      
          cap.release()
          cv2.destroyAllWindows()
      

      Vérifiez que votre fichier correspond à ce qui suit ou à ce référentiel :

      step_5_camera.py

      import cv2
      import numpy as np
      import onnxruntime as ort
      
      
      def center_crop(frame):
          h, w, _ = frame.shape
          start = abs(h - w) // 2
          if h > w:
              return frame[start: start + w]
          return frame[:, start: start + h]
      
      
      def main():
          # constants
          index_to_letter = list('ABCDEFGHIKLMNOPQRSTUVWXY')
          mean = 0.485 * 255.
          std = 0.229 * 255.
      
          # create runnable session with exported model
          ort_session = ort.InferenceSession("signlanguage.onnx")
      
          cap = cv2.VideoCapture(0)
          while True:
              # Capture frame-by-frame
              ret, frame = cap.read()
      
              # preprocess data
              frame = center_crop(frame)
              frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
              x = cv2.resize(frame, (28, 28))
              x = (x - mean) / std
      
              x = x.reshape(1, 1, 28, 28).astype(np.float32)
              y = ort_session.run(None, {'input': x})[0]
      
              index = np.argmax(y, axis=1)
              letter = index_to_letter[int(index)]
      
              cv2.putText(frame, letter, (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 2.0, (0, 255, 0), thickness=2)
              cv2.imshow("Sign Language Translator", frame)
      
              if cv2.waitKey(1) & 0xFF == ord('q'):
                  break
      
          cap.release()
          cv2.destroyAllWindows()
      
      if __name__ == '__main__':
          main()
      

      Quittez votre fichier et exécutez le script.

      Une fois le script exécuté, une fenêtre apparaîtra avec votre flux de webcam en direct. La lettre de la langue des signes prédite s’affichera en haut à gauche. Levez la main et faites votre signe favori pour voir votre classeur en action. Voici quelques exemples de résultats avec la lettre L et D.

      Capture d'écran de votre programme OpenCV échantillon, pour la langue des signes « L » 
       Capture d'écran de votre programme OpenCV échantillon, pour la langue des signes « D »

      Lorsque vous réalisez les tests, notez que l’arrière-plan doit être assez clair pour que ce traducteur fonctionne. C’est une conséquence malheureuse de la propreté de l’ensemble de données. Si l’ensemble de données comprenait des images de signes de la main avec des arrière-plans divers, le réseau pourrait résister aux arrière-plans bruyants. Cependant, dans cet ensemble de données, les arrière-plans sont vierges et les mains bien centrées. Par conséquent, ce traducteur de webcam fonctionne mieux lorsque vous centrez votre main et la placez sur un fond vierge.

      Ceci conclut l’application du traducteur de la langue des signes.

      Conclusion

      Dans ce tutoriel, vous avez créé un traducteur de la langue des signes américaine à l’aide de la vision par ordinateur et d’un modèle d’apprentissage automatique. Vous avez tout particulièrement abordé de nouveaux aspects de l’entraînement d’un modèle d’apprentissage automatique, notamment l’augmentation des données pour veiller à la robustesse du modèle, les calendriers de fréquence d’apprentissage pour réduire les pertes et l’exportation de modèles d’IA à l’aide d’ONNX pour la production. Vous avez ensuite obtenu une application de vision par ordinateur en temps réel, qui traduit le langage des signes en lettres à l’aide d’un pipeline que vous avez créé. Il convient de noter vous pouvez lutter contre la fragilité du classificateur final en utilisant l’une des méthodes suivantes (ou l’ensemble d’entre elles). Pour explorer le sujet plus profondément, essayez les rubriques suivantes pour améliorer votre application :

      • Généralisation : il ne s’agit d’un sous-thème de la vision par ordinateur, mais plutôt d’un problème constant tout au long de l’apprentissage automatique. Voir Comprendre les compromis entre le biais et la variance.
      • Adaptation du domaine : supposons que votre modèle soit formé dans le domaine A (par exemple, des environnements ensoleillés). Pouvez-vous rapidement adapter le modèle au domaine B (par exemple, des environnements nuageux) ?
      • Exemples contradictoires : Supposons qu’un adversaire conçoit intentionnellement des images pour tromper votre modèle. Comment pouvez-vous concevoir de telles images ? Que pouvez-vous faire pour combattre de telles images ?



      Source link

      Comment utiliser le module des collections dans Python 3


      L’auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      Python 3 possède un certain nombre de structures de données intégrées, notamment des tuples, des dictionnaires et des listes. Les structures de données nous fournissent un moyen d’organiser et de stocker les données. Le module de collections nous aide à remplir et à manipuler les structures de données de manière efficace.

      Dans ce tutoriel, nous allons passer en revue trois classes des collections pour vous aider à travailler avec des tuples, des dictionnaires et des listes. Nous utiliserons namedtuples pour créer des tuples avec des champs nommés, defaultdict pour regrouper de manière concise les informations dans les dictionnaires, et deque pour ajouter efficacement des éléments de chaque côté d’un objet de type liste. 

      Pour ce tutoriel, nous travaillerons principalement avec un inventaire de poissons que nous devons modifier au fur et à mesure que des poissons sont ajoutés ou retirés d’un aquarium fictif.

      Conditions préalables

      Pour tirer le meilleur parti de ce tutoriel, il est recommandé de se familiariser avec le tuple, le dictionnaire et les types de données de liste que ce soit en ce qui concerne leur syntaxe que de la manière d’en extraire des données. Vous pouvez consulter ces tutoriels pour obtenir les informations de base nécessaires :

      Ajouter des champs nominatifs aux tuples

      Les tuples de Python sont une séquence d’éléments immuables, ou inchangeables, ordonnés. Les tuples sont fréquemment utilisés pour représenter des données en colonnes ; par exemple, les lignes d’un fichier CSV ou les lignes d’une base de données SQL. Un aquarium peut garder une trace de son inventaire de poissons sous la forme d’une série de tuples.

      Un tuple de poissons individuel :

      ("Sammy", "shark", "tank-a")
      

      Ce tuple est composé de trois éléments de chaîne caractères.

      Bien qu’utile à certains égards, ce tuple n’indique pas clairement ce que représente chacun de ses champs. En réalité, l’élément 0 est un nom, l’élément 1 est une espèce, et l’élément 2 est le réservoir de stockage. 

      Explication des champs de tuples de poissons :

      name species tank
      Sammy shark tank-a

      Ce tableau montre clairement que chacun des trois éléments du tuple a une signification claire.

      namedtuple du module collections vous permet d’ajouter des noms explicites à chaque élément d’un tuple pour rendre ces significations claires dans votre programme Python.

      Utilisons namedtuple pour générer une classe qui nomme clairement chaque élément du tuple de poisson :

      from collections import namedtuple
      
      Fish = namedtuple("Fish", ["name", "species", "tank"])
      

      from collections import namedtuple donne à votre programme Python l’accès à la fonction d’usine namedtuple. L’appel de la fonction namedtuple() renvoie une classe qui est liée au nom Fish. Le namedtuple() a deux arguments : le nom souhaité de notre nouvelle classe "Fish" et une liste d’éléments nommés ["name", "species", "tank"]. 

      Nous pouvons utiliser la classe Fish pour représenter le tuple de poissons de tout à l’heure :

      sammy = Fish("Sammy", "shark", "tank-a")
      
      print(sammy)
      

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

      Fish(name="Sammy", species="shark", tank='tank-a')

      sammy est instancié à l’aide de la classeFish. sammy est un tuple avec trois éléments clairement nommés. 

      Les champs de sammy sont accessibles par leur nom ou avec un index tuple traditionnel :

      print(sammy.species)
      print(sammy[1])
      

      Si nous lançons ces deux appels d’impression, nous obtiendrons le résultat suivant :

      Output

      shark shark

      L’accès à .species a la même valeur que l’accès au deuxième élément de sammy en utilisant [1]. 

      Utiliser namedtuple du module collections rend votre programme plus lisible tout en conservant les propriétés importantes d’un tuple (à savoir qu’ils sont immuables et ordonnés). 

      De plus, la fonction d’usine namedtuple ajoute plusieurs méthodes supplémentaires aux instances de Fish.

      Utilisez ._asdict() pour convertir une instance en dictionnaire :

      print(sammy._asdict())
      

      Si nous lançons print, vous verrez des résultats comme ceux qui suivent :

      Output

      {'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}

      Appeler .asdict() sur sammy renvoie un dictionnaire mettant en correspondance les noms de chacun des trois champs avec leurs valeurs correspondantes. 

      Les versions de Python plus anciennes que 3.8 pourraient produire cette ligne de manière légèrement différente. Vous pourriez, par exemple, voir un OrderedDict au lieu du simple dictionnaire présenté ici. 

      Note : Dans Python, les méthodes avec des traits de soulignement en tête sont généralement considérées comme « privées ». Les méthodes supplémentaires fournies par namedtuple (comme _asdict(), ._make(), ._replace(), etc.), sont toutefois publiques.

      Rassembler des données dans un dictionnaire

      Il est souvent utile de collecter des données dans les dictionnaires Python. defaulttdict du module de collections peut nous aider à rassembler les informations dans les dictionnaires de manière rapide et concise. 

      defaultdict ne soulève jamais une KeyError. Si une clé n’est pas présente, defaulttdict se contente d’insérer et de renvoyer une valeur de remplacement à la place : 

      from collections import defaultdict
      
      my_defaultdict = defaultdict(list)
      
      print(my_defaultdict["missing"])
      

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

      []

      defaultdict insère et renvoie une valeur de remplacement au lieu de lancer une KeyError. Dans ce cas, nous avons spécifié la valeur de remplacement sous forme de liste.

      Les dictionnaires ordinaires, en revanche, lancent une KeyError sur les clés manquantes :

      my_regular_dict = {}
      
      my_regular_dict["missing"]
      

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'missing'

      Le dictionnaire habituel my_regular_dict soulève une KeyError lorsque nous essayons d’accéder à une clé qui n’est pas présente. 

      defaulttdict se comporte différemment d’un dictionnaire ordinaire. Au lieu de soulever une KeyError sur une clé manquante, defaultdict appelle la valeur de remplacement sans argument pour créer un nouvel objet. Dans ce cas, list() pour créer une liste vide. 

      Pour continuer avec notre exemple d’aquarium fictif, disons que nous avons une liste de tuples de poissons représentant l’inventaire d’un aquarium :

      fish_inventory = [
          ("Sammy", "shark", "tank-a"),
          ("Jamie", "cuttlefish", "tank-b"),
          ("Mary", "squid", "tank-a"),
      ]
      

      Trois poissons existent dans l’aquarium – leur nom, leur espèce et leur bac de rétention sont notés dans ces trois tuples.

      Notre objectif est d’organiser notre inventaire par réservoir – nous voulons connaître la liste des poissons présents dans chaque réservoir. En d’autres termes, nous voulons un dictionnaire qui cartographie "tank-a". à ["Jamie", "Mary"] et "tank-b" à ["Jamie"].

      Nous pouvons utiliser defaulttdict pour regrouper les poissons par bassin :

      from collections import defaultdict
      
      fish_inventory = [
          ("Sammy", "shark", "tank-a"),
          ("Jamie", "cuttlefish", "tank-b"),
          ("Mary", "squid", "tank-a"),
      ]
      fish_names_by_tank = defaultdict(list)
      for name, species, tank in fish_inventory:
          fish_names_by_tank[tank].append(name)
      
      print(fish_names_by_tank)
      

      En exécutant ce code, nous obtiendrons le résultat suivant :

      Output

      defaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})

      fish_names_by_tank est déclaré comme un paramètre par défaut qui consiste par défaut à insérer list() au lieu de lancer une KeyError.  Comme cela garantit que chaque clé dansfish_names_by_tank pointera sur une liste, nous pouvons librement appeler .append() pour ajouter des noms à la liste de chaque réservoir.

      defaultdict vous aide ici car il réduit le risque d’erreurs inattendues de KeyErrors. Réduire lesKeyErrors inattendues signifie que votre programme peut être écrit plus clairement et avec moins de lignes. Plus concrètement, l’idiome defaultdict permet d’éviter d’instancier manuellement une liste vide pour chaque réservoir.

      Sans défaultdict, le corps de la boucle for aurait pu ressembler davantage à ceci :

      More Verbose Example Without defaultdict

      ...
      
      fish_names_by_tank = {}
      for name, species, tank in fish_inventory:
          if tank not in fish_names_by_tank:
            fish_names_by_tank[tank] = []
          fish_names_by_tank[tank].append(name)
      

      L’utilisation d’un simple dictionnaire (au lieu d’un defaultdict) signifie que le corps de la boucle for doit toujours vérifier l’existence du tank donné dans fish_names_by_tank. Ce n’est qu’après avoir vérifié que tank est déjà présent dans fish_names_by_tank, ou vient d’être initialisé avec un [], qu’on peut ajouter le nom du poisson.

      defaultdict peut aider à réduire le nombre de codes passe-partout lors du remplissage des dictionnaires car il ne provoque jamais de KeyError.

      Utilisation de deque pour ajouter efficacement des éléments de chaque côté d’une collection

      Les listes Python sont une séquence d’éléments ordonnée, mutable ou modifiable. Python peut ajouter des listes en temps constant (la longueur de la liste n’a aucun effet sur le temps qu’il faut pour l’ajout), mais l’insertion au début d’une liste peut être plus lente (le temps nécessaire augmente à mesure que la liste s’agrandit).

      En termes de notation Big O, l’ajout à une liste est une opération O(1) à temps constant. L’insertion au début d’une liste, en revanche, est plus lente avec O(n) performance. 

      Note : Les informaticiens mesurent souvent la performance des procédures en utilisant ce qu’on appelle la notation « Big O ». Lorsque la taille d’une entrée n’a aucun effet sur le temps nécessaire pour exécuter une procédure, on dit qu’elle se déroule en temps constant ou O(1) (“Big O of 1”). Comme vous l’avez appris plus haut, Python peut s’ajouter aux listes à performance temporelle constante, autrement dit O(1). 

      Parfois, la taille d’une entrée a une incidence directe sur le temps nécessaire à l’exécution d’une procédure. L’insertion au début d’une liste Python, par exemple, est d’autant plus lente qu’il y a plus d’éléments dans la liste. La notation Big O utilise la lettre n pour représenter la taille de l’entrée. Cela signifie que l’ajout d’éléments au début d’une liste Python se fait en « temps linéaire » ou O(n) (“Big O of n”). 

      En général, les procédures O(1) sont plus rapides que les procédures O(n).

      On peut l’insérer au début d’une liste Python :

      favorite_fish_list = ["Sammy", "Jamie", "Mary"]
      
      # O(n) performance
      favorite_fish_list.insert(0, "Alice")
      
      print(favorite_fish_list)
      

      Si nous exécutons ce qui suit, nous obtiendrons le résultat suivant :

      Output

      ['Alice', 'Sammy', 'Jamie', 'Mary']

      La méthode .insert (index, objet) sur list nous permet d’insérer "Alice" au début de favorite_fish_list. Toutefois, il est à noter que l’insertion au début d’une liste a O(n) performance. Comme la durée de favorite_fish_list augmente, le temps nécessaire pour insérer un poisson au début de la liste augmentera proportionnellement et prendra de plus en plus de temps.

      deque (prononcé « deck ») du module collections est un objet de type liste qui nous permet d’insérer des éléments au début ou à la fin d’une séquence avec une performance à temps constant (O(1)).

      Insérez un élément au début d’un deque: 

      from collections import deque
      
      favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"])
      
      # O(1) performance
      favorite_fish_deque.appendleft("Alice")
      
      print(favorite_fish_deque)
      

      En exécutant ce code, nous obtiendrons le résultat suivant :

      Output

      deque(['Alice', 'Sammy', 'Jamie', 'Mary'])

      Nous pouvons instancier un deque en utilisant un ensemble d’éléments préexistants, en l’occurrence une liste de trois noms de poissons favoris. L’appel de la méthode appendleft de favorite_fish_deque nous permet d’insérer un élément au début de notre collection avec la performance O (1). O(1) performance signifie que le temps nécessaire pour ajouter un élément au début de favorite_fish_deque n’augmentera pas, même si favorite_fish_deque a des milliers ou des millions d’éléments. 

      Note : Bien que deque ajoute des entrées au début d’une séquence plus efficacement qu’une liste, deque n’effectue pas toutes ses opérations plus efficacement qu’une liste. Par exemple, l’accès à un article aléatoire dans un deque a une performance O(n), mais l’accès à un élément aléatoire d’une liste a une performance O(1). Utilisez deque lorsqu’il est important d’insérer ou de retirer rapidement des éléments de chaque côté de votre collection. Une comparaison complète des performances temporelles est disponible sur le wiki de Python.

      Conclusion

      Le module des collections est une partie puissante de la bibliothèque standard Python qui vous permet de travailler avec des données de manière concise et efficace.  Ce tutoriel couvrait trois des cours fournis par le module des collections comprenant namedtuple, defaultdict, et deque. 

      D’ici, vous pouvez utiliser la documentation du module de collecte pour en savoir plus sur les autres classes et utilités disponibles. Pour en savoir plus sur Python en général, vous pouvez lire notre Série de tutoriels Comment coder en Python 3. 



      Source link