Ich bezog mich auf die folgende offizielle Referenz. Neural Networks -- PyTorch Tutorials 1.4.0 documentation
Das allgemeine Verfahren zum Trainieren eines neuronalen Netzwerks ist wie folgt. ** 1. Daten vorbereiten (Trainingsdaten / Testdaten). ** ** ** ** 2. Definieren Sie ein neuronales Netzwerk mit trainierbaren Parametern. (Definieren Sie das Netzwerk) ** ** 3. Berechnen Sie die Verlustfunktion, wenn Trainingsdaten in das Netzwerk eingegeben werden. (Verlustfunktion) ** ** 4. Berechnen Sie die Steigung der Verlustfunktion in Bezug auf Netzwerkparameter. (Rückwärts) ** ** 5. Aktualisieren Sie die Parameter basierend auf dem Gradienten der Verlustfunktion. (Optimieren) ** ** 6. Trainiere, indem du 3 bis 6 viele Male wiederholst. ** ** **
Bauen Sie ein neuronales Netzwerk gemäß dem Verfahren auf.
Verwenden Sie für die zum Trainieren des neuronalen Netzwerks verwendeten Daten die bereits im Paket vorbereiteten Daten oder die von Ihnen selbst vorbereiteten Daten.
Wenn Sie das bereits vorbereitete verwenden möchten, können Sie das Paket "torchvision" verwenden. Zusätzlich zu den Datensätzen "torchvision.datasets" wie MNIST und CIFAR10, die häufig beim maschinellen Lernen verwendet werden, werden das allgemeine maschinelle Lernmodell "torchvision.models" und das Modul "torchvision.transforms" für die Datenverarbeitung vorbereitet. Wurde getan. Weitere Informationen finden Sie in der offiziellen Dokumentation-> torchvision.
Bereiten Sie bei der Durchführung des Trainings ein Datenfeld mit dem Namen "torch.utils.data.DataLoader" vor. DataLoader
ist eine Sammlung von Datensätzen, die Eingabedaten und ihre Beschriftungen nach Stapelgröße kombinieren.
Das Vorbereitungsverfahren ist wie folgt. ** (1) Bereiten Sie "Transformationen" vor, um die Daten vorzuverarbeiten. ** ** ** ** (2) Bereiten Sie "Dataset" vor, indem Sie die Dataset-Klasse mit "transforms" als Argument instanziieren. ** ** ** ** (3) Bereiten Sie "DataLoader" vor, indem Sie die DataLoader-Klasse mit "Dataset" als Argument instanziieren. ** ** ** ** (4) Verwenden Sie zum Zeitpunkt des Trainings "DataLoader", um Trainingsdaten und Etiketten in Blöcken mit Stapelgröße zu erfassen. ** ** **
Ein neuronales Netzwerk kann unter Verwendung des Pakets "torch.nn" aufgebaut werden.
nn
führt die Modelldefinition und -differenzierung durch, indem die automatische Differenzierung autograd
verwendet wird.
nn.Module
hat verschiedene Schichten des neuronalen Netzwerks und Forward (Input)
-Methode.
Daher sollte beim Aufbau eines neuen Netzwerks die Klasse "nn.Module" vererbt werden.
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)
#)
Definieren Sie die vom Netzwerk gehaltene Ebene mit der Methode __init__ ()
.
Die am häufigsten verwendeten Ebenen wie "Linear" und "Conv2d" sind in "torch.nn" definiert.
Weitere Informationen finden Sie in der offiziellen Dokumentation-> torch.nn.
In ähnlicher Weise wird eine Verarbeitung wie "relu" und "max_pool2d" in "torch.nn.functional" definiert. Es kann aufgerufen und entsprechend verwendet werden, wenn eine Verarbeitung erforderlich ist. Weitere Informationen finden Sie in der offiziellen Dokumentation-> torch.nn.functional.
Definieren Sie die Vorwärtsausbreitung des Netzwerks mit der Methode "forward ()". Die zu übergebenden Schichten und die Verarbeitung, die ausgeführt werden soll, bis die Eingabe "x" ausgegeben wird, werden der Reihe nach definiert.
Es ist nicht erforderlich, "backward ()" zu definieren, dh die Rückausbreitung des Netzwerks. Durch Definieren von "forward ()" und Verwenden von "autograd". Die Rückausbreitung wird automatisch erhalten.
Trainierbare Parameter können mit net.parameters ()
erhalten werden.
Da der Gewichtungsparameter und der Vorspannungsparameter getrennt erfasst werden, wird eine Liste von Parametern mit einer Länge von $ \ mal $ 2 erhalten, was der Anzahl der definierten Schichten entspricht.
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>)
Geben Sie für dieses Netzwerk die entsprechenden Daten $ 32 \ times 32 $ ein.
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>)
Die eingegebene Zufallszahl wird über die Ebene mit den Anfangsparametern ausgegeben.
Sie können den Gradienten aller Parameter mit der Methode zero_grad ()
auf Null setzen. Es wird empfohlen, "zero_grad ()" auszuführen, bevor "backward ()" ausgeführt wird, um unerwartete Parameteraktualisierungen zu vermeiden.
torch.nn
setzt voraus, dass ein Mini-Batch eingegeben wird. Zum Beispiel muss nn.Conv2d
einen 4-dimensionalen Tensor ($ \ rm {nSamples} \ mal nChannels \ mal Höhe \ mal Breite $) als Eingabe haben.
Häufig verwendete Verlustfunktionen wie "MSELoss ()" und "CrossEntropyLoss ()" sind im "nn" -Paket enthalten. Im Folgenden wird "MSELoss" unter Verwendung des Ausgabewerts berechnet, wenn eine Zufallszahl eingegeben wird, und einer Zufallszahlenfolge derselben Größe.
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>)
Wenn Sie die Vorwärtsausbreitung bisher verfolgen,
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss
Dies kann durch Betrachten des Attributs grad_fn
bestätigt werden.
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>
Ein Gradient der Verlustfunktion ist erforderlich, um eine Fehlerrückausbreitung für die Parameteraktualisierung durchzuführen. Wenn Sie in PyTorch "loss.backward ()" für die Verlustfunktion "loss" ausführen, wird der Gradient automatisch berechnet. Um die Anhäufung von Verläufen zu vermeiden, wird empfohlen, "net.zero_grad ()" für jede Iteration während des Trainings auszuführen, um die Verläufe zu beseitigen.
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])
Parameteraktualisierung (Optimierung) kann aus torch.optim
zitiert werden.
Hier verwenden wir die stochastische Gradientenabstiegsmethode (SGD), die durch die folgende Gleichung definiert ist.
Einzelheiten finden Sie in der offiziellen Dokumentation-> 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
Das Netzwerktraining wird durchgeführt, indem die obigen Schritte 3 bis 6 wiederholt werden.
Als Beispiel trainieren wir ein neuronales Netzwerk, das Bilder mit CIFAR10 klassifiziert. Ich habe auf die offizielle Referenz unten verwiesen. Training a Classifier -- PyTorch Tutorials 1.4.0 documentation
Erfassen und standardisieren Sie die in "torchvision.datasets" bereitgestellten CIFAR10-Daten. Da die Daten im Fackelvisionsdatensatz ein PILImage mit Werten im Bereich [0,1] sind, werden sie hier auf Tensor mit Werten im Bereich [-1,1] standardisiert.
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')
Lassen Sie uns die vorbereiteten Daten anzeigen.
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]
Als nächstes wird ein Netzwerk zum Klassifizieren von Bildern aufgebaut.
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()
Definieren Sie nach dem Aufbau des Netzwerks die Verlustfunktion und die Optimierungsmethode.
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
Beginnen Sie nach dem Definieren des Netzwerks, der Verlustfunktion und der Optimierungsmethode mit dem Training anhand der Trainingsdaten.
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
Hier wird das Training mit allen 12000 Trainingsdaten zweimal durchgeführt. Mit zunehmender Datenmenge, die für das Training verwendet wird, wird der Verlust der Verlustfunktion kleiner, sodass beobachtet werden kann, wie der Lernfortschritt verläuft. (Es scheint, dass das Lernen noch nicht abgeschlossen ist, aber dieses Mal werden wir hier anhalten und weitermachen.)
Die Parameter des trainierten Modells können mit torch.save ()
gespeichert werden.
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
Wenden Sie ein geschultes Netzwerk auf die Testdaten an. Überprüfen Sie zunächst den Inhalt der Testdaten.
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]
Lesen Sie dann die gespeicherten Netzwerkparameter. Geben Sie danach die Testdaten in das Lesemodell ein und zeigen Sie das Klassifizierungsergebnis an.
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
Das dritte Bild wird als Flugzeug statt als Schiff falsch eingeschätzt, aber die anderen drei sind korrekt klassifiziert.
Berechnen wir die richtige Antwortrate für alle 10000 Testdaten.
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 %
Die richtige Antwortrate beträgt 52%, was als Bildklassifizierer nicht sehr genau ist.
Versuchen Sie als Nächstes, die richtige Antwortrate für jede Art von Klassifizierung zu erhalten.
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 %
Daraus können wir erkennen, dass wir nicht gut darin sind, Katzen zu klassifizieren, aber wir sind gut darin, Schiffe zu klassifizieren.
Beim Training auf der GPU muss das CUDA-Gerät mit "Gerät" angegeben werden.
Überprüfen Sie zunächst, ob die GPU verfügbar ist. Wenn im folgenden Code cuda: 0
angezeigt wird, ist die GPU verfügbar.
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
Sie können Netzwerke und Daten mit .to (Gerät)
auf die GPU verschieben.
Vergessen Sie beim Training nicht, die Daten für jede Iteration auf die GPU zu verschieben.
net.to(device)
inputs, labels = data[0].to(device), data[1].to(device)
Schließlich ist das obige Verfahren in einem Code zusammengefasst.
# 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