Фреймворк PyTorch для задач искусственного интеллекта

Раздел: Искусственный интеллект -> глубокое обучение

Основы PyTorch и создание нейронной сети

Эффективное решение: определение и обучение простой полносвязной сети на наборе данных MNIST

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

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

transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
train_data = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)

class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784, 128)
        self.fc2 = nn.Linear(128, 10)
    def forward(self, x):
        x = x.view(-1, 784)
        x = torch.relu(self.fc1(x))
        return self.fc2(x)

model = SimpleNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

for epoch in range(3):
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
    print(f'Epoch {epoch+1} завершена')

Torch python (фреймворк pytorch)

Объяснение:

  • transforms нормализуют пиксели изображений 28x28.
  • DataLoader разбивает данные на батчи и перемешивает.
  • Модель наследует nn.Module, содержит два линейных слоя.
  • В цикле вычисляется loss, обратное распространение и шаг оптимизатора.

Как использовать Sequential для быстрого построения модели?

При необходимости простой последовательной архитектуры применяется nn.Sequential. Это сокращает код, но ограничивает сложные ветвления.

model = nn.Sequential(
    nn.Flatten(),
    nn.Linear(784, 128),
    nn.ReLU(),
    nn.Linear(128, 10)
)

библиотека torch python (библиотека pytorch в python)

Типичная ошибка:

Забыть указать nn.Flatten() перед Linear, что приводит к несоответствию размерностей. Ошибка RuntimeError: mat1 and mat2 shapes cannot be multiplied.

Решение: добавить сглаживание или явно изменять размер в forward при использовании кастомного класса.

Как использовать разные оптимизаторы, например Adam?

Adam часто сходится быстрее SGD. Достаточно заменить строку инициализации оптимизатора.

optimizer = optim.Adam(model.parameters(), lr=0.001)

классификация изображений python с использованием resnet50 (классификация изображений с resnet50)

Проблема:

Высокий learning rate (lr) может вызвать расходимость. Рекомендуется начинать с 0.001 для Adam.

Как перенести модель и данные на GPU?

Используется метод .cuda() или .to(device). Для ускорения обучения.

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = SimpleNet().to(device)
for images, labels in train_loader:
    images, labels = images.to(device), labels.to(device)
    ...

Типичная ошибка:

Забыть перенести тензоры на то же устройство, что и модель - возникает ошибка Expected all tensors to be on the same device.

Решение: всегда проверять device при загрузке батча.

Как сохранить и загрузить обученную модель?

Сохраняются state_dict (словарь параметров) или вся модель.

# Сохранение
 torch.save(model.state_dict(), 'mnist_model.pth')
# Загрузка
 model = SimpleNet()
 model.load_state_dict(torch.load('mnist_model.pth'))
 model.eval()

Распространённая ошибка:

Загрузить state_dict в модель, не создав предварительно экземпляр с той же архитектурой. Приводит к KeyError или несоответствию ключей.

Как использовать предобученную модель (transfer learning)?

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

from torchvision import models
model = models.resnet18(pretrained=True)
for param in model.parameters():
    param.requires_grad = False
model.fc = nn.Linear(512, 10)  # новый классификатор
optimizer = optim.Adam(model.fc.parameters(), lr=0.001)

Проблема:

Размер входных изображений может не совпадать с ожидаемым (224x224 для ResNet). Необходимо изменить transforms.

Расширенные примеры работы с PyTorch

Пример 1: Кастомный датасет и DataLoader

Создаётся класс, наследующий torch.utils.data.Dataset, с реализацией __len__ и __getitem__.

Пример
import torch
from torch.utils.data import Dataset, DataLoader
import numpy as np

class CustomDataset(Dataset):
    def __init__(self, num_samples=1000):
        self.data = torch.randn(num_samples, 10)
        self.labels = torch.randint(0, 2, (num_samples,))
    def __len__(self):
        return len(self.data)
    def __getitem__(self, idx):
        return self.data[idx], self.labels[idx]

