Лекция 2. Примеры полносвязной и сверточной нейронных сетей¶
1. Предварительная подготовка окружения¶
1.1. Загрузка пакетов¶
In [1]:
import torch
import torchmetrics
import torchvision
import torchvision.datasets
import torchvision.transforms
import torch.utils.data
import torch.nn
import os
from matplotlib import pyplot as plot
from time import time
1.2. Установка параметров, обеспечивающих параллелизм¶
In [2]:
num_inter_threads = 2
num_intra_threads = 2
def set_thread_num(num_inter_threads, num_intra_threads):
def validate(num):
if num < 0:
raise ValueError(f'Incorrect thread count: {num}')
if num_inter_threads:
validate(num_inter_threads)
torch.set_num_interop_threads(num_inter_threads)
print(f'The number of threads for inter-op parallelism: {num_inter_threads}')
if num_intra_threads:
validate(num_intra_threads)
torch.set_num_threads(num_intra_threads)
print(f'The number of threads for intra-op parallelism: {num_intra_threads}')
set_thread_num(num_inter_threads, num_intra_threads)
The number of threads for inter-op parallelism: 2 The number of threads for intra-op parallelism: 2
In [3]:
dir_name = os.getcwd() # получение текущей директории
# Чтение тренировочной и тестовой выборок набора данных MNIST.
# Данные представляются в виде пар (tuple), где первый элемент -
# изображение в формате PIL.Image.Image, а второй - целочисленная
# метка класса.Параметр transform обеспечивает преобразование
# изображений в формат torch.Tensor для последующей работы
train_dataset = torchvision.datasets.MNIST(root = dir_name, train = True, download = True,
transform = torchvision.transforms.ToTensor())
test_dataset = torchvision.datasets.MNIST(root = dir_name, train = False, download = True,
transform = torchvision.transforms.ToTensor())
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw\train-images-idx3-ubyte.gz
100.0%
Extracting C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw\train-images-idx3-ubyte.gz to C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw\train-labels-idx1-ubyte.gz
100.0%
Extracting C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw\train-labels-idx1-ubyte.gz to C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw\t10k-images-idx3-ubyte.gz
100.0%
Extracting C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw\t10k-images-idx3-ubyte.gz to C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
100.0%
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw\t10k-labels-idx1-ubyte.gz Extracting C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw\t10k-labels-idx1-ubyte.gz to C:\Users\kustikova.v\Documents\MyDocs\ITLab\projects\tvm\tensor-compilers-intro-course\lectures\sources\MNIST\raw
2.2. Проверка корректности загрузки данных¶
In [4]:
# функция для демонстрации примеров изображений
def show_images(images, title):
num_showed_imgs_x = 5
num_showed_imgs_y = 5
figsize = (2, 2)
fig, axes = plot.subplots(num_showed_imgs_y, num_showed_imgs_x, figsize = figsize)
fig.suptitle(title)
plot.setp(plot.gcf().get_axes(), xticks = [], yticks = [])
for i, ax in enumerate(axes.flat):
# images[i][0] - многомерный массив типа torch.Tensor
# images[i][1] - число типа numpy.int32
img = images[i][0].numpy().transpose(1, 2, 0).squeeze(axis = 2)
ax.imshow(img, cmap = 'gray')
# Логирование информации о загруженных данных
print('Number of train samples: {}'.format(len(train_dataset)))
show_images(train_dataset, 'Train samples')
print('Number of test samples: {}'.format(len(test_dataset)))
show_images(test_dataset, 'Test samples')
# Создание объектов для последовательной загрузки пачек
# из тренировочной и тестовой выборок
train_batch_size = 64 # размер обрабатываемой пачки данных
train_data_loader = torch.utils.data.DataLoader(train_dataset, batch_size = train_batch_size,
shuffle = True)
test_batch_size = 64
test_data_loader = torch.utils.data.DataLoader(test_dataset, batch_size = test_batch_size,
shuffle = False)
Number of train samples: 60000 Number of test samples: 10000
3. Создание модели, соответствующей логистической регрессии¶
In [5]:
image_resolution = 28 * 28 # 28*28=784 - количество входных нейронов
num_classes = 10 # количество выходных нейронов
# Создание класса сети, соответствующей логистической регрессии
class LogisticRegressionModel(torch.nn.Module):
# Объявление конструктора
def __init__(self, input_dim, output_dim):
super(LogisticRegressionModel, self).__init__()
# Создание полносвязного слоя (softmax включается в вычисление кросс-энтропии)
self.linear = torch.nn.Linear(input_dim, output_dim)
# Переопределение метода, вызываемого в процессе прямого прохода
def forward(self, x):
out = self.linear(x)
return out
# Создание объекта разработанного класса
logreg_nn = LogisticRegressionModel(image_resolution, num_classes)
# Логирование информации о параметрах модели
print('Weight matrix: {}. Biases: {}'.format(logreg_nn.linear.weight.shape,
logreg_nn.linear.bias.shape))
Weight matrix: torch.Size([10, 784]). Biases: torch.Size([10])
4. Создание однослойной сверточной сети¶
In [6]:
class ConvolutionalNeuralNetwork(torch.nn.Module):
# Объявление конструктора
def __init__(self):
super(ConvolutionalNeuralNetwork, self).__init__()
# Свертка. Размер результирующей карты признаков:
# 10 x ((28-3)/1+1) x ((28-3)/1+1) = 10 x 26 x 26
self.conv = torch.nn.Conv2d(in_channels = 1, out_channels = 10,
kernel_size = 3, stride = 1)
# Функция активации. Размер результирующей карты признаков:
# 10 x 26 x 26
self.activation = torch.nn.ReLU()
# Пространственное объединение по максимуму. Размер результирующей карты признаков:
# 10 x ((26-2)/1+1) x ((26-2)/1+1) = 10 x 25 x 25
self.pooling = torch.nn.MaxPool2d(kernel_size = 2, stride = 1)
# Создание полносвязного слоя (softmax включается в вычисление кросс-энтропии)
self.linear = torch.nn.Linear(10 * 25 * 25, 10)
# Переопределение метода, вызываемого в процессе прямого прохода
def forward(self, x):
# Свертка
out = self.conv(x)
# Функция активации
out = self.activation(out)
# Пространственное объединение
out = self.pooling(out)
# Изменение формата хранения тензора из (B, C, W, H) в (B, C*W*H)
out = out.view(out.size(0), -1)
# Полносвязный слой
out = self.linear(out)
return out
# Создание объекта разработанного класса
cnn_model = ConvolutionalNeuralNetwork()
# Логирование информации о параметрах модели
print('Parameters of convolutions:\n1. Kernels = {}\n2. Biases = {}'.
format(cnn_model.conv.weight.shape, cnn_model.conv.bias.shape))
print('Parameters of fully-connected layer:\n1. Weight matrix = {}\n2. Biases = {}'.
format(cnn_model.linear.weight.shape, cnn_model.linear.bias.shape))
Parameters of convolutions: 1. Kernels = torch.Size([10, 1, 3, 3]) 2. Biases = torch.Size([10]) Parameters of fully-connected layer: 1. Weight matrix = torch.Size([10, 6250]) 2. Biases = torch.Size([10])
In [7]:
# функции преобразования загрруженных данных к формату входа сети
def fcnn_transform_data(images):
# Преобразование тензора [B, C, W, H] к формату [B, W * H]
# (images.shape=[B, C, W, H], B - размер пачки, C=1 - число каналов
# W, H - ширина и высота изображений в пачке) и загрузка данных на устройство
return images.view(-1, image_resolution)
def cnn_transform_data(images):
# Для сверточной сети данные подаются в виде двумерного тензора
# (в исходном виде)
return images
def train_model(model, train_data_loader, transform_data, learning_rate=0.1, num_epochs=10):
# Выбор устройства для вычислений
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)
print(f'Device for training: {device}')
# Функция ошибки на этапе обучения
loss_function = torch.nn.CrossEntropyLoss()
# Метод оптимизации для обучения параметров
optimizer = torch.optim.SGD(model.parameters(), lr = learning_rate)
# Метрика качества решения задачи
metric = torchmetrics.classification.Accuracy(task="multiclass", num_classes=10)
metric.to(device)
training_time = 0
for epoch in range(num_epochs): # проход по эпохам
duration = 0.0
for i, (images, labels) in enumerate(train_data_loader): # получение пачки тренировочных данных
# Начало замера времени
tstart = time()
images = transform_data(images).requires_grad_().to(device)
labels = labels.to(device)
# Прямой проход
outputs = model(images) # вычисление выхода сети
loss = loss_function(outputs, labels) # вычисление функции ошибки, loss.item() дает значение
accuracy = metric(outputs, labels) # вычисление точности для пачки тренировочных данных
# Обратный проход
optimizer.zero_grad() # обнуление всех вычисляемых градиентов
loss.backward() # вычисление градиента функции ошибки
optimizer.step() # обновление параметров модели
# Конец замера времени
tfinish = time()
duration += (tfinish - tstart)
# Логирование метрики качества на тренировочных данных по завершении эпохи
accuracy = metric.compute()
print(f'Epoch[{epoch}]: train accuracy = {accuracy:.3f}, duration = {duration:.3f} s, '
f'FPS = {len(train_data_loader.dataset) / duration:.3f} frames/s')
training_time += duration
return training_time
5.2. Обучение логистической регрессии¶
In [8]:
print('Logistic regression model')
num_epochs=10
training_time = train_model(logreg_nn, train_data_loader, fcnn_transform_data,
num_epochs=num_epochs)
print(f'Training time: {training_time:.3f} s')
Logistic regression model Device for training: cpu Epoch[0]: train accuracy = 0.877, duration = 4.322 s, FPS = 13883.233 frames/s Epoch[1]: train accuracy = 0.891, duration = 4.083 s, FPS = 14694.677 frames/s Epoch[2]: train accuracy = 0.898, duration = 4.407 s, FPS = 13615.781 frames/s Epoch[3]: train accuracy = 0.902, duration = 4.566 s, FPS = 13141.489 frames/s Epoch[4]: train accuracy = 0.905, duration = 4.014 s, FPS = 14948.219 frames/s Epoch[5]: train accuracy = 0.908, duration = 4.582 s, FPS = 13095.151 frames/s Epoch[6]: train accuracy = 0.910, duration = 4.490 s, FPS = 13362.213 frames/s Epoch[7]: train accuracy = 0.911, duration = 4.897 s, FPS = 12253.156 frames/s Epoch[8]: train accuracy = 0.913, duration = 3.562 s, FPS = 16842.236 frames/s Epoch[9]: train accuracy = 0.914, duration = 3.452 s, FPS = 17381.312 frames/s Training time: 42.374 s
5.3. Обучение сверточной нейронной сети¶
In [9]:
print('Convolutional neural network')
num_epochs = 10
training_time = train_model(cnn_model, train_data_loader, cnn_transform_data,
num_epochs=num_epochs)
print(f'Training time: {training_time:.3f} s')
Convolutional neural network Device for training: cpu Epoch[0]: train accuracy = 0.912, duration = 46.110 s, FPS = 1301.226 frames/s Epoch[1]: train accuracy = 0.937, duration = 38.432 s, FPS = 1561.218 frames/s Epoch[2]: train accuracy = 0.949, duration = 40.069 s, FPS = 1497.434 frames/s Epoch[3]: train accuracy = 0.956, duration = 40.795 s, FPS = 1470.778 frames/s Epoch[4]: train accuracy = 0.961, duration = 36.309 s, FPS = 1652.500 frames/s Epoch[5]: train accuracy = 0.964, duration = 34.934 s, FPS = 1717.544 frames/s Epoch[6]: train accuracy = 0.967, duration = 23.518 s, FPS = 2551.225 frames/s Epoch[7]: train accuracy = 0.969, duration = 22.755 s, FPS = 2636.800 frames/s Epoch[8]: train accuracy = 0.971, duration = 25.040 s, FPS = 2396.126 frames/s Epoch[9]: train accuracy = 0.972, duration = 23.348 s, FPS = 2569.809 frames/s Training time: 331.309 s
6. Тестирование обученной модели¶
6.1. Функция вычисления точности для набора данных¶
In [10]:
def get_accuracy(data_loader, transform_data, model):
# Выбор устройства для вычислений
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)
print(f'Device for testing: {device}')
# Метрика качества решения задачи
metric = torchmetrics.classification.Accuracy(task="multiclass", num_classes=10)
metric.to(device)
# Время начала вывода
tstart = time()
for i, (images, labels) in enumerate(data_loader): # получение пачки данных
images = transform_data(images).requires_grad_().to(device)
labels = labels.to(device)
outputs = model(images) # вычисление выхода сети
accuracy = metric(outputs, labels) # вычисление точности для пачки данных
# Время окончания вывода
tfinish = time()
duration = tfinish - tstart
return metric.compute(), duration, len(data_loader.dataset) / duration
6.2. Определение точности для логистической регрессии¶
In [11]:
print('Logistic regression model')
accuracy, duration, fps = get_accuracy(test_data_loader, fcnn_transform_data, logreg_nn)
print(f'Test accuracy: {accuracy:.3f}')
print(f'Inference time on test dataset: {duration:.3f} s')
print(f'FPS: {fps:.3f} frames/s')
Logistic regression model Device for testing: cpu Test accuracy: 0.922 Inference time on test dataset: 2.132 s FPS: 4689.342 frames/s
6.3. Определение точности для сверточной сети¶
In [12]:
print('Convolutional neural network')
accuracy, duration, fps = get_accuracy(test_data_loader, cnn_transform_data, cnn_model)
print(f'Test accuracy: {accuracy:.3f}')
print(f'Inference time on test dataset: {duration:.3f} s')
print(f'FPS: {fps:.3f} frames/s')
Convolutional neural network Device for testing: cpu Test accuracy: 0.973 Inference time on test dataset: 3.921 s FPS: 2550.292 frames/s