prototorch_models/docs/source/tutorial.ipynb
Jensun Ravichandran 246719b837 [DOC] Add tutorial
2021-05-18 19:41:58 +02:00

461 lines
13 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "f176387e",
"metadata": {},
"source": [
"# A short tutorial for the `prototorch.models` plugin"
]
},
{
"cell_type": "markdown",
"id": "08f641b4",
"metadata": {},
"source": [
"## Introduction"
]
},
{
"cell_type": "markdown",
"id": "d0d8096f",
"metadata": {},
"source": [
"This is a short tutorial for the [models](https://github.com/si-cim/prototorch_models) plugin of the [ProtoTorch](https://github.com/si-cim/prototorch) framework.\n",
"\n",
"[ProtoTorch](https://github.com/si-cim/prototorch) provides [torch.nn](https://pytorch.org/docs/stable/nn.html) modules and utilities to implement prototype-based models. However, it is up to the user to put these modules together into models and handle the training of these models. Expert machine-learning practioners and researchers sometimes prefer this level of control. However, this leads to a lot of boilerplate code that is essentially same across many projects. Needless to say, this is a source of a lot of frustration. [PyTorch-Lightning](https://pytorch-lightning.readthedocs.io/en/latest/) is a framework that helps avoid a lot of this frustration by handling the boilerplate code for you so you don't have to reinvent the wheel every time you need to implement a new model.\n",
"\n",
"With the [prototorch.models](https://github.com/si-cim/prototorch_models) plugin, we've gone one step further and pre-packaged commonly used prototype-models like GMLVQ as [Lightning-Modules](https://pytorch-lightning.readthedocs.io/en/latest/api/pytorch_lightning.core.lightning.html?highlight=lightning%20module#pytorch_lightning.core.lightning.LightningModule). With only a few lines to code, it is now possible to build and train prototype-models. It quite simply cannot get any simpler than this."
]
},
{
"cell_type": "markdown",
"id": "7b57f991",
"metadata": {},
"source": [
"## Basics"
]
},
{
"cell_type": "markdown",
"id": "009efb2c",
"metadata": {},
"source": [
"First things first. When working with the models plugin, you'll probably need `torch`, `prototorch` and `pytorch_lightning`. So, we recommend that you import all three like so:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "d8eb606b",
"metadata": {},
"outputs": [],
"source": [
"import prototorch as pt\n",
"import pytorch_lightning as pl\n",
"import torch"
]
},
{
"cell_type": "markdown",
"id": "d5daf6be",
"metadata": {},
"source": [
"### Building Models"
]
},
{
"cell_type": "markdown",
"id": "7ddc8d04",
"metadata": {},
"source": [
"Let's start by building a `GLVQ` model. It is one of the simplest models to build. The only requirements are a prototype distribution and an initializer."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "39cc97fc",
"metadata": {},
"outputs": [],
"source": [
"model = pt.models.GLVQ(\n",
" hparams=dict(distribution=[1, 1, 1]),\n",
" prototype_initializer=pt.components.Zeros(2),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "54dc20ec",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"GLVQ(\n",
" (proto_layer): LabeledComponents(components.shape: (3, 2))\n",
" (train_acc): Accuracy()\n",
")\n"
]
}
],
"source": [
"print(model)"
]
},
{
"cell_type": "markdown",
"id": "3927cfea",
"metadata": {},
"source": [
"The `distribution` argument describes the prototype distribution. If it is a Python [list](https://docs.python.org/3/tutorial/datastructures.html), it is assumed that there are as many entries in this list as there are classes, and the number at each location of this list describes the number of prototypes to be used for that particular class. So, `[1, 1, 1]` implies that we have three classes with one prototype per class. If it is a Python [tuple](https://docs.python.org/3/tutorial/datastructures.html), it a shorthand of `(num_classes, prototypes_per_class)` is assumed. The `prototype_initializer` argument describes how the prototypes are meant to be initialized. This argument has to be an instantiated object of some kind of [ComponentInitializer](https://github.com/si-cim/prototorch/blob/dev/prototorch/components/initializers.py#L27). If this is a [DimensionAwareInitializer](https://github.com/si-cim/prototorch/blob/dev/prototorch/components/initializers.py), this only requires a dimension arugment that describes the vector dimension of the prototypes. So, `pt.components.Zeros(2)` creates 2d-vector prototypes all initialized to zeros."
]
},
{
"cell_type": "markdown",
"id": "4b10e1bf",
"metadata": {},
"source": [
"It is also possible to use a [ClassAwareInitializer](https://github.com/si-cim/prototorch/blob/dev/prototorch/components/initializers.py). However, this type of initializer requires data to be instantiated.\n"
]
},
{
"cell_type": "markdown",
"id": "69d64f38",
"metadata": {},
"source": [
"For a full list of available models, please check the [prototorch_models documentation](https://prototorch-models.readthedocs.io/en/latest/)."
]
},
{
"cell_type": "markdown",
"id": "b17c1476",
"metadata": {},
"source": [
"### Data"
]
},
{
"cell_type": "markdown",
"id": "b5d6d28e",
"metadata": {},
"source": [
"The preferred way to working with data in `torch` is to use the [Dataset and Dataloader API](https://pytorch.org/tutorials/beginner/basics/data_tutorial.html). There a few pre-packaged datasets available under `prototorch.datasets`. See [here](https://prototorch.readthedocs.io/en/latest/api.html#module-prototorch.datasets) for a full list of available datasets."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "9a104e40",
"metadata": {},
"outputs": [],
"source": [
"train_ds = pt.datasets.Iris(dims=[0, 2])"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "ebe9036c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"prototorch.datasets.iris.Iris"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(train_ds)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "40fc6e22",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"((150, 2), (150,))"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_ds.data.shape, train_ds.targets.shape"
]
},
{
"cell_type": "markdown",
"id": "413a1d4e",
"metadata": {},
"source": [
"Once we have such a dataset, we could wrap it in a `Dataloader` to load the data in batches, and possibly apply some transformations on the fly."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "cc8cbc5d",
"metadata": {},
"outputs": [],
"source": [
"train_loader = torch.utils.data.DataLoader(train_ds, batch_size=2)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "0788db2f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"torch.utils.data.dataloader.DataLoader"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(train_loader)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "b0aa9ef5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x_batch=tensor([[5.1000, 1.4000],\n",
" [4.9000, 1.4000]]), y_batch=tensor([0., 0.])\n"
]
}
],
"source": [
"x_batch, y_batch = next(iter(train_loader))\n",
"print(f\"{x_batch=}, {y_batch=}\")"
]
},
{
"cell_type": "markdown",
"id": "d8c63bd8",
"metadata": {},
"source": [
"This perhaps seems like a lot of work for a small dataset that fits completely in memory. However, this comes in very handy when dealing with huge datasets that can only be processed in batches."
]
},
{
"cell_type": "markdown",
"id": "b4bb738f",
"metadata": {},
"source": [
"### Training"
]
},
{
"cell_type": "markdown",
"id": "8da4f8eb",
"metadata": {},
"source": [
"If you're familiar with other deep learning frameworks, you might perhaps expect a `.fit(...)` or `.train(...)` method. However, in PyTorch-Lightning, this is done slightly differently. We first create a trainer and then pass the model and the Dataloader to `trainer.fit(...)` instead. So, it is more functional in style than object-oriented."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "952d90de",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"GPU available: False, used: False\n",
"TPU available: False, using: 0 TPU cores\n"
]
}
],
"source": [
"trainer = pl.Trainer(max_epochs=2, weights_summary=None)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "8937b061",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "ef2e7103c9a14a4d8000ce183675fbfd",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Training: 0it [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"trainer.fit(model, train_loader)"
]
},
{
"cell_type": "markdown",
"id": "915860fe",
"metadata": {},
"source": [
"### From data to a trained model - a very minimal example"
]
},
{
"cell_type": "code",
"execution_count": 38,
"id": "6ce12fc8",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"GPU available: False, used: False\n",
"TPU available: False, using: 0 TPU cores\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "5f7647e1e44c46159e98e92643ac1f9e",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Training: 0it [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"train_ds = pt.datasets.Iris(dims=[0, 2])\n",
"train_loader = torch.utils.data.DataLoader(train_ds, batch_size=32)\n",
"\n",
"model = pt.models.GLVQ(\n",
" dict(distribution=(3, 2), lr=0.1),\n",
" prototype_initializer=pt.components.SMI(train_ds),\n",
")\n",
"\n",
"trainer = pl.Trainer(max_epochs=50, weights_summary=None)\n",
"trainer.fit(model, train_loader)"
]
},
{
"cell_type": "markdown",
"id": "e8094c0b",
"metadata": {},
"source": [
"## Advanced"
]
},
{
"cell_type": "markdown",
"id": "c1acc6aa",
"metadata": {},
"source": [
"### Building Novel Model Architectures"
]
},
{
"cell_type": "markdown",
"id": "e75ba9e0",
"metadata": {},
"source": [
"## FAQs"
]
},
{
"cell_type": "markdown",
"id": "bffea4a1",
"metadata": {},
"source": [
"### How do I Retrieve the prototypes and their respective labels from the model?\n",
"\n",
"For prototype models, the prototypes can be retrieved (as `torch.tensor`) as `model.prototypes`. You can convert it to a NumPy Array by calling `.numpy()` on the tensor if required.\n",
"\n",
"```python\n",
">>> model.prototypes.numpy()\n",
"```\n",
"\n",
"Similarly, the labels of the prototypes can be retrieved via `model.prototype_labels`.\n",
"\n",
"```python\n",
">>> model.prototype_labels\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "ecf33e0a",
"metadata": {},
"source": [
"### How do I make inferences/predictions/recall with my trained model?\n",
"\n",
"The models under [prototorch.models](https://github.com/si-cim/prototorch_models) provide a `.predict(x)` method for making predictions. It is essential that the input to this method is a `torch.tensor` and not a NumPy array.\n",
"\n",
"#### Example\n",
"\n",
"```python\n",
">>> y_pred = model.predict(torch.Tensor(x_train))\n",
"```"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}