Почему эта сеть nerual плохо работает на MNIST?

#machine-learning #deep-learning #pytorch #mnist

Вопрос:

Привет, я создаю сеть nerual в pytorch для классификации MNIST, и, клянусь жизнью, я не могу понять, почему эта сеть не будет работать с точностью выше 7%. Любое руководство было бы неплохо.

 import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from keras.datasets import mnist
from keras.utils.np_utils import to_categorical
import numpy as np
from sklearn.metrics import precision_score, recall_score, f1_score, accuracy_score, confusion_matrix

(X_train, Y_train), (X_test, Y_test) = mnist.load_data()

X_train = X_train.astype("float32")/255
X_test = X_test.astype("float32")/255


X_train = X_train.reshape(X_train.shape[0],(X_train.shape[1] * X_train.shape[2]));
X_test = X_test.reshape(X_test.shape[0],(X_test.shape[1] * X_test.shape[2]));

class Net(torch.nn.Module):
  def __init__(self):
    super(Net, self).__init__()
    self.lin_1 = nn.Linear(784, 128)
    self.lin_2 = nn.Linear(128, 64)
    self.lin_3 = nn.Linear(64, 10)

  def forward(self,x) :
    x = self.lin_1(x)
    x = torch.relu(x)
    x = self.lin_2(x)
    x = torch.relu(x)
    x = self.lin_3(x)
    x = torch.softmax(x, dim=0)
    return x

net = Net();
loss = torch.nn.CrossEntropyLoss();
optimizer = torch.optim.SGD(net.parameters(),lr = 0.01);

X_train = torch.from_numpy(X_train);
X_test = torch.from_numpy(X_test);
y_train = torch.from_numpy(Y_train);
y_test = torch.from_numpy(Y_test)

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu");
X_train.to(device);
X_test.to(device);
y_train.to(device);
y_test.to(device);
net.to(device);
loss.to(device);

y_train = y_train.type(torch.long)
y_test = y_test.type(torch.long)

net.train()
for epoch in range(10):
  #pred = torch.max(net(X_train),1);
  pred = net(X_train.to(device));
  
  train_loss = loss(pred,y_train.to(device));
  optimizer.zero_grad()
  train_loss.backward()
  optimizer.step()

net.eval()

pred = torch.max(net(X_test.to(device)),1)[1];
print('The accuracy for pytorch is ' , accuracy_score(y_test.cpu().numpy(),pred.cpu().numpy()));
 

Я чувствую, что мне нужно как-то преобразовать данные. Вот почему я делю данные обучения и тестирования на 255, и сеть исключает плавающее значение для ввода и длинное значение для вывода.

Вот версия numpy, которую я сделал без pytorch

 from keras.datasets import mnist
from keras.utils.np_utils import to_categorical
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()

X_train = X_train.astype("float32")/255
X_test = X_test.astype("float32")/255


X_train = X_train.reshape(X_train.shape[0],(X_train.shape[1] * X_train.shape[2]));
X_test = X_test.reshape(X_test.shape[0],(X_test.shape[1] * X_test.shape[2]));

Y_train = to_categorical(Y_train);
Y_test = to_categorical(Y_test)
import numpy as np

print(Y_test.shape)

class DNN():
  def __init__(self, sizes, epochs=10, lr = 0.01):
    self.sizes = sizes
    self.epochs = epochs
    self.lr = lr
    self.params = self.initialization();

  def ReLu(self, x, derivative=False):
    if derivative:
      return 1. * (x > 0)
    else:
      return x * (x > 0)

  def softmax(self, x, derivative=False):
        # Numerically stable with large exponentials
        exps = np.exp(x - x.max())
        if derivative:
            return exps / np.sum(exps, axis=0) * (1 - exps / np.sum(exps, axis=0))
        return exps / np.sum(exps, axis=0)

  def initialization(self):
        # number of nodes in each layer
        input_layer=self.sizes[0]
        hidden_1=self.sizes[1]
        hidden_2=self.sizes[2]
        output_layer=self.sizes[3]

        params = {
            "W1":np.random.randn(hidden_1, input_layer) * np.sqrt(1. / hidden_1),
            "W2":np.random.randn(hidden_2, hidden_1) * np.sqrt(1. / hidden_2),
            "W3":np.random.randn(output_layer, hidden_2) * np.sqrt(1. / output_layer)
        }

        return params
  def forward (self,X_train):
    
    self.params["X0"] = X_train;
    
    self.params["Z1"] = np.dot(self.params["W1"], self.params["X0"])
    self.params['X1'] = self.ReLu(self.params["Z1"])

    self.params['Z2'] = np.dot(self.params["W2"], self.params["X1"])
    self.params["X2"] = self.ReLu(self.params["Z2"])

    self.params["Z3"] = np.dot(self.params["W3"], self.params["X2"])
    self.params["X3"] = self.softmax(self.params["Z3"])

    return self.params["X3"]
  
  def backpropagation (self, Y_train, output):

    update = {};

    error = 2 * (output - Y_train) / output.shape[0] * self.softmax(self.params["Z3"], derivative=True)
    update["W3"] = np.outer(error, self.params["X2"])

    error = np.dot(self.params["W3"].T, error) * self.ReLu(self.params["Z2"], derivative=True)
    update["W2"] = np.outer(error, self.params["X1"])

    error = np.dot(self.params["W2"].T, error) * self.ReLu(self.params["Z1"], derivative=True)
    update["W1"] = np.outer(error, self.params["X0"])

    return update

  def updateParams (self,update):
    for key, value in update.items():
      #print(key)
      self.params[key] -= self.lr * value

  def test_accuracy(self, X_test, Y_train):
    predictions = []
    for i in range(len(X_test)):
      output = self.forward(X_test[i])
      pred = np.argmax(output)
      predictions.append(pred == np.argmax(Y_train[i]))
    
    
    return np.mean(predictions)


  def train(self, X_train, Y_train):
        for epoch in range(self.epochs):
            print("epoch ", epoch)
            for i in range(len(X_train)):
                output = self.forward(X_train[i])
                update = self.backpropagation(Y_train[i], output)
                self.updateParams(update)

