В этом блоге мы будем использовать набор данных CIFAR10, определим модель CNN, затем обучим модель и, наконец, протестируем модель на тестовых данных.

Импорт библиотек

import torch
import torchvision
import torchvision.transforms as transforms

Проверьте версию torchvision

torchvision.__version__

Вывод: ‘0.9.1+cu102’

Загрузите набор данных CIFAR10

Создайте преобразование изображений

Выходными данными наборов данных torchvision являются изображения PILImage диапазона [0, 1]. Мы преобразуем их в тензоры нормализованного диапазона [-1, 1].

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

Загрузите данные и создайте загрузчик данных

batch_size = 5
train_data = torchvision.datasets.CIFAR10(root='./data', train=True,
                 download=True, transform=transform)
train_data_loader = torch.utils.data.DataLoader(train_data,    batch_size=batch_size, shuffle=True, num_workers=2)

Вывод:

test_data = torchvision.datasets.CIFAR10(root='./data', train=False,
                download=True, transform=transform)
test_data_loader = torch.utils.data.DataLoader(test_data,    batch_size=batch_size, shuffle=False, num_workers=2)

Вывод:

Создайте список классов

class_names = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

Повторите и просмотрите train_data_loader

sample = next(iter(train_data_loader))
imgs, lbls = sample
print(lbls)

Вывод: тензор([9, 0, 8, 1, 8])

Визуализируйте набор данных поезда

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 images
#dataiter = iter(train_data_loader)
images, labels = iter(train_data_loader).next()
# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join(f'{class_names[labels[j]]:5s}' for j in range(batch_size)))

Вывод:

Определите свёрточную нейронную сеть

import torch.nn as nn
import torch.nn.functional as F

класс

torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1,bias=True, padding_mode='zeros', device=None, dtype=None): применяет 2D свертка над входным сигналом, состоящим из нескольких входных плоскостей.

Параметры:

in_channels (int) — количество каналов во входном изображении.

out_channels (int) — количество каналов, созданных сверткой

kernel_size (целое число или кортеж) — размер свертывающегося ядра.

шаг (целое число или кортеж, необязательно) — шаг свертки. По умолчанию: 1

padding (int, tuple или str, необязательно) — заполнение добавляется ко всем четырем сторонам ввода. По умолчанию: 0

padding_mode (строка, необязательно) — «нули», «отражать», «реплицировать» или «круговой». По умолчанию: «нули»

dilation (int или tuple, необязательно) — интервал между элементами ядра. По умолчанию: 1

groups (целое число, необязательно) — количество заблокированных подключений от входных каналов к выходным каналам. По умолчанию: 1

bias (bool, необязательный) — если значение равно True, к выходным данным добавляется обучаемое смещение. По умолчанию: Истина

class MyModel(nn.Module):
    def __init__(self):
        super().__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 = torch.flatten(x, 1) # flatten all dimensions except batch
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x
model = MyModel()

Определите функцию потерь и оптимизатор

import torch.optim as optim
loss_function = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

Обучите сеть

for epoch in range(2):  # loop over the dataset multiple times
    running_loss = 0.0
    for i, data in enumerate(train_data_loader, 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 = model(inputs)
        loss = loss_function(outputs, labels)
        loss.backward()
        optimizer.step()
        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    
            # print every 2000 mini-batches
            print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
            running_loss = 0.0
print('Finished Training')

Выход:

Сохраните модель

PATH = './conv2d_model.sav'
torch.save(model.state_dict(), PATH)

Протестируйте сеть на тестовых данных

Мы обучили сеть за 2 прохода по обучающему набору данных. Но нам нужно проверить, научилась ли сеть вообще чему-нибудь.

Мы проверим это, предсказав метку класса, которую выдает нейронная сеть, и проверим ее на соответствие действительности. Если прогноз правильный, мы добавляем образец в список правильных прогнозов.

Хорошо, первый шаг. Давайте покажем изображение из тестового набора, чтобы ознакомиться.

dataiter = iter(test_data_loader)
images, labels = dataiter.next()
# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join(f'{class_names[labels[j]]:5s}' for j in range(4)))

Вывод:

Загрузите сохраненную модель

trained_model = MyModel()
trained_model.load_state_dict(torch.load(PATH))

Вывод: ‹Все ключи успешно подобраны›

Предсказывать

outputs = trained_model(images)

Результатом являются энергии для 10 классов. Чем выше энергия для класса, тем больше сеть считает, что изображение относится к определенному классу. Итак, получим индекс наибольшей энергии:

_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join(f'{class_names[predicted[j]]:5s}'
                              for j in range(4)))

Вывод: предсказано: корабль-корабль-самолет

Прогнозировать весь набор тестовых данных

correct = 0
total = 0
# we are not training, we don't need to calculate the gradients for our outputs
with torch.no_grad():
    for data in test_data_loader:
        
        images, labels = data
        # calculate outputs by running images through the network
        outputs = trained_model(images)
        
        # the class with the highest energy is what we choose as prediction
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
print(f'Accuracy of the network on the 10000 test images: {100 * correct // total} %')

Вывод: Точность сети на 10 000 тестовых изображений: 53 %

Давайте посмотрим, какие классы показали хорошие результаты, а какие — нет.

Подготовьтесь к подсчету прогнозов для каждого класса

correct_pred = {class_name: 0 for class_name in class_names}
print(correct_pred)
total_pred = {class_name: 0 for class_name in class_names}
print(total_pred)

Вывод:

Прогнозировать все тестовые данные

# again no gradients needed
with torch.no_grad():
    for data in test_data_loader:
        images, labels = data
        outputs = trained_model(images)
        
        #get the maximum of tensor
        _, predictions = torch.max(outputs, 1)
        
        # collect the correct predictions for each class
        for label, prediction in zip(labels, predictions):
            if label == prediction:
                correct_pred[class_names[label]] += 1
                
            total_pred[class_names[label]] += 1
            pass
        pass
    pass

Вывести правильный и общий прогноз

print(correct_pred)
print(total_pred)

Вывод:

Точность печати для каждого класса

for class_name, correct_count in correct_pred.items():
    accuracy = 100 * float(correct_count) / total_pred[class_name]
    print(f'Accuracy for class: {class_name:5s} is {accuracy:.1f} %')
    pass

Вывод:

Мы можем видеть точность каждого класса. Спасибо за прочтение.