Ich mache ein CNN mit Pytorch für eine Aufgabe, aber es wird nicht lernen und die Genauigkeit verbessern. Ich habe eine Version erstellt, die mit dem MNIST-Datensatz arbeitet, damit ich sie hier veröffentlichen kann. Ich suche nur nach einer Antwort, warum es nicht funktioniert. Die Architektur ist in Ordnung, ich habe sie in Keras implementiert und hatte nach 3 Epochen eine Genauigkeit von über 92%. Hinweis: Ich habe den MNIST in 60x60-Bilder umgeformt, da die Bilder in meinem "echten" Problem so sind.
import numpy as np
from PIL import Image
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torch.autograd import Variable
from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
def resize(pics):
pictures = []
for image in pics:
image = Image.fromarray(image).resize((dim, dim))
image = np.array(image)
pictures.append(image)
return np.array(pictures)
dim = 60
x_train, x_test = resize(x_train), resize(x_test) # because my real problem is in 60x60
x_train = x_train.reshape(-1, 1, dim, dim).astype('float32') / 255
x_test = x_test.reshape(-1, 1, dim, dim).astype('float32') / 255
y_train, y_test = y_train.astype('float32'), y_test.astype('float32')
if torch.cuda.is_available():
x_train = torch.from_numpy(x_train)[:10_000]
x_test = torch.from_numpy(x_test)[:4_000]
y_train = torch.from_numpy(y_train)[:10_000]
y_test = torch.from_numpy(y_test)[:4_000]
class ConvNet(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 32, 3)
self.conv2 = nn.Conv2d(32, 64, 3)
self.conv3 = nn.Conv2d(64, 128, 3)
self.fc1 = nn.Linear(5*5*128, 1024)
self.fc2 = nn.Linear(1024, 2048)
self.fc3 = nn.Linear(2048, 1)
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv3(x)), (2, 2))
x = x.view(x.size(0), -1)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.dropout(x, 0.5)
x = torch.sigmoid(self.fc3(x))
return x
net = ConvNet()
optimizer = optim.Adam(net.parameters(), lr=0.03)
loss_function = nn.BCELoss()
class FaceTrain:
def __init__(self):
self.len = x_train.shape[0]
self.x_train = x_train
self.y_train = y_train
def __getitem__(self, index):
return x_train[index], y_train[index].unsqueeze(0)
def __len__(self):
return self.len
class FaceTest:
def __init__(self):
self.len = x_test.shape[0]
self.x_test = x_test
self.y_test = y_test
def __getitem__(self, index):
return x_test[index], y_test[index].unsqueeze(0)
def __len__(self):
return self.len
train = FaceTrain()
test = FaceTest()
train_loader = DataLoader(dataset=train, batch_size=64, shuffle=True)
test_loader = DataLoader(dataset=test, batch_size=64, shuffle=True)
epochs = 10
steps = 0
train_losses, test_losses = [], []
for e in range(epochs):
running_loss = 0
for images, labels in train_loader:
optimizer.zero_grad()
log_ps = net(images)
loss = loss_function(log_ps, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
else:
test_loss = 0
accuracy = 0
with torch.no_grad():
for images, labels in test_loader:
log_ps = net(images)
test_loss += loss_function(log_ps, labels)
ps = torch.exp(log_ps)
top_p, top_class = ps.topk(1, dim=1)
equals = top_class.type('torch.LongTensor') == labels.type(torch.LongTensor).view(*top_class.shape)
accuracy += torch.mean(equals.type('torch.FloatTensor'))
train_losses.append(running_loss/len(train_loader))
test_losses.append(test_loss/len(test_loader))
print("[Epoch: {}/{}] ".format(e+1, epochs),
"[Training Loss: {:.3f}] ".format(running_loss/len(train_loader)),
"[Test Loss: {:.3f}] ".format(test_loss/len(test_loader)),
"[Test Accuracy: {:.3f}]".format(accuracy/len(test_loader)))
python
conv-neural-network
pytorch
Nicolas Gervais
quelle
quelle
comp.ai.neural-nets
FAQs finden Sie einige gute Vorschläge, wo Sie suchen müssen, wenn Ihr neuronales Netz nicht lernt. Ich würde empfehlen, dort zu beginnen.nn.CrossEntropyLoss
für das Klassifizierungsproblem einer einzelnen Klasse verwenden.nn.CrossEntropyLoss
Wendet sowohl Softmax als auch NLLLoss als eine einzige Operation an, also nicht zuerst Softmax.nn.MSELoss
aber dann müssen Sie entweder die Ziele so anpassen, dass sie in den Bereich der Sigmoid-Ausgabe fallen, oder nicht. t Sigmoid nach der letzten Schicht auftragen.RuntimeError: multi-target not supported
.Antworten:
Zuerst die Hauptprobleme ...
1. Das Hauptproblem bei diesem Code ist, dass Sie die falsche Ausgabeform und die falsche Verlustfunktion für die Klassifizierung verwenden.
nn.BCELoss
berechnet den binären Kreuzentropieverlust. Dies gilt, wenn Sie ein oder mehrere Ziele haben, die entweder 0 oder 1 sind (daher die Binärdatei). In Ihrem Fall ist das Ziel eine einzelne Ganzzahl zwischen 0 und 9. Da es nur eine geringe Anzahl potenzieller Zielwerte gibt, wird am häufigsten der kategoriale Kreuzentropieverlust verwendet (nn.CrossEntropyLoss
). Die "theoretische" Definition des Kreuzentropieverlusts erwartet, dass die Netzwerkausgänge und die Ziele beide 10-dimensionale Vektoren sind, wobei das Ziel alle Nullen außer an einem Ort ist (One-Hot-codiert). Aus Gründen der Rechenstabilität und der Raumeffizienznn.CrossEntropyLoss
nimmt pytorch's jedoch die ganze Zahl direkt als Ziel . jedochmüssen Sie ihm noch einen 10-dimensionalen Ausgabevektor aus Ihrem Netzwerk bereitstellen.Um dieses Problem in Ihrem Code zu beheben, muss
fc3
ein 10-dimensionales Feature ausgegeben werden, und die Beschriftungen müssen Ganzzahlen (keine Gleitkommazahlen) sein. Außerdem muss.sigmoid
fc3 nicht verwendet werden, da die Entropieverlustfunktion von pytorch intern log-softmax anwendet, bevor der endgültige Verlustwert berechnet wird.2. Wie von Serget Dymchenko hervorgehoben, müssen Sie das Netzwerk
eval
während der Inferenz in den Modus undtrain
während des Zugs in den Modus schalten. Dies betrifft hauptsächlich Dropout- und Batch_Norm-Ebenen, da sie sich während des Trainings und der Inferenz unterschiedlich verhalten.3. Eine Lernrate von 0,03 ist wahrscheinlich etwas zu hoch. Es funktioniert gut mit einer Lernrate von 0,001 und in ein paar Experimenten sah ich, dass das Training bei 0,03 divergierte.
Um diese Korrekturen zu berücksichtigen, mussten einige Änderungen vorgenommen werden. Die minimalen Korrekturen am Code sind unten aufgeführt. Ich habe alle Zeilen kommentiert, die geändert wurden,
####
gefolgt von einer kurzen Beschreibung der Änderung.Trainingsergebnisse sind jetzt ...
Einige andere Probleme, die Ihre Leistung und Ihren Code verbessern.
4. Sie verschieben das Modell niemals auf die GPU. Dies bedeutet, dass Sie keine GPU-Beschleunigung erhalten.
5.
torchvision
wurde mit allen Standardtransformationen und Datensätzen entwickelt und ist für die Verwendung mit PyTorch ausgelegt. Ich empfehle es zu benutzen. Dadurch wird auch die Abhängigkeit von Keras in Ihrem Code aufgehoben.6. Normalisieren Sie Ihre Daten, indem Sie den Mittelwert subtrahieren und durch die Standardabweichung dividieren, um die Leistung Ihres Netzwerks zu verbessern. Mit Fackelvision können Sie verwenden
transforms.Normalize
. Dies wird bei MNIST keinen großen Unterschied machen, da es bereits zu einfach ist. Bei schwierigeren Problemen erweist es sich jedoch als wichtig.Weiter verbesserter Code wird unten gezeigt (viel schneller auf der GPU).
Aktualisierte Trainingsergebnisse ...
quelle
Eines ist mir aufgefallen, dass Sie das Modell im Zugmodus testen. Sie müssen anrufen
net.eval()
, um Aussetzer zu deaktivieren (und dannnet.train()
wieder in den Zugmodus zu versetzen).Vielleicht gibt es noch andere Probleme. Geht der Trainingsverlust zurück? Haben Sie versucht, ein einzelnes Beispiel zu überarbeiten?
quelle