<jupyter_start><jupyter_text>Quick start  Simple example Define a PyTorch model:<jupyter_code>import torch
from torch import nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self, n_classes, p_dropout=0.5):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.conv2_drop = nn.Dropout2d(p=p_dropout)
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, n_classes)

    def forward(self, x):
        x = F.relu(F.max_pool2d(self.conv1(x), 2))
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return x<jupyter_output><empty_output><jupyter_text>Define an ``argus.Model`` with ``nn_module``, ``optimizer``, ``loss`` attributes. Each value must be a classor function that returns object (``torch.nn.Module`` for loss and nn_module, ``torch.optim.Optimizer`` for optimizer).<jupyter_code>import argus

class MnistModel(argus.Model):
    nn_module = Net
    optimizer = torch.optim.SGD
    loss = torch.nn.CrossEntropyLoss<jupyter_output><empty_output><jupyter_text>Create an instance of ``MnistModel`` with the specified parameters. Net will be initialized like ``Net(n_classes=10, p_dropout=0.1)``. The same logic is applied for the optimizer ``torch.optim.SGD(lr=0.01)``. Loss will be created without any arguments ``torch.nn.CrossEntropyLoss()``. The model will use the CPU.<jupyter_code>params = {
    'nn_module': {'n_classes': 10, 'p_dropout': 0.1},
    'optimizer': {'lr': 0.01},
    'device': 'cpu'
}

model = MnistModel(params)<jupyter_output><empty_output><jupyter_text>Download MNIST dataset. Create validation and training PyTorch data loaders.<jupyter_code>from torch.utils.data import DataLoader
from torchvision.transforms import Compose, ToTensor, Normalize
from torchvision.datasets import MNIST

data_transform = Compose([ToTensor(), Normalize((0.1307,), (0.3081,))])
train_mnist_dataset = MNIST(download=True, root="mnist_data",
                            transform=data_transform, train=True)
val_mnist_dataset = MNIST(download=False, root="mnist_data",
                          transform=data_transform, train=False)
train_loader = DataLoader(train_mnist_dataset,
                          batch_size=64, shuffle=True)
val_loader = DataLoader(val_mnist_dataset,
                        batch_size=128, shuffle=False)<jupyter_output><empty_output><jupyter_text>Define some callbacks and start training the model for 50 epochs. As metrics, you can use instances of classes inherit from `argus.metrics.Metric` or they names `argus.metrics.Metric.name`.<jupyter_code>from argus.callbacks import MonitorCheckpoint, EarlyStopping, ReduceLROnPlateau

callbacks = [
    MonitorCheckpoint(dir_path='mnist', monitor='val_accuracy', max_saves=3),
    EarlyStopping(monitor='val_accuracy', patience=9),
    ReduceLROnPlateau(monitor='val_accuracy', factor=0.5, patience=3)
]

model.fit(train_loader,
          val_loader=val_loader,
          num_epochs=50,
          metrics=['accuracy'],  # or [argus.metrics.CategoricalAccuracy()]
          callbacks=callbacks)<jupyter_output><empty_output><jupyter_text>Load the model from the best checkpoint.<jupyter_code>from pathlib import Path
from argus import load_model

model_path = Path("mnist/").glob("*.pth")
model_path = sorted(model_path)[-1]
print(f"Load model: {model_path}")
loaded_model = argus.load_model(model_path)
loaded_model<jupyter_output><empty_output><jupyter_text>More flexibility Argus can help you simplify the experiments with different architectures, losses, and optimizers. Let's define an ``argus.Model`` with two models via a dictionary. If you want to use PyTorch losses and optimizers, it's not necessary to define them in the argus model.<jupyter_code>from torchvision.models import resnet18

class FlexModel(argus.Model):
    nn_module = {
        'net': Net,
        'resnet18': resnet18
    }<jupyter_output><empty_output><jupyter_text>Create a model instance. Parameters for nn_module is a tuple where the first element is a name, second is arguments.PyTorch losses and optimizers can be selected by a string with a class name.<jupyter_code>params = {
    'nn_module': ('resnet18', {
        'pretrained': False, 
        'num_classes': 1
    }),
    'optimizer': ('Adam', {'lr': 0.01}),
    'loss': 'CrossEntropyLoss',
    'device': 'cuda'
}

model = FlexModel(params)<jupyter_output><empty_output><jupyter_text>Argus allows managing different parts combinations of a pipeline.<jupyter_code>class MoreFlexModel(argus.Model):
    nn_module = {
        'net': Net,
        'resnet18': resnet18
    }
    optimizer = {
        'SGD': torch.optim.SGD,
        'adam_w': torch.optim.AdamW
    }
    loss = {
        'BCE': nn.BCEWithLogitsLoss,
        'cross_entropy': nn.CrossEntropyLoss,
        'nll': nn.NLLLoss
    }
    prediction_transform = {
        'sigmoid': nn.Sigmoid,
        'Softmax': nn.Softmax
    }


params = {
    'nn_module': ('resnet18', {
        'pretrained': False, 
        'num_classes': 1
    }),
    'optimizer': ('adam_w', {
        'lr': 0.01, 
        'weight_decay': 0.042
    }),
    'loss': ('BCE', {'reduction': 'sum'}),
    'prediction_transform': 'sigmoid',
    'device': 'cuda'
}

model = MoreFlexModel(params)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12380952380952381, 'fraction_numerical': 0.01904761904761905, 'mean_word_length': 3.9242560865644727, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '276796', 'license': 'MIT', 'n_tokens_mistral': 1909, 'n_tokens_neox': 1822, 'n_words': 414, 'path': 'examples/quickstart.ipynb', 'repo_name': 'NickVeld/argus', 'size': '5460'}