Evolution
Тема интерфейса

Инференс на собственных изображениях с использованием модели CNN, обученной на MNIST

В данном сценарии мы рассмотрим процесс создания и использования простой сверточной нейронной сети (CNN) для распознавания рукописных цифр на примере датасета MNIST. Этот сценарий подходит для начинающих, интересующихся компьютерным зрением и машинным обучением.

Шаги:

Перед началом работы

Шаг 1. Подготовьте среду

  1. Убедитесь, что при создании ноутбука выбрали образ, который поддерживает CUDA.

  2. Установите PyTorch и torchvision:

    pip install torch
    pip install torchvision
  3. Проверьте доступность GPU:

    import torch
    # Проверим доступность GPU
    cuda_available = torch.cuda.is_available()
    print(f"CUDA доступен: {cuda_available}")
    # Если GPU доступен — выведем информацию о доступности, количестве и названии GPU
    if cuda_available:
    print(f"Количество доступных GPU: {torch.cuda.device_count()}")
    print(f"Название GPU: {torch.cuda.get_device_name(0)}")
    device = torch.device("cuda")
    else:
    print("Используется CPU")
    device = torch.device("cpu")
  4. Для начала работы над созданием и обучением модели CNN необходимо импортировать следующие библиотеки:

    • torch — основная библиотека для работы с нейронными сетями.

    • torchvision — библиотека для работы с изображениями и наборами данных.

    • matplotlib — библиотека для визуализации данных.

    • SummaryWriter из torch.utils.tensorboard — инструмент для отслеживания и визуализации процесса обучения.

    Импортируйте библиотеки:

    from torch import nn, optim
    from torchvision import datasets
    from torch.utils.data import DataLoader
    import matplotlib.pyplot as plt
    from torch.utils.tensorboard import SummaryWriter

Шаг 2. Обучите простую сверточную нейросеть (CNN)

В этом шаге мы перейдем к практическому применению сверточных нейронных сетей (CNN) для решения задачи классификации изображений. Мы будем использовать набор данных MNIST, который является классическим набором данных для задач машинного обучения и компьютерного зрения.

  1. Выполните трансформацию данных для MNIST (одноканальные изображения):

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

    В результате мы выполнили трансформацию данных из набора MNIST для обучения модели. Это нужно для того, чтобы привести данные к формату, который требуется для работы с моделью.

  2. Загрузите датасеты MNIST:

    train_dataset = datasets.MNIST(root='./mnist_data', train=True, download=True, transform=transform)
    test_dataset = datasets.MNIST(root='./mnist_data', train=False, download=True, transform=transform)
    train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
    test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)

    В результате мы загрузили датасеты MNIST для обучения и тестирования модели сверточной нейронной сети (CNN). Этот набор данных содержит изображения рукописных цифр от 0 до 9 и является одним из наиболее популярных наборов данных для задач классификации изображений.

  3. Для создания эффективной модели сверточной нейронной сети (CNN) необходимо определить ее архитектуру. В данном случае мы будем использовать архитектуру, похожую на ResNet, которая зарекомендовала себя как одна из наиболее эффективных для задач классификации изображений.

    Определите архитектуру простой ResNet-like CNN:

    class BasicBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
    super(BasicBlock, self).__init__()
    self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
    self.bn1 = nn.BatchNorm2d(out_channels)
    self.relu = nn.ReLU(inplace=True)
    self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
    self.bn2 = nn.BatchNorm2d(out_channels)
    self.downsample = None
    if stride != 1 or in_channels != out_channels:
    self.downsample = nn.Sequential(
    nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
    nn.BatchNorm2d(out_channels)
    )
    def forward(self, x):
    identity = x
    out = self.conv1(x)
    out = self.bn1(out)
    out = self.relu(out)
    out = self.conv2(out)
    out = self.bn2(out)
    if self.downsample is not None:
    identity = self.downsample(x)
    out += identity
    out = self.relu(out)
    return out
  4. После определения архитектуры модели сверточной нейронной сети (CNN) необходимо выполнить ее инициализацию и настроить параметры для обучения.

    Выполните инициализацию модели, настройте функцию потерь и оптимизатор:

    model = MiniResNet().to(device)
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)

    В результате мы создали экземпляр модели MiniResNet, определили функцию потерь и выбрали оптимизатор, который будет использоваться для обновления весов модели в процессе обучения.

  5. Для отслеживания процесса обучения модели и оценки его эффективности необходимо создать логгер, который будет записывать метрики, такие как потери и точность модели на обучающей и тестовой выборках.

    Создайте логгер для записи метрик при обучении модели:

    writer = SummaryWriter(log_dir='runs/mnist_experiment')
    # Например, логируем параметры модели:
    writer.add_graph(model, torch.randn(1, 1, 28, 28).to(device))
    epochs = 10
    train_losses = []
    test_losses = []
    train_accuracies = []
    test_accuracies = []
  6. Проведите обучение модели и оцените точность:

    for epoch in range(epochs):
    model.train()
    total_train_loss = 0
    correct_train = 0
    total_train = 0
    for images, labels in train_loader:
    images, labels = images.to(device), labels.to(device)
    outputs = model(images)
    loss = criterion(outputs, labels)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    total_train_loss += loss.item()
    _, predicted = outputs.max(1)
    correct_train += (predicted == labels).sum().item()
    total_train += labels.size(0)
    avg_train_loss = total_train_loss / len(train_loader)
    train_accuracy = correct_train / total_train
    train_losses.append(avg_train_loss)
    train_accuracies.append(train_accuracy)
    # Оценка на тесте
    model.eval()
    total_test_loss = 0
    correct_test = 0
    total_test = 0
    with torch.no_grad():
    for images, labels in test_loader:
    images, labels = images.to(device), labels.to(device)
    outputs = model(images)
    loss = criterion(outputs, labels)
    total_test_loss += loss.item()
    _, predicted = outputs.max(1)
    correct_test += (predicted == labels).sum().item()
    total_test += labels.size(0)
    avg_test_loss = total_test_loss / len(test_loader)
    test_accuracy = correct_test / total_test
    test_losses.append(avg_test_loss)
    test_accuracies.append(test_accuracy)
    # Логируем значения в TensorBoard
    writer.add_scalar('Loss/Train', avg_train_loss, epoch)
    writer.add_scalar('Loss/Test', avg_test_loss, epoch)
    writer.add_scalar('Accuracy/Train', train_accuracy, epoch)
    writer.add_scalar('Accuracy/Test', test_accuracy, epoch)
    print(f"Эпоха [{epoch+1}/{epochs}] "
    f"Train Loss: {avg_train_loss:.4f}, Train Acc: {train_accuracy:.4f} "
    f"| Test Loss: {avg_test_loss:.4f}, Test Acc: {test_accuracy:.4f}")
    # Закрываем SummaryWriter после обучения
    writer.close()

    В этом шаге мы перешли к непосредственному обучению модели на обучающей выборке и оценке ее точности.

    Для корректной работы TensorBoard используйте расширение JupyterLab — tensorboard-pro.

