[PYTHON] Bases de PyTorch (2) -Comment créer un réseau de neurones-

Réseau neuronal avec PyTorch

J'ai fait référence à la référence officielle suivante. Neural Networks -- PyTorch Tutorials 1.4.0 documentation

La procédure générale pour la formation d'un réseau neuronal est la suivante. ** 1. Préparez les données (données d'entraînement / données de test). ** ** ** 2. Définissez un réseau neuronal avec des paramètres entraînables. (Définissez le réseau) ** ** 3. Calculez la fonction de perte lorsque les données d'apprentissage sont entrées dans le réseau. (Fonction de perte) ** ** 4. Calculez la pente de la fonction de perte par rapport aux paramètres du réseau. (En arrière) ** ** 5. Mettez à jour les paramètres en fonction du gradient de la fonction de perte. (Optimiser) ** ** 6. Entraînez-vous en répétant 3 à 6 fois plusieurs fois. ** **

Construisez un réseau neuronal selon la procédure.

1. Préparation des données

Pour les données utilisées pour entraîner le réseau neuronal, utilisez les données déjà préparées dans le package ou utilisez les données préparées par vous-même.

Si vous voulez utiliser celui qui est déjà préparé, il est pratique d'utiliser le paquet torchvision. Outre les ensembles de données «torchvision.datasets» tels que MNIST et CIFAR10 qui sont souvent utilisés dans l'apprentissage automatique, le modèle d'apprentissage automatique à usage général «torchvision.models» et le module «torchvision.transforms» pour le traitement des données sont préparés. A été fait. Voir la documentation officielle pour plus de détails-> torchvision

Lors de l'exécution de la formation, préparez une boîte de données appelée torch.utils.data.DataLoader. DataLoader est une collection d'ensembles de données qui combinent les données d'entrée et leurs étiquettes par taille de lot.

La procédure de préparation est la suivante. ** (1) Préparez les transformations pour prétraiter les données. ** ** ** (2) Préparez Dataset en instanciant la classe Dataset avec transforms comme argument. ** ** ** (3) Préparez DataLoader en instanciant la classe DataLoader avec Dataset comme argument. ** ** ** (4) Au moment de la formation, utilisez DataLoader pour acquérir des données de formation et des étiquettes par blocs de taille de lot. ** **

2. Définition du réseau neuronal

Un réseau neuronal peut être construit en utilisant le package torch.nn. nn exécute la définition et la différenciation du modèle en utilisant la différenciation automatique ʻautograd`.

«nn.Module» a plusieurs couches de réseau neuronal et une méthode «forward (input)». Par conséquent, lors de la construction d'un nouveau réseau, la classe nn.Module doit être héritée.

import torch
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 1 input image channel, 6 output channels, 3x3 square convolution
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 3)
        self.conv2 = nn.Conv2d(6, 16, 3)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 6 * 6, 120)  # 6*6 from image dimension
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # Max pooling over a (2, 2) window
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

net = Net()
print(net)

# ---Output---
#Net(
#  (conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))
#  (conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
#  (fc1): Linear(in_features=576, out_features=120, bias=True)
#  (fc2): Linear(in_features=120, out_features=84, bias=True)
#  (fc3): Linear(in_features=84, out_features=10, bias=True)
#)

Définissez la couche détenue par le réseau avec la méthode __init__ (). Les couches les plus couramment utilisées telles que "Linear" et "Conv2d" sont définies dans "torch.nn". Voir la documentation officielle pour plus de détails-> torch.nn

De même, des traitements tels que «relu» et «max_pool2d» sont définis dans «torch.nn.functional». Il peut être appelé et utilisé selon les besoins lorsqu'un traitement est requis. Voir la documentation officielle pour plus de détails-> torch.nn.functional

Définissez la propagation avant du réseau avec la méthode forward (). Les couches à transmettre et le traitement à exécuter jusqu'à ce que l'entrée «x» soit sortie sont définis dans l'ordre.

Il n'est pas nécessaire de définir «backward ()», qui est la rétro-propagation du réseau. En définissant forward () et en utilisant ʻautograd`. La rétro-propagation est obtenue automatiquement.

Les paramètres entraînables peuvent être obtenus avec net.parameters (). Comme le paramètre de pondération et le paramètre de biais sont acquis séparément, une liste de paramètres d'une longueur de $ \ fois $ 2, qui est le nombre de couches définies, est obtenue.

params = list(net.parameters())
print(len(params))
print(params[0].size())   # conv1's weight
print(params[1].size())   # conv1's bias
print(params[0][0,:,:,:]) # conv1's weights on the first dimension