dataset = CustomDataset(500)
loader = DataLoader(dataset, batch_size=32, shuffle=True)
for batch_idx, (x, y) in enumerate(loader):
    print(f'Батч {batch_idx}: x shape {x.shape}, y length {len(y)}')
    if batch_idx == 2:
        break
Батч 0: x shape torch.Size([32, 10]), y length 32
Батч 1: x shape torch.Size([32, 10]), y length 32
Батч 2: x shape torch.Size([32, 10]), y length 32

Этот подход позволяет загружать произвольные данные (изображения, текст, сигналы) без стандартных реализаций torchvision.

Пример 2: Использование autograd для ручного вычисления градиента

Пример
x = torch.tensor([2.0, 3.0], requires_grad=True)
y = x**2 + 2*x + 1
loss = y.sum()
loss.backward()
print('Градиенты:', x.grad)
Градиенты: tensor([6., 8.])

Вручную вычислены производные от y = x^2 + 2x + 1: dy/dx = 2x + 2. Для x=2 => 6, для x=3 => 8.

Пример 3: Планировщик learning rate (ReduceLROnPlateau)

Пример
import torch.optim as optim
from torch.optim.lr_scheduler import ReduceLROnPlateau

model = nn.Linear(10, 2)
optimizer = optim.Adam(model.parameters(), lr=0.01)
scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=2)

for epoch in range(10):
    train_loss = ...  # вычисленный loss
    scheduler.step(train_loss)
    print(f'Epoch {epoch}: lr = {optimizer.param_groups[0]["lr"]}')
Epoch 0: lr = 0.01
Epoch 1: lr = 0.01
Epoch 2: lr = 0.01
Epoch 3: lr = 0.005  (loss не уменьшался 2 эпохи)

Планировщик уменьшает lr, когда метрика перестаёт улучшаться, что предотвращает перескакивание минимума.

Пример 4: Сохранение контрольной точки (checkpoint) с состоянием оптимизатора

Пример
checkpoint = {
    'epoch': 10,
    'model_state_dict': model.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
    'loss': loss,
}
torch.save(checkpoint, 'checkpoint.pth')

# Загрузка
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
start_epoch = checkpoint['epoch'] + 1

Контрольные точки позволяют возобновить обучение после прерывания.

Пример 5: Использование DataParallel для обучения на нескольких GPU

Пример
if torch.cuda.device_count() > 1:
    model = nn.DataParallel(model)
model = model.to(device)
# Остальной код обучения без изменений

DataParallel автоматически разделяет батч по доступным GPU, собирает результаты.

Пример 6: Визуализация архитектуры с помощью torchviz

Пример
from torchviz import make_dot
x = torch.randn(1, 784).requires_grad_(True)
y = model(x)
viz = make_dot(y, params=dict(model.named_parameters()))
viz.render('model_graph', format='png')  # сохраняет граф

Это помогает понять потоки данных и зависимости между параметрами.

Пример 7: Функциональный API (torch.nn.functional) для гибкости

Пример
import torch.nn.functional as F

class FlexibleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3)
        self.fc = nn.Linear(32*26*26, 10)
    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

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

Пример 8: Использование TensorBoard для логирования

Пример
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('runs/experiment')

for epoch in range(10):
    loss = ...
    writer.add_scalar('Loss/train', loss, epoch)
writer.close()

После запуска tensorboard --logdir=runs в браузере отображаются графики потерь и метрик.

Пример 9: Создание собственного слоя с параметрами

Пример
class MyLinear(nn.Module):
    def __init__(self, in_features, out_features):
        super().__init__()
        self.weight = nn.Parameter(torch.randn(out_features, in_features))
        self.bias = nn.Parameter(torch.zeros(out_features))
    def forward(self, x):
        return x @ self.weight.T + self.bias

layer = MyLinear(10, 5)
x = torch.randn(3, 10)
print(layer(x).shape)
torch.Size([3, 5])

Такой подход используется для реализации нестандартных операций.

фреймворк PyTorch - comments

En
Torch python (python)