dnn = DNN(sizes=[784, 200, 50, 10],epochs=10)
dnn.train(X_train, Y_train)

print("The accuracy of the numpy network on the test dataset is ", dnn.test_accuracy(X_test,Y_test))

 

Ответ №1:

Что ж, я могу сразу сказать, что с предоставленным вами кодом есть пара проблем:

  1. Пожалуйста, ознакомьтесь с документацией для функции перекрестных потерь энтропии PyTorch. Если вы прочтете его, вы заметите, что torch.nn.CrossEntropyLoss он выполняет функцию softmax внутренне. Это означает, что вы действительно не должны использовать другого torch.softmax в качестве активации вывода, если используете nn.CrossEntropyLoss . Если по какой-то причине вы хотите использовать softmax на выходном уровне, вам следует рассмотреть возможность использования nn.NLLLoss вместо этого. Если вы посмотрите на изображение, которое я опубликовал ниже, простое удаление x = torch.softmax(x, dim=0) приведет к снижению потерь, в то время как его использование приведет к тому, что потери будут одинаковыми (следовательно, плохими).
  2. Вы тренируетесь со слишком малым количеством эпох. Я попытался запустить ваш код с 3000 эпохами, а не с 10, и конечная производительность составляет 0,9028, а не 0,1038. Вы также можете видеть, что значение потерь падает намного больше по сравнению с исходной реализацией (второе изображение).

введите описание изображения здесь

введите описание изображения здесь

Редактировать

После того, как я взглянул на ваш код NumPy, проблема стала яснее. Мое второе замечание все еще остается в силе: вы недостаточно тренируете свою модель. Я несколько неправильно использовал термин «эпоха» выше, но на самом деле я имел в виду «шаги».

Если вы посмотрите на свой код NumPy, у вас есть два цикла for: внешний-это количество эпох, а внутренний-циклы по обучающим данным. Очевидно, вы используете однопакетное обучение в течение десяти эпох. Это означает, что вы обновляете параметры своей модели в общей сложности 600 000 раз (60 000 обучающих выборок * 10 эпох) за весь процесс. Для вашего кода PyTorch вы передаете все обучающие данные в одной партии и тренируетесь в течение десяти эпох. Это означает, что вы обновляете свои параметры только десять раз.

Если вы измените свой код PyTorch, чтобы он был:

 for epoch in range(10):
    net.train()

    for idx, _ in enumerate(X_train):
        prediction = net(X_train[idx].to(device))
        train_loss = loss(prediction.unsqueeze(0), y_train[idx].unsqueeze(0).to(device))

        optimizer.zero_grad()
        train_loss.backward()
        optimizer.step()

    net.eval()
    prediction = torch.max(net(X_test.to(device)), 1)[1]
    accuracy = accuracy_score(y_test,cpu().numpy(), prediction.cpu().numpy())
    print(f"Epoch {epoch   1} test accuracy is {accuracy}.")
 

затем вы заметите, что модели требуется всего две эпохи, чтобы достичь точности 96%.

Комментарии:

1. Поэтому я запрограммировал эту сеть вручную в numpy и смог получить точность 96% за 10 эпох, так что, за вычетом проблемы softmax, я все еще думаю, что здесь что-то не так.

2. Не могли бы вы тогда отредактировать код NumPy в своем вопросе? Я не думаю, что без этого можно понять, в чем разница.