first commit

This commit is contained in:
Даниил Грабарь
2026-09-14 11:05:56 +10:00
commit 41c9010db2
8 changed files with 1009 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
models/
Datasets/
test_images/
train_logs/
+91
View File
@@ -0,0 +1,91 @@
# %% Импорт библиотек
import kagglehub
import splitfolders
import pytorch_lightning as pl
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
from torchvision.transforms import v2 as transforms
from pathlib import Path
from typing import Optional, Callable, Any
# %% Скачивание набора данных
path = kagglehub.dataset_download("jehanbhathena/weather-dataset",
output_dir="./Datasets",
force_download=False)
# %% Разделение набора данных
splitfolders.ratio("./Datasets/weather-dataset",
output="./Datasets/weather-dataset-split",
ratio=(.8, .1, .1),
group_prefix=None,
move=False)
# %% Пример модуля для загрузки данных
TransformType = Optional[Callable[..., Any]]
class MyImageDataset(pl.LightningDataModule):
def __init__(self,
batch_size: int = 1,
train_transform: TransformType = None,
val_transform: TransformType = None,
img_root: str = ""
):
super().__init__()
self.batch_size = batch_size
self.train_transform = train_transform
self.val_transform = val_transform
self.img_root = img_root
def setup(self, stage=None):
self.train_set = ImageFolder(self.img_root / Path("train"),
transform=self.train_transform)
self.val_set = ImageFolder(self.img_root / Path("val"),
transform=self.val_transform)
self.test_set = ImageFolder(self.img_root / Path("test"),
transform=self.val_transform)
def train_dataloader(self):
return DataLoader(self.train_set, batch_size=self.batch_size,
shuffle=True, num_workers=2)
def val_dataloader(self):
return DataLoader(self.val_set, batch_size=self.batch_size,
shuffle=False, num_workers=2)
def test_dataloader(self):
return DataLoader(self.test_set, batch_size=self.batch_size,
shuffle=False, num_workers=2)
# %% Создание трансформаций
transform_train = transforms.Compose([
transforms.Resize((224, 224)),
transforms.RandomHorizontalFlip(p=.5),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
transform_test = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# %% Создание экземляра класса и вывод объектов класса
dm = MyImageDataset(train_transform=transform_train,
val_transform=transform_test,
img_root="./Datasets/weather-dataset-split")
dm.setup()
print({k:v for k, v in enumerate(dm.test_set.classes)})
+229
View File
@@ -0,0 +1,229 @@
# %% Импорт библиотек
import torch
import torch.nn as nn
import pytorch_lightning as pl
from torch.utils.data import DataLoader
from torchmetrics import MetricCollection
from torchvision import datasets, transforms
from torchmetrics.classification import Accuracy, Precision, Recall, F1Score
# Автоматическое логирование результатов
from pytorch_lightning.loggers import WandbLogger, TensorBoardLogger, CSVLogger
# сохранение весов
from pytorch_lightning.callbacks.model_checkpoint import ModelCheckpoint
# автоматическое отслеживание lr
from pytorch_lightning.callbacks import LearningRateMonitor
# ранние остановки
from pytorch_lightning.callbacks.early_stopping import EarlyStopping
# %% Гиперпараметры
num_epoch = 8
batch_size = 64
# %% Создание модели PyTorch
class MyTorchCNNModel(nn.Module):
def __init__(self,
output_size: int = 10):
super().__init__()
self.output_size = output_size
self.feature = nn.Sequential(
nn.LazyConv2d(64, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2)
)
self.head = nn.Conv2d(256, self.output_size, kernel_size=1)
self.pool = nn.AdaptiveAvgPool2d((1, 1))
def forward(self, image):
x = self.feature(image)
x = self.head(x)
x = self.pool(x)
return x.flatten(1)
# %% Создание модели PyTorch Lightning
class MyNNExample(pl.LightningModule):
def __init__(self,
model: nn.Module,
eta=3e-4):
super().__init__()
self.save_hyperparameters(ignore=["model"])
# device = "cuda" if torch.cuda.is_available() else "cpu"
self.model = model
self.criterion = nn.CrossEntropyLoss()
self.train_metrics = MetricCollection({
"accuracy": Accuracy(task="multiclass",
num_classes=model.output_size),
"precision": Precision(task="multiclass",
num_classes=model.output_size,
average="macro"),
"recall": Recall(task="multiclass",
num_classes=model.output_size,
average="macro"),
"f1": F1Score(task="multiclass",
num_classes=model.output_size,
average="macro")
})
self.val_metrics = self.train_metrics.clone()
self.test_metrics = self.train_metrics.clone()
def forward(self, image):
return self.model(image)
def _shared_step(self,
sample,
stage,
metrics):
x, y = sample
logits = self.forward(x.to(torch.float32))
preds = torch.argmax(logits, 1)
loss = self.criterion(logits, y.to(torch.int64))
metrics.update(preds, y)
self.log(f"{stage}_loss", loss,
prog_bar=True, on_epoch=True, on_step=False)
return loss
def _log_metrics(self, stage, metrics):
computed = metrics.compute()
for name, value in computed.items():
self.log(f"{stage}_{name}", value, prog_bar=True)
metrics.reset()
def training_step(self, batch, batch_idx):
return self._shared_step(batch, "train", self.train_metrics)
def on_train_epoch_end(self):
self._log_metrics("train", self.train_metrics)
def validation_step(self, batch, batch_idx):
return self._shared_step(batch, "val", self.val_metrics)
def on_validation_epoch_end(self):
self._log_metrics("val", self.val_metrics)
def test_step(self, batch, batch_idx):
return self._shared_step(batch, "test", self.test_metrics)
def on_test_epoch_end(self):
self._log_metrics("test", self.test_metrics)
def configure_optimizers(self):
optimizer = torch.optim.Adam(self.parameters(), lr=self.hparams.eta)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
optimizer, patience=5, mode="min"
)
return {
"optimizer": optimizer,
"lr_scheduler": {
"scheduler": scheduler,
"monitor": "val_loss",
"interval": "epoch",
}
}
# %% Загрузка набора данных
image_transforms = transforms.Compose([
transforms.ToTensor()
])
train_dataset = datasets.MNIST(
root="./Datasets",
train=True,
download=False, # Если скачен, то убрать
transform=image_transforms
)
test_dataset = datasets.MNIST(
root="./Datasets",
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)
# %% Запуск обучения
model = MyNNExample(model=MyTorchCNNModel())
callbacks = [
ModelCheckpoint(dirpath="models",
filename="{epoch}_{val_accuracy:.4f}_{val_loss:.4f}",
auto_insert_metric_name=False,
save_last=True,
save_top_k=1,
monitor="val_loss",
mode="min"),
LearningRateMonitor(logging_interval="step"),
EarlyStopping(monitor="val_loss",
min_delta=2e-4,
patience=10,
verbose=False,
mode="min")
]
trainer = pl.Trainer(
max_epochs=num_epoch,
accelerator="gpu",
devices=1,
logger=CSVLogger("./train_logs", name="mnist_csv",
version=None, flush_logs_every_n_steps=100),
callbacks=callbacks
)
trainer.fit(model=model,
train_dataloaders=train_loader,
val_dataloaders=test_loader)
# %% Инференс модели
checkpoint_path = "./models/6_0.9460_0.1643.ckpt"
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = MyNNExample.load_from_checkpoint(checkpoint_path,
model=MyTorchCNNModel(output_size=10))
model = model.to(device)
model.eval()
image, true_label = test_dataset[42]
image_batch = image.unsqueeze(0).to(device)
with torch.no_grad():
logits = model(image_batch)
probs = torch.softmax(logits, dim=1)
pred_label = logits.argmax(dim=1).item()
print(f"Предсказание: {pred_label}")
print(f"Истинная метка: {true_label}")
print(f"Уверенность: {probs[0, pred_label].item():.2%}")
+19
View File
@@ -0,0 +1,19 @@
Установка с созданием новой среды:
```bash
conda env create -f environment.yml
```
Установка в уже созданную среду:
```bash
conda env update --file environment.yml --prune
```
Для переноса в другую ОС:
```bash
conda env export --from-history > environment.yml
```
Просто сохранить зависимости:
```bash
conda env export > environment.yml
```
+37
View File
@@ -0,0 +1,37 @@
# %% Импорт библиотек
import torch
import torch.nn as nn
# %% Входные переменные
input_ = 32*32
hidden_1 = 5
hidden_2 = 12
output = 2
learning_rate = 1e-3
weights_1 = torch.rand(size=(input_, hidden_1), requires_grad=True)
weights_2 = torch.rand(size=(hidden_1, hidden_2), requires_grad=True)
weights_3 = torch.rand(size=(hidden_2, output), requires_grad=True)
random_input = torch.rand(1, input_)
true_label = torch.zeros(1, output)
true_label[0][0] = 1
# %% Цикл обучения
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD([weights_1, weights_2, weights_3], lr=learning_rate)
hidden_layer_1 = random_input @ weights_1
hidden_layer_2 = hidden_layer_1 @ weights_2
output_layer = hidden_layer_2 @ weights_3
loss = criterion(output_layer, true_label)
print(loss)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(weights_1._grad)
print(weights_2._grad)
print(weights_3._grad)
+427
View File
@@ -0,0 +1,427 @@
name: CV
channels:
- defaults
- conda-forge
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=5.1=52_gnu
- aiodns=3.6.1=py312h06a4308_0
- aiohappyeyeballs=2.6.2=py312h06a4308_0
- aiohttp=3.14.3=py312h47b2149_0
- aiosignal=1.4.0=py312h06a4308_0
- alabaster=1.0.0=py312h06a4308_0
- alsa-lib=1.2.15.3=h47b2149_0
- anyio=4.12.1=py312h06a4308_0
- aom=3.13.2=h664349e_0
- argon2-cffi=25.1.0=py312h06a4308_0
- argon2-cffi-bindings=25.1.0=py312hee96239_0
- arrow=1.4.0=py312h06a4308_0
- astroid=4.0.3=py312h06a4308_0
- asttokens=3.0.1=py312h06a4308_0
- async-lru=2.3.0=py312h06a4308_0
- asyncssh=2.24.0=py312h06a4308_0
- attrs=26.1.0=py312h0c820a0_0
- autopep8=2.0.4=pyhd3eb1b0_0
- babel=2.18.0=py312h06a4308_0
- bcrypt=5.0.0=py312h498d7c9_0
- beartype=0.22.9=py312h06a4308_0
- beautifulsoup4=4.15.0=py312h06a4308_0
- binaryornot=0.6.0=py312h06a4308_0
- black=26.5.1=py312h06a4308_0
- blas=1.0=openblas
- bleach=6.4.0=py312h06a4308_0
- brotlicffi=1.2.0.1=py312h7354ed3_0
- bzip2=1.0.8=h5eee18b_6
- c-ares=1.34.7=hd44998d_0
- ca-certificates=2026.8.13=h06a4308_0
- cairo=1.18.4=h44eff21_0
- cattrs=26.1.0=py312h06a4308_0
- certifi=2026.7.22=py312h06a4308_0
- cffi=2.1.1=py312h3b52fac_0
- chardet=5.2.0=py312h06a4308_1
- charset-normalizer=3.4.7=py312h06a4308_0
- click=8.5.0=py312h06a4308_0
- cloudpickle=3.1.2=py312h06a4308_0
- comm=0.2.3=py312h06a4308_0
- cookiecutter=2.7.1=py312h06a4308_0
- cpython=3.12.14=py312hd8ed1ab_3
- cryptography=50.0.0=py312hef7b95b_0
- cuda-crt-tools=12.9.86=ha770c72_2
- cuda-cudart=12.9.79=h5888daf_0
- cuda-cudart_linux-64=12.9.79=h3f2d84a_0
- cuda-cuobjdump=12.9.82=hffce074_1
- cuda-cupti=12.9.79=h676940d_2
- cuda-nvcc-tools=12.9.86=he02047a_2
- cuda-nvdisasm=12.9.88=hffce074_1
- cuda-nvrtc=12.9.86=hecca717_1
- cuda-nvtx=12.9.79=hecca717_1
- cuda-nvvm-tools=12.9.86=h4bc722e_2
- cuda-version=12.9=h4f385c5_3
- cudnn=9.25.1.1=ha9dd1bd_0
- cusparselt=0.7.0.0=hcd2ec93_0
- cyrus-sasl=2.1.28=h2f687bf_5
- dav1d=1.5.3=h12c9f22_1
- dbus=1.16.2=h5bd4931_0
- debugpy=1.8.21=py312h7354ed3_0
- decorator=5.3.1=py312h06a4308_0
- defusedxml=0.7.1=pyhd3eb1b0_0
- diff-match-patch=20200713=pyhd3eb1b0_0
- dill=0.4.1=py312h06a4308_0
- docstring-to-markdown=0.17=py312h06a4308_0
- docutils=0.21.2=py312h06a4308_1
- executing=2.2.1=py312h06a4308_1
- expat=2.8.4=h7354ed3_0
- filelock=3.32.5=pyhd8ed1ab_0
- flake8=7.1.1=py312h06a4308_0
- fmt=12.1.0=h44f220b_0
- fontconfig=2.15.0=h2c49b7f_0
- freetype=2.14.1=hf5b9546_0
- fribidi=1.0.16=h10f3c28_1
- frozenlist=1.8.0=py312hbdd6827_0
- fsspec=2026.7.0=pyhd8ed1ab_0
- gettext=0.25.1=h92eb808_0
- gettext-tools=0.25.1=h6a67909_0
- giflib=6.1.3=h621bd71_1
- gmp=6.3.0=hfd2156b_3
- gmpy2=2.3.1=py312h04c1168_0
- graphite2=1.3.15=h9ba177b_0
- h11=0.16.0=py312h06a4308_1
- harfbuzz=12.3.0=h79d275a_1
- httpcore=1.0.9=py312h06a4308_0
- httpx=0.28.1=py312h06a4308_1
- icu=73.1=h6a678d5_0
- idna=3.18=py312h06a4308_0
- imagesize=1.4.1=py312h06a4308_0
- importlib-metadata=9.0.0=py312h06a4308_0
- inflection=0.5.1=py312h06a4308_1
- intervaltree=3.2.1=pyhd3eb1b0_0
- ipykernel=6.31.0=py312h7040dfc_0
- ipython=9.15.0=py312h06a4308_0
- ipython_pygments_lexers=1.1.1=py312h06a4308_0
- isort=8.0.1=py312h06a4308_1
- jansson=2.15.0=hbcba0ee_0
- jaraco.classes=3.4.0=py312h06a4308_0
- jaraco.context=6.1.0=py312h06a4308_0
- jaraco.functools=4.4.0=py312h06a4308_0
- jedi=0.19.2=py312h06a4308_0
- jeepney=0.7.1=pyhd3eb1b0_0
- jellyfish=1.2.1=py312h498d7c9_0
- jinja2=3.1.6=pyhcf101f3_1
- jpeg=9f=he7e78df_1
- json5=0.15.0=py312h06a4308_0
- jsonschema=4.26.0=py312h06a4308_0
- jsonschema-specifications=2025.9.1=py312h06a4308_0
- jupyter-builder=1.2.0=py312h06a4308_0
- jupyter-lsp=2.3.0=py312h06a4308_0
- jupyter_client=8.10.0=py312h06a4308_0
- jupyter_core=5.9.1=py312h06a4308_0
- jupyter_events=0.12.1=py312h06a4308_0
- jupyter_server=2.20.0=py312h06a4308_0
- jupyter_server_terminals=0.5.4=py312h06a4308_0
- jupyterlab=4.6.2=py312h06a4308_1
- jupyterlab_pygments=0.3.0=py312h06a4308_0
- jupyterlab_server=2.28.0=py312h06a4308_1
- kagglehub=1.0.2=py312h06a4308_0
- kagglesdk=0.1.35=py312h06a4308_0
- keyring=25.7.0=py312h06a4308_0
- krb5=1.20.1=h143b758_1
- lcms2=2.19=he283960_0
- ld_impl_linux-64=2.44=h9e0c5a2_3
- lerc=4.1.0=h7354ed3_2
- libabseil=20260107.0=cxx17_h6199ee8_0
- libasprintf=0.25.1=hf2ab22a_0
- libasprintf-devel=0.25.1=hf2ab22a_0
- libavif=1.3.0=h2b90b00_1
- libbrotlicommon=1.2.0=h32cd6e7_0
- libbrotlidec=1.2.0=ha2c5f68_0
- libbrotlienc=1.2.0=h2e96acb_0
- libcap=2.78=h084b8d7_1
- libcublas=12.9.2.10=h676940d_0
- libcudnn=9.25.1.1=hfd19c1e_0
- libcudnn-dev=9.25.1.1=ha9dd1bd_0
- libcudss0=0.4.0.2=he55f5cd_2
- libcufft=11.4.1.4=hecca717_1
- libcufile=1.14.1.1=hbc026e6_1
- libcups=2.4.19=h23114ae_0
- libcurand=10.3.10.19=h676940d_1
- libcurl=8.19.0=ha05a353_0
- libcusolver=11.7.5.82=h676940d_2
- libcusparse=12.5.10.65=hecca717_2
- libdeflate=1.22=h5eee18b_0
- libdrm=2.4.134=h9c74679_0
- libedit=3.1.20230828=h5eee18b_0
- libegl=1.7.0=h5eee18b_2
- libev=4.33=h7f8727e_1
- libevent=2.1.12=hdbd6064_1
- libexpat=2.8.4=h7354ed3_0
- libffi=3.4.8=h06d3fd0_3
- libgcc=15.2.0=h69a1729_8
- libgcc-ng=15.2.0=h166f726_8
- libgettextpo=0.25.1=hf2ab22a_0
- libgettextpo-devel=0.25.1=hf2ab22a_0
- libgfortran=15.2.0=h69a702a_20
- libgfortran5=15.2.0=h68bc16d_20
- libgl=1.7.0=h5eee18b_2
- libglib=2.88.3=ha16e27a_0
- libglvnd=1.7.0=h5eee18b_2
- libglx=1.7.0=h5eee18b_2
- libgomp=15.2.0=h4751f2c_8
- libiconv=1.18=h75a1612_0
- libidn2=2.3.8=hf80d704_0
- libkrb5=1.22.1=h6d2bf13_0
- libllvm20=20.1.8=h465586c_0
- libllvm22=22.1.2=hfb1d434_0
- liblzma=5.8.2=hb03c661_0
- libnghttp2=1.67.1=h697f920_0
- libnl=3.11.0=hb03c661_1
- libnvjitlink=12.9.86=hecca717_2
- libnvjpeg=12.4.0.76=h7354ed3_0
- libopenblas=0.3.34=pthreads_h94d23a6_0
- libopengl=1.7.0=h5eee18b_2
- libopenjpeg=2.5.4=hee96239_1
- libopus=1.6.1=h9f10d21_1
- libpciaccess=0.18=h5eee18b_0
- libpng=1.6.56=h22898a0_0
- libpq=17.9=h0cb448f_1
- libprotobuf=6.33.5=h4435b4a_0
- libre2-11=2025.11.05=h3356fce_1
- libsodium=1.0.21=h83fc4cd_1
- libspatialindex=2.1.0=hf7d4471_0
- libssh2=1.11.1=hfbabe93_1
- libstdcxx=15.2.0=h39759b7_8
- libstdcxx-ng=15.2.0=hc03a8fd_8
- libsystemd0=261.2=h6f4a2f1_0
- libtiff=4.7.1=h029b1ac_0
- libtirpc=1.3.7=h47b2149_0
- libtorch=2.12.0=gpu_cuda129_h0198de7_200
- libudev1=261.2=h6f4a2f1_0
- libunistring=1.4.2=h34b0ebb_0
- libuuid=1.41.5=h5eee18b_0
- libuv=1.52.1=h280c20c_1
- libvpx=1.16.0=h4b463fa_0
- libwebp-base=1.6.0=hb7bb969_0
- libxcb=1.17.0=h9b100fa_0
- libxkbcommon=1.9.1=h69220b7_0
- libxkbfile=1.1.0=hf54ef8a_2
- libxml2=2.13.9=h2c43086_0
- libxslt=1.1.43=h28b3bda_0
- libzlib=1.3.2=h47b2149_0
- lightning-utilities=0.15.3=pyhd8ed1ab_0
- lmdb=1.0.0=hfe55579_0
- lsprotocol=2025.0.0=py312h06a4308_1
- lz4-c=1.9.4=h7354ed3_5
- magma=2.10.0=hfa452ef_1
- markdown-it-py=4.2.0=py312h06a4308_0
- markupsafe=3.0.3=py312h47b2149_0
- matplotlib-inline=0.2.2=py312h06a4308_0
- mccabe=0.7.0=pyhd3eb1b0_0
- mdurl=0.1.2=py312h06a4308_0
- mesalib=25.1.5=h3583ad3_5
- minizip=4.2.1=h7e49174_0
- mistune=3.3.3=py312h06a4308_0
- more-itertools=11.0.2=py312h06a4308_0
- mpc=1.4.0=ha2cb11d_1
- mpfr=4.2.2=ha2cb11d_1
- mpmath=1.4.1=pyhd8ed1ab_0
- multidict=6.7.1=py312h47b2149_0
- mypy_extensions=1.1.0=py312h06a4308_0
- mysql=9.3.0=hef88997_5
- mysql-client=9.3.0=hf2c64df_5
- mysql-common=9.3.0=h9e076cb_5
- mysql-devel=9.3.0=h9e076cb_5
- mysql-libs=9.3.0=h6ecde68_5
- mysql-server=9.3.0=h284711e_5
- nbclient=0.11.0=py312h06a4308_0
- nbconvert=7.17.1=py312h06a4308_0
- nbconvert-core=7.17.1=py312h06a4308_0
- nbconvert-pandoc=7.17.1=py312h06a4308_0
- nbformat=5.11.0=py312h06a4308_0
- nccl=2.30.7.1=h4d09622_0
- ncurses=6.6=hfaaeb4e_0
- nest-asyncio=1.6.0=py312h06a4308_1
- nest-asyncio2=1.7.2=py312h06a4308_0
- networkx=3.6.1=pyhcf101f3_0
- nomkl=1.0=h5ca1d4c_0
- notebook-shim=0.2.4=py312h06a4308_1
- nspr=4.39=h6e5897e_0
- nss=3.125=hcf7f90d_0
- numpy=2.5.2=py312h35deafb_0
- numpy-base=2.5.2=py312h4bc27c9_0
- numpydoc=1.10.0=py312h06a4308_0
- openjpeg=2.5.4=h4e0627c_1
- openldap=2.6.12=h007892f_1
- openssl=3.6.4=h781a0a9_0
- optree=0.20.0=py312ha5105d2_1
- packaging=26.3=py312h06a4308_0
- pandoc=3.8=h06a4308_0
- pandocfilters=1.5.1=py312h06a4308_0
- parso=0.8.7=py312h06a4308_0
- pathspec=1.1.1=py312h06a4308_0
- pcre2=10.46=hf426167_0
- pexpect=4.9.0=py312h06a4308_1
- pickleshare=0.7.5=pyhd3eb1b0_1003
- pillow=12.2.0=py312haa37d31_0
- pip=26.2.1=pyhc872135_0
- pixman=0.46.4=h86ba9f7_1
- platformdirs=4.11.0=py312h06a4308_0
- pluggy=1.6.0=py312h06a4308_0
- prometheus_client=0.24.1=py312h06a4308_0
- prompt-toolkit=3.0.53=py312h06a4308_0
- prompt_toolkit=3.0.53=hd3eb1b0_0
- propcache=0.5.2=py312h47b2149_0
- protobuf=6.33.5=py312h7354ed3_0
- psutil=7.2.2=py312h47b2149_0
- pthread-stubs=0.3=h47b2149_2
- ptyprocess=0.7.0=pyhd3eb1b0_3
- pure_eval=0.2.3=py312h06a4308_0
- pybind11=3.0.4=py312h24d9097_0
- pybind11-abi=11=hd3eb1b0_0
- pybind11-global=3.0.4=py312h24d9097_0
- pycares=4.10.0=py312ha137689_0
- pycodestyle=2.12.1=py312h06a4308_0
- pycparser=3.0=py312h06a4308_0
- pydocstyle=6.3.0=py312h06a4308_1
- pyflakes=3.2.0=py312h06a4308_0
- pygithub=2.9.1=py312h06a4308_0
- pygments=2.20.0=py312h06a4308_0
- pyjwt=2.13.0=py312h06a4308_0
- pylint=4.0.6=py312h06a4308_0
- pylint-venv=3.0.3=py312h06a4308_0
- pyls-spyder=0.4.0=pyhd3eb1b0_0
- pynacl=1.6.2=py312h83fc4cd_1
- pyqt=6.11.0=py312h0eb9b55_0
- pyqt6-sip=13.11.1=py312h47b2149_0
- pyqtwebengine=6.11.0=py312hdc71590_0
- pysocks=1.7.1=py312h06a4308_1
- python=3.12.14=hc5f7cf0_0
- python-dateutil=2.9.0post0=py312h06a4308_2
- python-fastjsonschema=2.22.1=py312h06a4308_0
- python-json-logger=4.1.0=py312h06a4308_0
- python-lsp-black=2.0.0=py312h06a4308_1
- python-lsp-jsonrpc=1.1.2=pyhd3eb1b0_0
- python-lsp-ruff=2.3.0=py312h06a4308_0
- python-lsp-server=1.14.0=py312h7040dfc_1
- python-slugify=8.0.4=py312h06a4308_0
- python-tzdata=2026.3=pyhd3eb1b0_0
- python_abi=3.12=4_cp312
- pytokens=0.4.1=py312h47b2149_0
- pytoolconfig=1.3.1=py312h06a4308_0
- pytorch=2.12.0=gpu_cuda129_py312h95261ae_200
- pytorch-lightning=2.6.5=pyhcf101f3_0
- pyuca=1.2=py312h06a4308_1
- pyxdg=0.28=py312h06a4308_0
- pyyaml=6.0.3=py312h8a5da7c_1
- pyzmq=27.1.0=py312hcf8288c_1
- qdarkstyle=3.2.3=pyhd3eb1b0_0
- qstylizer=0.2.4=py312h06a4308_0
- qtawesome=1.4.2=py312h4333747_0
- qtbase=6.11.0=h9201cad_0
- qtconsole=5.7.2=py312h06a4308_0
- qtdeclarative=6.11.0=h89ee561_1
- qtpy=2.4.3=py312h06a4308_0
- qtsvg=6.11.0=hbecefc2_1
- qttools=6.11.0=h0af0a4d_1
- qtwayland=6.11.0=h9a62c7d_0
- qtwebchannel=6.11.0=h7ebffc9_1
- qtwebengine=6.11.0=hb54882b_0
- qtwebsockets=6.11.0=h03d4248_1
- rdma-core=65.0=h7b4f2e6_0
- re2=2025.11.05=h71b5c5c_1
- readline=8.3=hc2a1206_0
- referencing=0.37.0=py312h06a4308_0
- requests=2.34.2=py312h06a4308_0
- rfc3339-validator=0.1.4=py312h06a4308_0
- rfc3986-validator=0.1.1=py312h06a4308_0
- rich=15.0.0=py312h06a4308_0
- roman-numerals=3.1.0=py312h06a4308_0
- rope=1.14.0=py312h06a4308_0
- rpds-py=0.28.0=py312h498d7c9_0
- rtree=1.4.1=py312hd966e25_1
- ruff=0.15.1=py312h6e1b9ff_0
- secretstorage=3.5.0=py312h3e8c6aa_0
- send2trash=1.8.3=py312h06a4308_0
- setuptools=80.10.2=py312h06a4308_0
- sip=6.15.1=py312he6c97cb_0
- six=1.17.0=py312h06a4308_0
- sleef=3.9.0=ha0421bc_0
- snappy=1.2.2=h4bcf44c_1
- snowballstemmer=3.1.1=py312h06a4308_0
- sortedcontainers=2.4.0=pyhd3eb1b0_0
- soupsieve=2.8.4=py312h06a4308_0
- sphinx=9.1.0=py312h893fd2c_0
- sphinxcontrib-applehelp=2.0.0=pyhd3eb1b0_1
- sphinxcontrib-devhelp=2.0.0=pyhd3eb1b0_0
- sphinxcontrib-htmlhelp=2.1.0=pyhd3eb1b0_0
- sphinxcontrib-jsmath=1.0.1=pyhd3eb1b0_0
- sphinxcontrib-qthelp=2.0.0=pyhd3eb1b0_1
- sphinxcontrib-serializinghtml=2.0.0=pyhd3eb1b0_0
- spirv-tools=2026.1=h24d9097_0
- spyder=6.1.5=py312h06a4308_0
- spyder-kernels=3.1.6=py312h7040dfc_0
- sqlite=3.53.2=h795bf6d_0
- stack_data=0.6.3=py312h06a4308_0
- superqt=0.8.1=py312ha9b9ae4_0
- sympy=1.14.0=pyh2585a3b_106
- terminado=0.18.1=py312h06a4308_1
- text-unidecode=1.3=pyhd3eb1b0_0
- textdistance=4.6.3=py312he3bba80_1
- three-merge=0.1.1=pyhd3eb1b0_0
- tinycss2=1.5.1=py312h06a4308_0
- tk=8.6.15=h54e0aa7_0
- tomlkit=0.15.1=py312h06a4308_0
- torchao=0.17.0=cuda129_py312hd0f1f67_2
- torchmetrics=1.9.0=pyhd8ed1ab_0
- torchvision=0.27.0=cuda129py312h054a6d6_100
- tornado=6.5.8=py312h47b2149_0
- tqdm=4.70.0=pyh8f84b5b_0
- traitlets=5.15.0=py312h06a4308_0
- triton=3.7.0=cuda129py312h338dc61_1
- typing-extensions=4.16.0=py312h06a4308_0
- typing_extensions=4.16.0=py312h06a4308_0
- tzdata=2026c=he532380_0
- ujson=5.13.0=py312h7354ed3_0
- urllib3=2.7.0=py312h06a4308_0
- uvloop=0.22.1=py312h939ff40_1
- watchdog=6.0.0=py312h06a4308_0
- wayland=1.24.0=hdac8c69_0
- wcwidth=0.8.2=py312h06a4308_0
- webencodings=0.6.1=py312h06a4308_0
- websocket-client=1.8.0=py312h06a4308_0
- whatthepatch=1.0.7=py312h06a4308_0
- wheel=0.47.0=py312h06a4308_0
- wurlitzer=3.1.1=py312h06a4308_0
- xcb-util=0.4.1=h5eee18b_2
- xcb-util-cursor=0.1.5=h5eee18b_0
- xcb-util-image=0.4.0=h5eee18b_2
- xcb-util-keysyms=0.4.1=h5eee18b_0
- xcb-util-renderutil=0.3.10=h5eee18b_0
- xcb-util-wm=0.4.2=h5eee18b_0
- xkeyboard-config=2.48=h382ed1a_0
- xorg-libice=1.1.2=h9b100fa_0
- xorg-libsm=1.2.6=h9b100fa_0
- xorg-libx11=1.8.12=h9b100fa_1
- xorg-libxau=1.0.12=h9b100fa_0
- xorg-libxcomposite=0.4.6=h9b100fa_2
- xorg-libxdamage=1.1.6=h9b100fa_0
- xorg-libxdmcp=1.1.5=h9b100fa_0
- xorg-libxext=1.3.7=h1ce37a7_0
- xorg-libxfixes=6.0.1=h9b100fa_0
- xorg-libxi=1.8.2=h9b100fa_0
- xorg-libxrandr=1.5.4=h9b100fa_0
- xorg-libxrender=0.9.12=h9b100fa_0
- xorg-libxshmfence=1.3.3=h9b100fa_0
- xorg-libxtst=1.2.5=h9b100fa_3
- xorg-libxxf86vm=1.1.6=h9b100fa_0
- xorg-xorgproto=2024.1=h47b2149_2
- xz=5.8.2=h448239c_0
- yaml=0.2.5=h591646f_1
- yapf=0.43.0=py312h06a4308_0
- yarl=1.24.2=py312h47b2149_0
- zeromq=4.3.5=hf801bfb_2
- zipp=4.1.0=py312h06a4308_0
- zlib=1.3.2=h47b2149_0
- zstd=1.5.7=h11fc155_0
- pip:
- pyqt6-qt6==6.11.2
- split-folders==0.6.1
prefix: /home/daniel/anaconda3/envs/CV
+13
View File
@@ -0,0 +1,13 @@
name: CV
channels:
- defaults
dependencies:
- python=3.12
- jupyterlab
- spyder
- pytorch-lightning
- torchao
- beartype
- torchvision
- kagglehub
prefix: /home/daniel/anaconda3/envs/CV
+189
View File
@@ -0,0 +1,189 @@
# %% Импорт библиотек
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')}")