Files
PyTorchCource/ffn.py
T

189 lines
5.8 KiB
Python

# %% Импорт библиотек
import tqdm
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchmetrics import MetricCollection
from torchvision import datasets, transforms
from torchmetrics.classification import Accuracy, Precision, Recall, F1Score
from PIL import Image
from beartype import beartype
# %% Гиперпараметры
num_epoch = 8
batch_size = 64
learning_rate = 1e-3
device = "cuda" if torch.cuda.is_available() else "cpu"
# %% Трансформация данных
# Resize(size) — изменяет размер изображения.
# Grayscale() — преобразование изображения в оттенки серого.
# ToTensor() — преобразует изображение PIL или NumPy-массив в PyTorch-тензор.
# Normalize(mean, std) — нормализация тензора изображения.
# GaussianBlur(kernel_size) — размытие по Гауссу.
image_transforms = transforms.Compose([
transforms.ToTensor()
])
# %% Загрузка набора данных
train_dataset = datasets.MNIST(
root="./MNIST",
train=True,
download=False, # Если скачен, то убрать
transform=image_transforms
)
test_dataset = datasets.MNIST(
root="./MNIST",
train=False,
download=False, # Если скачен, то убрать
transform=image_transforms
)
train_loader = DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
test_loader = DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False)
# %% Структура модели
class FFN(nn.Module):
@beartype
def __init__(self,
input_size: int,
output_size: int):
super().__init__()
self.input_layer = nn.Linear(input_size, 32*32)
self.hidden_layer_1 = nn.Linear(32*32, 32*32)
self.hidden_layer_2 = nn.Linear(32*32, 32*32)
self.output_layer = nn.Linear(32*32, output_size)
@beartype
def forward(self, image: torch.Tensor) -> torch.Tensor:
x = image.flatten(start_dim=1) # batch_size x 28*28
x = torch.relu(self.input_layer(x))
x = torch.relu(self.hidden_layer_1(x))
x = torch.relu(self.hidden_layer_2(x))
x = self.output_layer(x)
return x # batch_size x 10
model = FFN(input_size=28*28,
output_size=10)
model.to(device)
# %% Метрики
train_metrics = MetricCollection({
"accuracy": Accuracy(task="multiclass", num_classes=10),
"precision": Precision(task="multiclass", num_classes=10, average="macro"),
"recall": Recall(task="multiclass", num_classes=10, average="macro"),
"f1": F1Score(task="multiclass", num_classes=10, average="macro")
}).to(device)
test_metrics = MetricCollection({
"accuracy": Accuracy(task="multiclass", num_classes=10),
"precision": Precision(task="multiclass", num_classes=10, average="macro"),
"recall": Recall(task="multiclass", num_classes=10, average="macro"),
"f1": F1Score(task="multiclass", num_classes=10, average="macro")
}).to(device)
# %% Оптимизаторы
criterion = nn.CrossEntropyLoss()
optim = torch.optim.Adam(model.parameters(), lr=learning_rate)
# %% Тренировочный цикл
for epoch in tqdm.tqdm(range(num_epoch)):
model.train()
running_loss = 0
for data, target in train_loader:
data, target = data.to(device), target.to(device)
optim.zero_grad()
outputs = model(data)
loss = criterion(outputs, target)
loss.backward()
running_loss += loss.item()
optim.step()
preds = outputs.argmax(dim=1)
train_metrics.update(preds, target)
results = train_metrics.compute()
print(f"Epoch: {epoch+1}")
print("=== TRAIN ===")
print(f" Loss: {(running_loss/len(train_loader)):.4f}")
print(f" Accuracy: {results['accuracy']:.4f}")
print(f" Precision: {results['precision']:.4f}")
print(f" Recall: {results['recall']:.4f}")
print(f" F1: {results['f1']:.4f}")
print()
train_metrics.reset()
model.eval()
with torch.no_grad():
test_loss = 0
for data, target in test_loader:
data, target = data.to(device), target.to(device)
outputs = model(data)
loss = criterion(outputs, target)
test_loss += loss.item()
preds = outputs.argmax(dim=1)
test_metrics.update(preds, target)
results = test_metrics.compute()
print("=== TEST ===")
print(f" Loss: {(test_loss/len(train_loader)):.4f}")
print(f" Accuracy: {results['accuracy']:.4f}")
print(f" Precision: {results['precision']:.4f}")
print(f" Recall: {results['recall']:.4f}")
print(f" F1: {results['f1']:.4f}")
print()
test_metrics.reset()
torch.save(model.state_dict(), "./models/mnist_model.pth")
# %% Инференс модели
image_transforms = transforms.Compose([
transforms.Grayscale(),
transforms.Resize((28,28)),
transforms.ToTensor()
])
model.eval()
model.load_state_dict(torch.load("./models/mnist_model.pth"))
def predict(image_path):
image = Image.open(image_path)
image = image_transforms(image)
image = image.unsqueeze(0)
image = image.to(device)
with torch.no_grad():
output = model(image)
pred = output.argmax(dim=1).item()
return pred
print(f"Predicted label: {predict('./test_images/mnist/3.png')}")
print(f"Predicted label: {predict('./test_images/mnist/5.png')}")
print(f"Predicted label: {predict('./test_images/mnist/6.png')}")