Шаг 3. Выполните инференс на собственных изображениях

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

  1. Загрузите изображение и преобразуйте его в нужный формат:

    image_path = 'my_digit_3.jpg'
    img = Image.open(image_path).convert('L').resize((28, 28))
  2. После загрузки и преобразования изображения необходимо убедиться, что оно было правильно обработано и готово к классификации с помощью модели.

    Посмотрите на загруженное изображение:

    plt.imshow(img, cmap='gray')
    plt.show()
  3. Перед тем как подавать загруженное изображение на вход обученной модели для классификации, необходимо выполнить его преобразование в формат, который использовался во время обучения модели.

    Выполните преобразование изображения:

    transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
    ])
    input_tensor = transform(img).unsqueeze(0).to(device) # (1, 1, 28, 28)
  4. После того как изображение было загружено, преобразовано и подготовлено к классификации, мы можем использовать обученную модель для выполнения инференса и получения предсказания.

    Выполните инференс на подготовленном изображении:

    model.eval()
    with torch.no_grad():
    output = model(input_tensor)
    probabilities = torch.softmax(output, dim=1)
    predicted_class = probabilities.argmax(dim=1).item()
  5. После того как модель классифицировала подготовленное изображение, необходимо получить и проанализировать результаты предсказания.

    Получите результат предсказаний:

    print(f"Модель предсказала цифру: {predicted_class}")
    top3_prob, top3_classes = torch.topk(probabilities, 3)
    for i in range(3):
    print(f"{i+1}) Цифра {top3_classes[0][i].item()} с вероятностью {top3_prob[0][i].item():.4f}")

Шаг 4. Сохраните модель для повторного использования

После сохранения, вы можете загрузить и использовать модель для классификации новых изображений без необходимости повторного обучения.

Сохраните модель для повторного использования:

model_path = "mini_resnet_mnist.pth"
torch.save(model.state_dict(), model_path)
print(f"Веса модели сохранены в {model_path}")