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.
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. ** **
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.
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>
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])
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
La formation réseau est effectuée en répétant les étapes 3 à 6 ci-dessus.
À 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
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]
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()
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)
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.)
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)
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]
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 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)
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