# ---Output---
#10
#torch.Size([6, 1, 3, 3])
#torch.Size([6])
#tensor([[[-0.0146, -0.0219,  0.0491],
#         [-0.3047, -0.0137,  0.0954],
#         [-0.2612, -0.2972, -0.2798]]], grad_fn=<SliceBackward>)

Entrez les données appropriées de 32 $ \ fois 32 $ pour ce réseau.

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)

# ---Output---
#tensor([[-0.0703,  0.0575, -0.0679, -0.1168, -0.1093,  0.0815, -0.0085,  0.0408,
#          0.1275,  0.0472]], grad_fn=<AddmmBackward>)

Le nombre aléatoire d'entrée est émis via la couche avec les paramètres initiaux.

Vous pouvez rendre nul le gradient de tous les paramètres avec la méthode zero_grad (). Il est recommandé d'exécuter zero_grad () avant d'exécuter backward () pour éviter des mises à jour inattendues des paramètres.

torch.nn suppose qu'un mini-lot est entré. Par exemple, nn.Conv2d doit préparer un Tenseur à 4 dimensions ($ \ rm {nSamples} \ fois nChannels \ fois Hauteur \ fois Largeur $) comme entrée.

3. Fonction de perte

Les fonctions de perte couramment utilisées telles que «MSELoss ()» et «CrossEntropyLoss ()» sont fournies dans le paquet «nn». Dans ce qui suit, «MSELoss» est calculé en utilisant la valeur de sortie lorsqu'un nombre aléatoire est entré et une séquence de nombres aléatoires de la même taille.

input = torch.randn(1, 1, 32, 32)
output = net(input)
target = torch.randn(10)    # a dummy target, for example
target = target.view(1,-1)  # make it the same shape as output
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)

# ---Output---
#tensor(0.5322, grad_fn=<MseLossBackward>)

Si vous suivez la propagation vers l'avant jusqu'à présent,

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d 
      -> view -> linear -> relu -> linear -> relu -> linear 
      -> MSELoss 
      -> loss

Cela peut être confirmé en regardant l'attribut grad_fn.

print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU

# ---Output---
#<MseLossBackward object at 0x7f5008a1c4e0>
#<AddmmBackward object at 0x7f5008a1c5c0>
#<AccumulateGrad object at 0x7f5008a1c4e0>

4. Calcul du gradient

Un gradient de la fonction de perte est nécessaire pour effectuer une rétropropagation d'erreur pour la mise à jour des paramètres. Dans PyTorch, si vous exécutez loss.backward () pour la fonction de perte loss, le gradient sera calculé automatiquement. Afin d'éviter l'accumulation de dégradés, il est recommandé d'exécuter net.zero_grad () à chaque itération pendant l'entraînement pour éliminer les dégradés.

net.zero_grad()     # zeroes the gradient buffers of all parameters
print("conv1.bias.grad before backward")
print(net.conv1.bias.grad)

loss.backward()
print("conv1.bias.grad after backward")
print(net.conv1.bias.grad)

# ---Output---
#conv1.bias.grad before backward
#tensor([0., 0., 0., 0., 0., 0.])
#conv1.bias.grad after backward
#tensor([ 0.0072, -0.0051, -0.0008, -0.0017,  0.0043, -0.0030])

5. Mettre à jour les paramètres

La mise à jour des paramètres (optimisation) peut être citée à partir de torch.optim. Ici, nous utiliserons la méthode de descente de gradient stochastique (SGD) définie par l'équation suivante. Voir la documentation officielle pour plus de détails-> torch.optim

weight -> weight - learning_rate * gradient
import torch.optim as optim

# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)

# in your training loop:
optimizer.zero_grad()   # zero the gradient buffers
output = net(input)
loss = criterion(output,target)
loss.backward()
optimizer.step()        # do the update

6. Formation

La formation réseau est effectuée en répétant les étapes 3 à 6 ci-dessus.

Implémentation à l'aide de CIFAR10

À titre d'exemple, nous formons un réseau de neurones qui classe les images à l'aide de CIFAR10. Je me suis référé à la référence officielle ci-dessous. Training a Classifier -- PyTorch Tutorials 1.4.0 documentation

Préparation des données

Acquérir et standardiser les données CIFAR10 fournies dans torchvision.datasets. Étant donné que les données du jeu de données TorchVision sont une image PIL avec des valeurs dans la plage [0,1], elles sont ici normalisées en Tensor avec des valeurs dans la plage [-1,1].

import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

Affiche les données préparées.

import matplotlib.pyplot as plt
import numpy as np

def imshow(img):
    img = img/2 + 0.5 # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1,2,0)))
    plt.show()
    
