[PYTHON] Les amoureux de Keras ont essayé PyTorch

introduction

J'écris des programmes de Deep Learning uniquement dans Keras depuis longtemps, mais maintenant je dois écrire dans PyTorch, donc après avoir étudié, j'ai essayé de faire quelque chose qui fonctionne en utilisant MNIST comme exemple.

[Historique des corrections] -Flatten () et Softmax () sont maintenant utilisés dans le modèle -En conséquence, la fonction de perte est changée en CrossEntropyLoss ().

Premier tutoriel

Pour le moment, ce sera un tutoriel au début, donc j'ai commencé PyTorch Tutorial depuis le début.

Cependant, c'est vraiment difficile à comprendre. Je ne pouvais pas saisir "Autograd" sensuellement, et ma motivation est tombée à moins de 10%.

Maintenant, je pense qu'il aurait été préférable de commencer par "Qu'est-ce que torch.nn vraiment?" au lieu de partir du début. ..

sauvegarde

J'avais un tel sentiment, alors j'ai commencé le tutoriel et acheté un livre. Après avoir recherché diverses choses, j'ai choisi les deux livres suivants.

Je vais lire ceci attentivement en le copiant.

Et MNIST

Alors, après tout, c'est MNIST au début, non? Donc, en vous référant au code officiel de PyTorch, extrayez uniquement les parties nécessaires et modifiez-les pour que vous puissiez les comprendre ** J'ai essayé de.

environnement

Pour le moment, je le fais avec Windows 10 + Anaconda + CUDA.

import

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim.lr_scheduler import StepLR
from torchvision import datasets, transforms

from torchsummary import summary

En gros, c'est toujours un échantillon, mais j'ai ajouté "résumé de la torche" pour confirmation. Malheureusement, il ne peut pas être installé avec conda, je l'ai donc installé avec pip. De plus, la version de torchvision ne correspondait pas et je n'ai pas pu l'installer à partir de l'interface graphique. (Installé avec conda)

Paramètres

seed = 1

epochs = 14
batch_size = 64
log_interval = 100

lr = 1.0
gamma = 0.7

Dans l'exemple, c'était un argument, mais je n'ai extrait que les variables nécessaires et leur ai fait des valeurs fixes. La valeur est le nombre par défaut. (J'ai changé seulement log_interval)

modèle

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=2)
        self.dropout1 = nn.Dropout2d(0.25)
        self.dropout2 = nn.Dropout2d(0.5)
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)
        self.softmax = nn.Softmax(dim=1)

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu(x)
        x = self.conv2(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.dropout1(x)
        x = self.fc2(x)
        x = self.relu(x)
        x = self.dropout2(x)
        output = self.softmax(x)
        return output

J'en ai fait une variable interne avec \ _ \ _ init__ () autant que possible. Le dernier est Softmax ().

J'ai également pensé à écrire le modèle avec l'API séquentielle, mais j'ai l'impression que je vais jouer avec lui en fonction de cela, alors je l'ai laissé comme API fonctionnelle.

Apprentissage

def train(model, loss_fn, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = loss_fn(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()/len(data)))

C'est essentiellement un échantillon. La fonction de perte est maintenant spécifiée par l'appelant.

J'aime le mécanisme de rotation de l'époque du côté de l'appelant et de rotation du lot ici car il est facile à comprendre.

Évaluation

def test(model, loss_fn, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += loss_fn(output, target)  # sum up batch loss
            pred = output.argmax(dim=1, keepdim=True)  # get the index of the max log-probability
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)

    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

Encore une fois, j'ai décidé de spécifier la fonction de perte du côté de l'appelant.

Il semble que PyTorch n'ait pas le concept de «prédire», donc je pense que le code ici sera utile lors de la déduction en utilisant le modèle entraîné plus tard. (Je n'ai pas encore écrit le code d'inférence)

view_as () est trop pratique et je suis surpris.

Traitement principal

torch.manual_seed(seed)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

Tout d'abord, intégrez une graine aléatoire et spécifiez l'environnement d'exécution (CUDA).

transform=transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.0,), (1.0,))
    ])
