Estoy haciendo una CNN con Pytorch para una tarea, pero no aprenderá ni mejorará la precisión. Hice una versión trabajando con el conjunto de datos MNIST para poder publicarlo aquí. Solo estoy buscando una respuesta de por qué no funciona. La arquitectura está bien, la implementé en Keras y tenía más del 92% de precisión después de 3 épocas. Nota: Reformé el MNIST en imágenes de 60x60 porque así es como están las imágenes en mi problema "real".
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
fuente
fuente
comp.ai.neural-nets
preguntas frecuentes tienen algunas sugerencias excelentes sobre dónde buscar si su red neuronal no está aprendiendo; Recomiendo comenzar allí.nn.CrossEntropyLoss
para un problema de clasificación de clase única.nn.CrossEntropyLoss
aplica tanto softmax como NLLLoss como una operación única, así que no lo hagas primero.nn.MSELoss
pero luego debe ajustar los objetivos para que caigan en el rango de salida sigmoide o no ' t aplique sigmoide después de la capa final.RuntimeError: multi-target not supported
.Respuestas:
Primero los problemas principales ...
1. El problema principal con este código es que está utilizando la forma de salida incorrecta y la función de pérdida incorrecta para la clasificación.
nn.BCELoss
calcula la pérdida de entropía cruzada binaria . Esto es aplicable cuando tiene uno o más objetivos que son 0 o 1 (de ahí el binario). En su caso, el objetivo es un número entero entre 0 y 9. Dado que solo hay un pequeño número de valores objetivo potenciales, el enfoque más común es utilizar la pérdida de entropía cruzada categórica (nn.CrossEntropyLoss
). La definición "teórica" de pérdida de entropía cruzada espera que las salidas de la red y los objetivos sean ambos vectores de 10 dimensiones donde el objetivo es todos ceros excepto en una ubicación (codificado en caliente). Sin embargo, por razones de estabilidad computacional y eficiencia de espacio, Pytorchnn.CrossEntropyLoss
toma directamente el entero como objetivo . sin embargo, aún necesita proporcionarle un vector de salida de 10 dimensiones desde su red.Para solucionar este problema en su código, necesitamos tener
fc3
una salida de una característica de 10 dimensiones, y necesitamos que las etiquetas sean enteras (no flotantes). Además, no es necesario usar.sigmoid
en fc3 ya que la función de pérdida de entropía cruzada de pytorch aplica internamente log-softmax antes de calcular el valor de pérdida final.2. Como señaló Serget Dymchenko, debe cambiar la red al
eval
modo durante la inferencia y altrain
modo durante el tren. Esto afecta principalmente a las capas dropout y batch_norm ya que se comportan de manera diferente durante el entrenamiento y la inferencia.3. Una tasa de aprendizaje de 0.03 es probablemente un poco demasiado alta. Funciona bien con una tasa de aprendizaje de 0.001 y en un par de experimentos vi que el entrenamiento divergía en 0.03.
Para acomodar estas correcciones, se necesita realizar una serie de cambios. Las correcciones mínimas al código se muestran a continuación. Comenté cualquier línea con la que se cambió
####
seguido de una breve descripción del cambio.Los resultados del entrenamiento son ahora ...
Algunos otros problemas que mejorarán su rendimiento y código.
4) Nunca moverás el modelo a la GPU. Esto significa que no obtendrá aceleración de GPU.
5)
torchvision
está diseñado con todas las transformaciones y conjuntos de datos estándar y está diseñado para usarse con PyTorch. Recomiendo usarlo. Esto también elimina la dependencia de keras en su código.6. Normalice sus datos restando la media y dividiendo por la desviación estándar para mejorar el rendimiento de su red. Con torchvision puedes usar
transforms.Normalize
. Esto no hará una gran diferencia en MNIST porque ya es demasiado fácil. Pero en problemas más difíciles resulta ser importante.El código mejorado adicional se muestra a continuación (mucho más rápido en GPU).
Resultados actualizados del entrenamiento ...
fuente
Una cosa que noté fue que probaste el modelo en modo tren. Debe llamar
net.eval()
para deshabilitar los abandonos (y luegonet.train()
nuevamente para volver a ponerlo en el modo de tren).Tal vez hay otros problemas. ¿Está bajando la pérdida de entrenamiento? ¿Has tratado de sobreajustar en un solo ejemplo?
fuente