# get some random training imges
dataiter = iter(trainloader)
images, labels = dataiter.next()

# show images
imshow(torchvision.utils.make_grid(images))

# print labels
print(''.join('%5s' % classes[labels[j]] for j in range(4)))

[Output] image.png

Construire un réseau

Ensuite, un réseau de classification des images est construit.

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(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

net = Net()

Définition de la fonction de perte et méthode d'optimisation

Une fois le réseau construit, définissez la fonction de perte et la méthode d'optimisation.

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

Entraînement

Après avoir défini le réseau, la fonction de perte et la méthode d'optimisation, commencez l'entraînement à l'aide des données d'entraînement.

for epoch in range(2): # loop over the dataset multiple times
    
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data
        
        # zero the parameter gradients
        optimizer.zero_grad()
        
        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs,labels)
        loss.backward()
        optimizer.step()
        
        # print statistics
        running_loss += loss.item()
        if i%2000==1999: # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' % (epoch+1, i+1, running_loss/2000))
            running_loss = 0.0
            
print('Finished Training')

# ---Output---
#[1,  2000] loss: 2.149
#[1,  4000] loss: 1.832
#[1,  6000] loss: 1.651
#[1,  8000] loss: 1.573
#[1, 10000] loss: 1.514
#[1, 12000] loss: 1.458
#[2,  2000] loss: 1.420
#[2,  4000] loss: 1.371
#[2,  6000] loss: 1.348
#[2,  8000] loss: 1.333
#[2, 10000] loss: 1.326
#[2, 12000] loss: 1.293
#Finished Training

Ici, l'entraînement utilisant les 12000 données d'entraînement est effectué deux fois. À mesure que la quantité de données utilisées pour la formation augmente, la perte de fonction de perte devient plus petite, il est donc possible d'observer la progression de l'apprentissage. (Il semble que l'apprentissage ne soit pas encore terminé, mais cette fois, nous allons nous arrêter ici et continuer.)

Enregistrer les paramètres du modèle

Les paramètres du modèle entraîné peuvent être enregistrés avec torch.save ().

PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

Appliquer aux données de test

Appliquez un réseau formé aux données de test. Vérifiez d'abord le contenu des données de test.

dataiter = iter(testloader)
images, labels = dataiter.next()

imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

[Output] image.png

Ensuite, lisez les paramètres réseau enregistrés. Après cela, entrez les données de test dans le modèle de lecture et affichez le résultat de la classification.

net = Net()
net.load_state_dict(torch.load(PATH))
# ---Output---
# <All keys matched successfully>

outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))
# ---Output---
# Predicted:    cat  ship plane plane

La troisième image est mal jugée comme avion au lieu de navire, mais les trois autres sont correctement classées.

Calculons le taux de réponse correct pour toutes les 10000 données de test.

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
        
print('Accuracy of the network on the 10000 test images: %d %%' % (100*correct/total))
# ---Output---
# Accuracy of the network on the 10000 test images: 52 %

Le taux de réponse correct est de 52%, ce qui n'est pas très précis en tant que classificateur d'images.

Ensuite, essayez d'obtenir le taux de réponse correct pour chaque type de classification.

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))

with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs,1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1
            
for i in range(10):
    print('Accuracy of %5s : %2d %%' % ( classes[i], 100*class_correct[i]/class_total[i]))

# ---Output---
# Accuracy of plane : 61 %
# Accuracy of   car : 61 %
# Accuracy of  bird : 52 %
# Accuracy of   cat : 26 %
# Accuracy of  deer : 34 %
# Accuracy of   dog : 51 %
# Accuracy of  frog : 67 %
# Accuracy of horse : 43 %
# Accuracy of  ship : 76 %
# Accuracy of truck : 50 %

De là, nous pouvons voir que nous ne sommes pas bons pour classer les chats, mais nous sommes bons pour classer les navires.

Lors de l'utilisation du GPU

Lors de la formation sur le GPU, il est nécessaire de spécifier le périphérique CUDA avec périphérique. Tout d'abord, vérifiez si le GPU est disponible. Si cuda: 0 est affiché dans le code ci-dessous, le GPU est disponible.

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Assuming that we are on a CUDA machine, this should print a CUDA device:
print(device)

# ---Output---
# cuda:0

Vous pouvez déplacer des réseaux et des données sur le GPU avec .to (périphérique). Lors de l'entraînement, n'oubliez pas de déplacer les données vers le GPU pour chaque itération.

net.to(device)
inputs, labels = data[0].to(device), data[1].to(device)

Résumé

Enfin, la procédure ci-dessus est résumée dans un code.

# import packages -------------------------------
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