kwargs = {'batch_size': batch_size,
          'num_workers': 1,
          'pin_memory': True,
          'shuffle': True}

dataset1 = datasets.MNIST('../data', train=True, download=True, transform=transform)
dataset2 = datasets.MNIST('../data', train=False, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1,**kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **kwargs)

Chargez ensuite le jeu de données MNIST. Lors de la lecture, la moyenne est de 0 et l'écart type est de 1. (J'ai changé cela de l'exemple)

Maintenant, préparez un chargeur de données. Celui-ci est assez pratique. J'espère que ce gars élargira les données. (Pas encore examiné)

Je suis également heureux que la variable objective n'ait pas besoin d'être au format one-hot.

model = Net().to(device)
summary(model, (1,28,28))

loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adadelta(model.parameters(), lr=lr)
scheduler = StepLR(optimizer, step_size=1, gamma=gamma)

J'ai créé un modèle et affiché son contenu. résumé de la torche, excellent. Ceux qui aiment les keras sont incontournables. Je ne peux pas être soulagé si je ne vois pas ça. Au fait, cette fois, il sera affiché comme ceci.

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1           [-1, 32, 26, 26]             320
              ReLU-2           [-1, 32, 26, 26]               0
            Conv2d-3           [-1, 64, 24, 24]          18,496
              ReLU-4           [-1, 64, 24, 24]               0
         MaxPool2d-5           [-1, 64, 12, 12]               0
           Flatten-6                 [-1, 9216]               0
            Linear-7                  [-1, 128]       1,179,776
              ReLU-8                  [-1, 128]               0
         Dropout2d-9                  [-1, 128]               0
           Linear-10                   [-1, 10]           1,290
             ReLU-11                   [-1, 10]               0
        Dropout2d-12                   [-1, 10]               0
          Softmax-13                   [-1, 10]               0
================================================================
Total params: 1,199,882
Trainable params: 1,199,882
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 1.04
Params size (MB): 4.58
Estimated Total Size (MB): 5.62
----------------------------------------------------------------

Je suis satisfait de l'affichage familier. À propos, il convient de noter que l'ordre de l'arrangement est différent. Comme c'était (lot, hauteur, largeur, canal) tout le temps, je pense que quelque chose ne va pas avec (lot, canal, hauteur, largeur).

N'oubliez pas de préparer la fonction de perte. J'utilise CrossEntropyLoss () pour le modèle Softmax ().

Après cela, décidez de l'algorithme d'optimisation et de la manière d'ajuster le taux d'apprentissage. Ce serait bien de pouvoir intégrer facilement un mécanisme pour modifier le taux d'apprentissage.

for epoch in range(1, epochs + 1):
    train(model, loss_fn, device, train_loader, optimizer, epoch)
    test(model, loss_fn, device, test_loader)
    scheduler.step()

Je continuerai à apprendre avec l'époque. Apprentissage → Évaluation → Ajustement du taux d'apprentissage C'est très facile à comprendre.

Enregistrer / charger le modèle entraîné

torch.save(model.state_dict(), "mnist_cnn.pt")
model.load_state_dict(torch.load("mnist_cnn.pt"))

En prime, j'ai écrit sur la façon de sauvegarder et de charger le modèle entraîné. J'ai regardé le contenu avec un éditeur binaire, mais c'était rafraîchissant.

Résumé

Je ne suis pas encore familier avec lui, mais j'ai senti qu'il avait les caractéristiques suivantes.

--Facile à saisir le flux ――Peut décrire en détail

J'étudierai plus dur!

Recommended Posts

Les amoureux de Keras ont essayé PyTorch
J'ai essayé d'expliquer l'ensemble de données de Pytorch
J'ai essayé d'implémenter DeepPose avec PyTorch
J'ai essayé d'implémenter DeepPose avec PyTorch PartⅡ
J'ai essayé d'implémenter CVAE avec PyTorch