Фреймворк 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])
Такой подход используется для реализации нестандартных операций.