# prepare data ----------------------------------
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

# define a network ------------------------------
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(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

net = Net()

# define loss function and optimizer -------------
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

# start training ---------------------------------
for epoch in range(2): # loop over the dataset multiple times
    
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data
        
        # zero the parameter gradients
        optimizer.zero_grad()
        
        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs,labels)
        loss.backward()
        optimizer.step()
        
        # print statistics
        running_loss += loss.item()
        if i%2000==1999: # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' % (epoch+1, i+1, running_loss/2000))
            running_loss = 0.0
            
print('Finished Training')

# check on test data ----------------------------
correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
        
print('Accuracy of the network on the 10000 test images: %d %%' % (100*correct/total))

Recommended Posts

Bases de PyTorch (2) -Comment créer un réseau de neurones-
Bases de PyTorch (1) -Comment utiliser Tensor-
Comment faire une traduction japonais-anglais
Implémentation d'un réseau de neurones à deux couches 2
Comment créer un robot - Avancé
Comment créer une fonction récursive
[Blender] Comment créer un plug-in Blender
Comment créer un robot - Basic
[Python] Comment créer une liste de chaînes de caractères caractère par caractère
[Python] Comment rendre une classe itérable
Faisons un noyau jupyter
Comment créer un indicateur personnalisé Backtrader
Comment créer un plan de site Pelican
Comment dessiner facilement la structure d'un réseau de neurones sur Google Colaboratory à l'aide de "convnet-tiroir"
[Python] Comment créer une matrice de motifs répétitifs (repmat / tile)
[NNabla] Comment supprimer le niveau intermédiaire d'un réseau prédéfini
J'ai essayé d'améliorer la précision de mon propre réseau neuronal
Comment créer un système de dialogue dédié aux débutants
Comment calculer la volatilité d'une marque
Visualisez la couche interne du réseau neuronal
Comment créer un dictionnaire avec une structure hiérarchique.
Comment créer un plug-in QGIS (génération de package)
J'ai lu "Comment créer un laboratoire de piratage"
[PyTorch] Exemple ⑧ ~ Comment créer un modèle complexe ~
Explication de base de Lark (faites un gars comme une coquille avec Python, Lark)
Bases des programmes réseau?
[NNabla] Comment obtenir la sortie (variable) de la couche intermédiaire du réseau construit
Comment faire un Raspberry Pi qui parle les tweets d'un utilisateur spécifié
Une nouvelle forme d'application qui fonctionne avec GitHub: Comment créer des applications GitHub
Remarques sur l'utilisation d'AIST Spacon ABCI
Entraînez les données MNIST avec PyTorch en utilisant un réseau neuronal
Comment écrire un type liste / dictionnaire de Python3
L'histoire de la création d'un réseau neuronal de génération musicale
Comment créer un package Python à l'aide de VS Code
Implémentation d'un réseau de neurones convolutifs utilisant uniquement Numpy
L'explication la plus simple au monde sur la création de LINE BOT (1) [Account preparation]
[NNabla] Comment ajouter une nouvelle couche entre les couches intermédiaires d'un réseau pré-construit
Implémenter un réseau neuronal à 3 couches
Comment appeler une fonction
Comment pirater un terminal
Tutoriel sur le réseau neuronal (CNN) de Pytorch 1.3.1.
J'ai essayé de mettre en œuvre le modèle de base du réseau neuronal récurrent
Comment créer un BOT Cisco Webex Teams à l'aide de Flask
[Ubuntu] Comment supprimer tout le contenu du répertoire
Essayez de créer un réseau de neurones / d'apprentissage en profondeur avec scratch
Comment mélanger une partie de la liste Python (au hasard.shuffle)
Comment créer un jeu d'action multijoueur en ligne avec Slack
Comment créer un laboratoire de piratage - Kali Linux (2020.1) VirtualBox 64 bits Partie 2-
Comment créer un laboratoire de piratage - Kali Linux (2020.1) VirtualBox 64-bit edition -
Comment créer un package Python (écrit pour un stagiaire)
Comment faire un simple jeu Flappy Bird avec Pygame
Comment afficher une liste des versions installables avec pyenv
Comment enregistrer un package dans PyPI (à partir de septembre 2017)
Comment obtenir une liste d'exceptions intégrées pour python
Comment trouver le coefficient de mise à l'échelle d'une ondelette bipolaire
Les bases de l'apprentissage Python ~ Comment générer (afficher) une chaîne de caractères ~
Construction d'un réseau neuronal qui reproduit XOR par Z3
Comment obtenir une liste de liens à partir d'une page de wikipedia
Comment connecter le contenu de la liste dans une chaîne de caractères