From 4aef41dc27a9474e96f1c5a0964a0f1b60a27398 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 23 Apr 2021 16:52:12 +0200 Subject: [PATCH 01/60] add CTrL integration --- avalanche/benchmarks/classic/ctrl.py | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 avalanche/benchmarks/classic/ctrl.py diff --git a/avalanche/benchmarks/classic/ctrl.py b/avalanche/benchmarks/classic/ctrl.py new file mode 100644 index 000000000..958a87cab --- /dev/null +++ b/avalanche/benchmarks/classic/ctrl.py @@ -0,0 +1,30 @@ +import torch +from avalanche.benchmarks import GenericCLScenario, dataset_benchmark +from avalanche.benchmarks.utils import AvalancheTensorDataset +from torchvision import transforms + +import ctrl + + +def CTrL(stream_name): + stream = ctrl.get_stream(stream_name) + + exps = [[], [], []] + norms = [] + for t in stream: + for split, exp in zip(t.datasets, exps): + samples, labels = split.tensors + # samples -= torch.tensor(t.statistics['mean']).view(1, 3, 1, 1) + # samples /= torch.tensor(t.statistics['std']).view(1, 3, 1, 1) + + task_labels = [t.id] * samples.size(0) + dataset = AvalancheTensorDataset(samples, labels.squeeze(1), + task_labels=task_labels) + exp.append(dataset) + norms.append(transforms.Normalize(t.statistics['mean'], + t.statistics['std'])) + return dataset_benchmark( + train_datasets=exps[0], + test_datasets=exps[2], + other_streams_datasets=dict(valid=exps[1]), + ) From e2e6e603ddcc317bdd56e3de3f69656b776030bf Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 23 Apr 2021 18:17:07 +0200 Subject: [PATCH 02/60] Add normalization to each task --- avalanche/benchmarks/classic/ctrl.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/avalanche/benchmarks/classic/ctrl.py b/avalanche/benchmarks/classic/ctrl.py index 958a87cab..e453eeb4e 100644 --- a/avalanche/benchmarks/classic/ctrl.py +++ b/avalanche/benchmarks/classic/ctrl.py @@ -9,20 +9,18 @@ def CTrL(stream_name): stream = ctrl.get_stream(stream_name) + # Train, val and test experiences exps = [[], [], []] - norms = [] for t in stream: + trans = transforms.Normalize(t.statistics['mean'], + t.statistics['std']) for split, exp in zip(t.datasets, exps): samples, labels = split.tensors - # samples -= torch.tensor(t.statistics['mean']).view(1, 3, 1, 1) - # samples /= torch.tensor(t.statistics['std']).view(1, 3, 1, 1) - task_labels = [t.id] * samples.size(0) dataset = AvalancheTensorDataset(samples, labels.squeeze(1), - task_labels=task_labels) + task_labels=task_labels, + transform=trans) exp.append(dataset) - norms.append(transforms.Normalize(t.statistics['mean'], - t.statistics['std'])) return dataset_benchmark( train_datasets=exps[0], test_datasets=exps[2], From ae65f1410f6ca541aec6a416c3ff9f9f3f0a5663 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 4 May 2021 12:39:41 +0200 Subject: [PATCH 03/60] Add tests --- avalanche/benchmarks/classic/ctrl.py | 18 +++++++--- environment-dev.yml | 1 + environment.yml | 1 + tests/test_ctrl.py | 49 ++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 tests/test_ctrl.py diff --git a/avalanche/benchmarks/classic/ctrl.py b/avalanche/benchmarks/classic/ctrl.py index e453eeb4e..5e7a3d0ba 100644 --- a/avalanche/benchmarks/classic/ctrl.py +++ b/avalanche/benchmarks/classic/ctrl.py @@ -1,13 +1,20 @@ -import torch -from avalanche.benchmarks import GenericCLScenario, dataset_benchmark -from avalanche.benchmarks.utils import AvalancheTensorDataset from torchvision import transforms import ctrl +from avalanche.benchmarks import dataset_benchmark +from avalanche.benchmarks.utils import AvalancheTensorDataset -def CTrL(stream_name): - stream = ctrl.get_stream(stream_name) +def CTrL(stream_name: str, seed: int = None): + """ + Gives access to the Continual Transfer Learning benchmark streams + introduced in https://arxiv.org/abs/2012.12631. + :param stream_name: Name of the test stream to generate. Must be one of + `s_plus`, `s_minus`, `s_in`, `s_out` and `s_pl`. + :param seed: The seed to use to generate the streams. + :return: A scenario containing 3 streams: train, val and test. + """ + stream = ctrl.get_stream(stream_name, seed) # Train, val and test experiences exps = [[], [], []] @@ -21,6 +28,7 @@ def CTrL(stream_name): task_labels=task_labels, transform=trans) exp.append(dataset) + return dataset_benchmark( train_datasets=exps[0], test_datasets=exps[2], diff --git a/environment-dev.yml b/environment-dev.yml index 8ac1eceb0..0fd366b7b 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -31,3 +31,4 @@ dependencies: - pip: - pytorchcv - gdown + - ctrl-bench \ No newline at end of file diff --git a/environment.yml b/environment.yml index dbfd76e7a..b789fcd07 100644 --- a/environment.yml +++ b/environment.yml @@ -26,3 +26,4 @@ dependencies: - pip: - pytorchcv - gdown + - ctrl-bench \ No newline at end of file diff --git a/tests/test_ctrl.py b/tests/test_ctrl.py new file mode 100644 index 000000000..2f7c10ac3 --- /dev/null +++ b/tests/test_ctrl.py @@ -0,0 +1,49 @@ +import unittest + +import torch + +from avalanche.benchmarks.classic.ctrl import CTrL + + +def custom_equals(item, other) -> bool: + """ + Helper function allowing to test if two items are equal. + The function is called recursively if the items are lists or tuples and + it uses `torch.equal` if the two items to compare are Tensors. + """ + if type(item) != type(other): + return False + if isinstance(item, (tuple, list)): + if len(item) != len(other): + return False + return all(custom_equals(*elts) for elts in zip(item, other)) + if isinstance(item, torch.Tensor): + return torch.equal(item, other) + return item == other + + +class CTrLTests(unittest.TestCase): + stream_lengths = dict( + s_plus=6, + s_minus=6, + s_in=6, + s_out=6, + s_pl=5, + ) + + def test_length(self): + for stream, length in self.stream_lengths.items(): + with self.subTest(stream=stream, length=length): + bench = CTrL(stream) + self.assertEqual(length, bench.n_experiences) + + def test_determinism(self): + for stream in self.stream_lengths.keys(): + with self.subTest(stream=stream): + bench_1 = CTrL(stream, seed=1) + bench_2 = CTrL(stream, seed=1) + + for exp1, exp2 in zip(bench_1.train_stream, + bench_2.train_stream): + for sample1, sample2 in zip(exp1.dataset, exp2.dataset): + self.assertTrue(custom_equals(sample1, sample2)) From d0e793626d306904846a0d7493983351dc880275 Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 23 May 2021 11:17:04 +0200 Subject: [PATCH 04/60] Add example script --- examples/simple_ctrl.py | 85 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 examples/simple_ctrl.py diff --git a/examples/simple_ctrl.py b/examples/simple_ctrl.py new file mode 100644 index 000000000..e3190e043 --- /dev/null +++ b/examples/simple_ctrl.py @@ -0,0 +1,85 @@ +################################################################################ +# Copyright (c) 2021 ContinualAI. # +# Copyrights licensed under the MIT License. # +# See the accompanying LICENSE file for terms. # +# # +# Date: 20-11-2020 # +# Author(s): Vincenzo Lomonaco # +# E-mail: contact@continualai.org # +# Website: avalanche.continualai.org # +################################################################################ + +""" +In this simple example we show all the different ways you can use MNIST with +Avalanche. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import torch +import argparse + +from torch.nn import CrossEntropyLoss +from torch.optim import SGD + +from avalanche.benchmarks import GenericCLScenario +from avalanche.benchmarks.classic import PermutedMNIST, RotatedMNIST, \ + SplitMNIST +from avalanche.benchmarks.classic.ctrl import CTrL +from avalanche.models import SimpleMLP, SimpleCNN +from avalanche.training.strategies import Naive + +def main(args): + # Device config + device = torch.device(f"cuda:{args.cuda}" + if torch.cuda.is_available() and + args.cuda >= 0 else "cpu") + + # model + # model = SimpleMLP(num_classes=11, input_size=3*32*32) + model = SimpleCNN(num_classes=10) + + scenario = CTrL(stream_name='s_plus') + + # Than we can extract the parallel train and test streams + train_stream = scenario.train_stream + test_stream = scenario.test_stream + + # Prepare for training & testing + optimizer = SGD(model.parameters(), lr=0.001, momentum=0.9) + criterion = CrossEntropyLoss() + + # Continual learning strategy with default logger + cl_strategy = Naive( + model, optimizer, criterion, train_mb_size=32, train_epochs=200, + eval_mb_size=32, device=device) + + # train and test loop + results = [] + transfer_mat = [] + for train_task in train_stream: + print("Current Classes: ", train_task.classes_in_this_experience) + res = cl_strategy.train(train_task) + print('') + print(f'Train res: {res}') + results.append(cl_strategy.eval(test_stream)) + print(f'Val res: {results}') + + transfer_mat.append([results[-1][f'Top1_Acc_Exp/eval_phase/test_stream/Task00{i}/Exp00{i}'] for i in range(len(train_stream))]) + print(torch.tensor(transfer_mat)) + print((torch.tensor(transfer_mat)*1000).round()/1000) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--mnist_type', type=str, default='split', + choices=['rotated', 'permuted', 'split'], + help='Choose between MNIST variations: ' + 'rotated, permuted or split.') + parser.add_argument('--cuda', type=int, default=0, + help='Select zero-indexed cuda device. -1 to use CPU.') + args = parser.parse_args() + + main(args) From 1ca7ccac8c23c5e6633effed80130b242c1a6a8a Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 25 May 2021 12:38:11 +0200 Subject: [PATCH 05/60] Clone model to compute transfer --- examples/simple_ctrl.py | 55 +++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/examples/simple_ctrl.py b/examples/simple_ctrl.py index e3190e043..b3f968fd7 100644 --- a/examples/simple_ctrl.py +++ b/examples/simple_ctrl.py @@ -18,6 +18,9 @@ from __future__ import division from __future__ import print_function +from copy import deepcopy +from pprint import pprint + import torch import argparse @@ -28,7 +31,10 @@ from avalanche.benchmarks.classic import PermutedMNIST, RotatedMNIST, \ SplitMNIST from avalanche.benchmarks.classic.ctrl import CTrL +from avalanche.evaluation.metrics import accuracy_metrics, loss_metrics +from avalanche.logging import InteractiveLogger from avalanche.models import SimpleMLP, SimpleCNN +from avalanche.training import EvaluationPlugin from avalanche.training.strategies import Naive def main(args): @@ -40,6 +46,7 @@ def main(args): # model # model = SimpleMLP(num_classes=11, input_size=3*32*32) model = SimpleCNN(num_classes=10) + model_last = deepcopy(model) scenario = CTrL(stream_name='s_plus') @@ -52,25 +59,47 @@ def main(args): criterion = CrossEntropyLoss() # Continual learning strategy with default logger + + logger = EvaluationPlugin( + accuracy_metrics(minibatch=False, epoch=True, experience=True, + stream=True), + loss_metrics(minibatch=False, epoch=True, experience=True, + stream=True), + loggers=[InteractiveLogger()]) cl_strategy = Naive( model, optimizer, criterion, train_mb_size=32, train_epochs=200, - eval_mb_size=32, device=device) + eval_mb_size=128, device=device, evaluator=logger) # train and test loop - results = [] + assert logger == cl_strategy.evaluator + for tid, train_task in enumerate(train_stream): + cl_strategy.train(train_task) + cl_strategy.eval(test_stream) + + + # print() + transfer_mat = [] - for train_task in train_stream: - print("Current Classes: ", train_task.classes_in_this_experience) - res = cl_strategy.train(train_task) - print('') - print(f'Train res: {res}') - results.append(cl_strategy.eval(test_stream)) - print(f'Val res: {results}') - - transfer_mat.append([results[-1][f'Top1_Acc_Exp/eval_phase/test_stream/Task00{i}/Exp00{i}'] for i in range(len(train_stream))]) - print(torch.tensor(transfer_mat)) - print((torch.tensor(transfer_mat)*1000).round()/1000) + for tid in range(len(train_stream)): + transfer_mat.append( + logger.all_metric_results[f'Top1_Acc_Exp/eval_phase/test_stream/Task00{tid}/Exp00{tid}'][1]) + # cl_strategy.evaluator.all_metric_results[ + # f'Top1_Acc_Exp/eval_phase/test_stream/Task00{tid}/Exp00{tid}'][1]) + + print(torch.tensor(transfer_mat)) + # pprint(logger.all_metric_results) + pprint(logger.last_metric_results) + + optimizer = SGD(model_last.parameters(), lr=0.001, momentum=0.9) + cl_strategy = Naive( + model_last, optimizer, criterion, train_mb_size=32, train_epochs=200, + eval_mb_size=32, device=device) + cl_strategy.train(train_task) + res = cl_strategy.eval([test_stream[-1]]) + print() + print(res) + print(transfer_mat[-1][-1] - res['Top1_Acc_Exp/eval_phase/test_stream/Task005/Exp-01']) if __name__ == '__main__': parser = argparse.ArgumentParser() From 648062a54c30c7af8754442d9a5a382c036883d0 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 10 Jun 2021 19:21:25 +0200 Subject: [PATCH 06/60] Fix PEP8 errors --- examples/simple_ctrl.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/simple_ctrl.py b/examples/simple_ctrl.py index b3f968fd7..c5eda912b 100644 --- a/examples/simple_ctrl.py +++ b/examples/simple_ctrl.py @@ -37,6 +37,7 @@ from avalanche.training import EvaluationPlugin from avalanche.training.strategies import Naive + def main(args): # Device config device = torch.device(f"cuda:{args.cuda}" @@ -65,7 +66,7 @@ def main(args): stream=True), loss_metrics(minibatch=False, epoch=True, experience=True, stream=True), - loggers=[InteractiveLogger()]) + loggers=[InteractiveLogger()]) cl_strategy = Naive( model, optimizer, criterion, train_mb_size=32, train_epochs=200, eval_mb_size=128, device=device, evaluator=logger) @@ -76,13 +77,11 @@ def main(args): cl_strategy.train(train_task) cl_strategy.eval(test_stream) - - # print() - transfer_mat = [] for tid in range(len(train_stream)): transfer_mat.append( - logger.all_metric_results[f'Top1_Acc_Exp/eval_phase/test_stream/Task00{tid}/Exp00{tid}'][1]) + logger.all_metric_results[f'Top1_Acc_Exp/eval_phase/test_stream/' + f'Task00{tid}/Exp00{tid}'][1]) # cl_strategy.evaluator.all_metric_results[ # f'Top1_Acc_Exp/eval_phase/test_stream/Task00{tid}/Exp00{tid}'][1]) @@ -99,7 +98,9 @@ def main(args): print() print(res) - print(transfer_mat[-1][-1] - res['Top1_Acc_Exp/eval_phase/test_stream/Task005/Exp-01']) + print(transfer_mat[-1][-1] - res['Top1_Acc_Exp/eval_phase/test_stream/' + 'Task005/Exp-01']) + if __name__ == '__main__': parser = argparse.ArgumentParser() From 3cda2778a69600e53468c2399b3065c659d76166 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 14 Jun 2021 17:02:31 +0200 Subject: [PATCH 07/60] Example script cleaning --- avalanche/benchmarks/classic/__init__.py | 1 + avalanche/benchmarks/classic/ctrl.py | 7 ++- examples/simple_ctrl.py | 77 +++++++++++------------- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/avalanche/benchmarks/classic/__init__.py b/avalanche/benchmarks/classic/__init__.py index fd1d73e39..07d7e122b 100644 --- a/avalanche/benchmarks/classic/__init__.py +++ b/avalanche/benchmarks/classic/__init__.py @@ -7,5 +7,6 @@ from .comniglot import * from .core50 import CORe50 from .ctiny_imagenet import * +from .ctrl import * from .openloris import * from .stream51 import * diff --git a/avalanche/benchmarks/classic/ctrl.py b/avalanche/benchmarks/classic/ctrl.py index 5e7a3d0ba..d3bf316e2 100644 --- a/avalanche/benchmarks/classic/ctrl.py +++ b/avalanche/benchmarks/classic/ctrl.py @@ -32,5 +32,10 @@ def CTrL(stream_name: str, seed: int = None): return dataset_benchmark( train_datasets=exps[0], test_datasets=exps[2], - other_streams_datasets=dict(valid=exps[1]), + other_streams_datasets=dict(val=exps[1]), ) + + +__all__ = [ + 'CTrL' +] diff --git a/examples/simple_ctrl.py b/examples/simple_ctrl.py index c5eda912b..1fd1fd6a7 100644 --- a/examples/simple_ctrl.py +++ b/examples/simple_ctrl.py @@ -10,30 +10,25 @@ ################################################################################ """ -In this simple example we show all the different ways you can use MNIST with -Avalanche. +In this simple example we show a simple way to use the ctrl benchmark using the +s+ stream. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import argparse from copy import deepcopy -from pprint import pprint import torch -import argparse - from torch.nn import CrossEntropyLoss from torch.optim import SGD -from avalanche.benchmarks import GenericCLScenario -from avalanche.benchmarks.classic import PermutedMNIST, RotatedMNIST, \ - SplitMNIST from avalanche.benchmarks.classic.ctrl import CTrL -from avalanche.evaluation.metrics import accuracy_metrics, loss_metrics +from avalanche.evaluation.metrics import accuracy_metrics from avalanche.logging import InteractiveLogger -from avalanche.models import SimpleMLP, SimpleCNN +from avalanche.models import SimpleCNN from avalanche.training import EvaluationPlugin from avalanche.training.strategies import Naive @@ -42,39 +37,33 @@ def main(args): # Device config device = torch.device(f"cuda:{args.cuda}" if torch.cuda.is_available() and - args.cuda >= 0 else "cpu") + args.cuda >= 0 else "cpu") - # model - # model = SimpleMLP(num_classes=11, input_size=3*32*32) + # Intialize the model, stream and training strategy model = SimpleCNN(num_classes=10) - model_last = deepcopy(model) + model_init = deepcopy(model) - scenario = CTrL(stream_name='s_plus') + scenario = CTrL(stream_name=args.stream) - # Than we can extract the parallel train and test streams train_stream = scenario.train_stream test_stream = scenario.test_stream + val_stream = scenario.val_stream - # Prepare for training & testing optimizer = SGD(model.parameters(), lr=0.001, momentum=0.9) criterion = CrossEntropyLoss() - # Continual learning strategy with default logger - logger = EvaluationPlugin( - accuracy_metrics(minibatch=False, epoch=True, experience=True, + accuracy_metrics(minibatch=False, epoch=False, experience=True, stream=True), - loss_metrics(minibatch=False, epoch=True, experience=True, - stream=True), loggers=[InteractiveLogger()]) cl_strategy = Naive( - model, optimizer, criterion, train_mb_size=32, train_epochs=200, - eval_mb_size=128, device=device, evaluator=logger) + model, optimizer, criterion, train_mb_size=32, device=device, + train_epochs=args.max_epochs, eval_mb_size=128, evaluator=logger) # train and test loop - assert logger == cl_strategy.evaluator for tid, train_task in enumerate(train_stream): cl_strategy.train(train_task) + cl_strategy.eval(val_stream) cl_strategy.eval(test_stream) transfer_mat = [] @@ -82,34 +71,36 @@ def main(args): transfer_mat.append( logger.all_metric_results[f'Top1_Acc_Exp/eval_phase/test_stream/' f'Task00{tid}/Exp00{tid}'][1]) - # cl_strategy.evaluator.all_metric_results[ - # f'Top1_Acc_Exp/eval_phase/test_stream/Task00{tid}/Exp00{tid}'][1]) - print(torch.tensor(transfer_mat)) - # pprint(logger.all_metric_results) - pprint(logger.last_metric_results) - - optimizer = SGD(model_last.parameters(), lr=0.001, momentum=0.9) + optimizer = SGD(model_init.parameters(), lr=0.001, momentum=0.9) cl_strategy = Naive( - model_last, optimizer, criterion, train_mb_size=32, train_epochs=200, - eval_mb_size=32, device=device) + model_init, optimizer, criterion, train_mb_size=32, device=device, + train_epochs=args.max_epochs, eval_mb_size=128) + cl_strategy.train(train_task) res = cl_strategy.eval([test_stream[-1]]) - print() - print(res) - print(transfer_mat[-1][-1] - res['Top1_Acc_Exp/eval_phase/test_stream/' - 'Task005/Exp-01']) + acc_last_stream = transfer_mat[-1][-1] + acc_last_only = res['Top1_Acc_Exp/eval_phase/test_stream/Task005/Exp-01'] + transfer_value = acc_last_stream - acc_last_only + + print(f'Accuracy on probe task after training on the whole ' + f'stream: {acc_last_stream}') + print(f'Accuracy on probe task after trained ' + f'independently: {acc_last_only}') + print(f'T({args.stream})={transfer_value}') if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument('--mnist_type', type=str, default='split', - choices=['rotated', 'permuted', 'split'], - help='Choose between MNIST variations: ' - 'rotated, permuted or split.') + parser.add_argument('--stream', type=str, default='s_plus', + choices=['s_plus', 's_minus', 's_in', 's_out', 's_pl'], + help='Select the CTrL Stream to train on: [s_plus], ' + 's_minus, s_in, s_out or s_pl.') + parser.add_argument('--max-epochs', type=int, default=50, + help='The maximum number of training epochs for each ' + 'task. Default to 200.') parser.add_argument('--cuda', type=int, default=0, help='Select zero-indexed cuda device. -1 to use CPU.') args = parser.parse_args() - main(args) From 67f611c7460745f5050fa6a9e59911d229328516 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 16 Jun 2021 10:59:40 +0200 Subject: [PATCH 08/60] Add support for the long stream + early stopping --- avalanche/benchmarks/classic/ctrl.py | 60 +++++++++++++--- examples/simple_ctrl.py | 101 ++++++++++++++++++++------- tests/test_ctrl.py | 4 ++ 3 files changed, 133 insertions(+), 32 deletions(-) diff --git a/avalanche/benchmarks/classic/ctrl.py b/avalanche/benchmarks/classic/ctrl.py index d3bf316e2..9c5fa36d2 100644 --- a/avalanche/benchmarks/classic/ctrl.py +++ b/avalanche/benchmarks/classic/ctrl.py @@ -1,33 +1,77 @@ +import random +import sys +from pathlib import Path + +import torchvision.transforms.functional as F from torchvision import transforms +from tqdm import tqdm import ctrl from avalanche.benchmarks import dataset_benchmark -from avalanche.benchmarks.utils import AvalancheTensorDataset +from avalanche.benchmarks.datasets import default_dataset_location +from avalanche.benchmarks.utils import AvalancheTensorDataset, \ + common_paths_root, AvalancheDataset, PathsDataset -def CTrL(stream_name: str, seed: int = None): +def CTrL(stream_name: str, save_to_disk: bool = False, + path: Path = default_dataset_location(''), seed: int = None): """ Gives access to the Continual Transfer Learning benchmark streams introduced in https://arxiv.org/abs/2012.12631. :param stream_name: Name of the test stream to generate. Must be one of `s_plus`, `s_minus`, `s_in`, `s_out` and `s_pl`. - :param seed: The seed to use to generate the streams. + :param save_to_disk: Whether to save each stream on the disk or load + everything in memory. Setting it to `True` will save memory but takes more + time on the first generation using the corresponding seed. + :param path: The path under which the generated stream will be saved if + save_to_disk is True. + :param seed: The seed to use to generate the streams. If no seed is given, + a random one will be used to make sure that the generated stream can + be reproduced. :return: A scenario containing 3 streams: train, val and test. """ + seed = seed or random.randint(0, sys.maxsize) + stream = ctrl.get_stream(stream_name, seed) + if save_to_disk: + folder = path / 'ctrl' / stream_name / f'seed_{seed}' + # Train, val and test experiences exps = [[], [], []] - for t in stream: + for t_id, t in enumerate(tqdm(stream, desc=f'Loading {stream_name}'), ): trans = transforms.Normalize(t.statistics['mean'], t.statistics['std']) - for split, exp in zip(t.datasets, exps): + for split, split_name, exp in zip(t.datasets, t.split_names, exps): samples, labels = split.tensors task_labels = [t.id] * samples.size(0) - dataset = AvalancheTensorDataset(samples, labels.squeeze(1), - task_labels=task_labels, - transform=trans) + if save_to_disk: + exp_folder = folder / f'exp_{t_id}' / split_name + exp_folder.mkdir(parents=True, exist_ok=True) + files = [] + for i, (sample, label) in enumerate(zip(samples, labels)): + sample_path = exp_folder / f'sample_{i}.png' + if not sample_path.exists(): + F.to_pil_image(sample).save(sample_path) + files.append((sample_path, label.item())) + + common_root, exp_paths_list = common_paths_root(files) + paths_dataset = PathsDataset(common_root, exp_paths_list) + dataset = AvalancheDataset( + paths_dataset, + task_labels=task_labels, + transform=transforms.Compose([ + transforms.ToTensor(), + trans + ]) + ) + else: + dataset = AvalancheTensorDataset(samples, labels.squeeze(1), + task_labels=task_labels, + transform=trans) exp.append(dataset) + if stream_name == 's_long' and t_id == 99: + break return dataset_benchmark( train_datasets=exps[0], diff --git a/examples/simple_ctrl.py b/examples/simple_ctrl.py index 1fd1fd6a7..54002bd7b 100644 --- a/examples/simple_ctrl.py +++ b/examples/simple_ctrl.py @@ -29,21 +29,60 @@ from avalanche.evaluation.metrics import accuracy_metrics from avalanche.logging import InteractiveLogger from avalanche.models import SimpleCNN -from avalanche.training import EvaluationPlugin +from avalanche.training.plugins import StrategyPlugin, EvaluationPlugin from avalanche.training.strategies import Naive +class EarlyStopper(StrategyPlugin): + def __init__(self, patience, val_stream_name): + """ + Simple plugin stopping the training when the accuracy on the + corresponding validation task stopped progressing for a few epochs. + + :param patience: Number of epochs to wait before stopping the training. + :param val_stream_name: Name of the validation stream to search in the + metrics. The corresponding stream will be used to keep track of the + evolution of the performance of a model. + """ + super().__init__() + self.val_stream_name = val_stream_name + self.patience = patience + + self.best = None # Contains the best val acc on the current experience + self.best_it = None + self.best_epoch = None + self.cur_exp = None + + def before_training(self, strategy, **kwargs): + self.best = None + self.best_it = None + self.best_epoch = None + self.cur_exp = 0 if self.cur_exp is None else self.cur_exp + 1 + + def after_training_epoch(self, strategy, **kwargs): + res = strategy.evaluator.get_last_metrics() + val_acc = res.get(f'Top1_Acc_Stream/eval_phase/{self.val_stream_name}') + if self.best is None or val_acc > self.best: + self.best = val_acc + self.best_it = strategy.mb_it + self.best_epoch = strategy.epoch + if strategy.epoch - self.best_epoch > self.patience: + strategy.stop_training() + + def main(args): # Device config device = torch.device(f"cuda:{args.cuda}" if torch.cuda.is_available() and - args.cuda >= 0 else "cpu") + args.cuda >= 0 else "cpu") # Intialize the model, stream and training strategy model = SimpleCNN(num_classes=10) - model_init = deepcopy(model) + if args.stream != 's_long': + model_init = deepcopy(model) - scenario = CTrL(stream_name=args.stream) + scenario = CTrL(stream_name=args.stream, save_to_disk=args.save, + path=args.path, seed=10) train_stream = scenario.train_stream test_stream = scenario.test_stream @@ -56,14 +95,16 @@ def main(args): accuracy_metrics(minibatch=False, epoch=False, experience=True, stream=True), loggers=[InteractiveLogger()]) + cl_strategy = Naive( model, optimizer, criterion, train_mb_size=32, device=device, - train_epochs=args.max_epochs, eval_mb_size=128, evaluator=logger) + train_epochs=args.max_epochs, eval_mb_size=128, evaluator=logger, + plugins=[EarlyStopper(100, 'val_stream')], eval_every=5 + ) # train and test loop - for tid, train_task in enumerate(train_stream): - cl_strategy.train(train_task) - cl_strategy.eval(val_stream) + for train_task, val_task in zip(train_stream, val_stream): + cl_strategy.train(train_task, eval_streams=[val_task]) cl_strategy.eval(test_stream) transfer_mat = [] @@ -71,24 +112,31 @@ def main(args): transfer_mat.append( logger.all_metric_results[f'Top1_Acc_Exp/eval_phase/test_stream/' f'Task00{tid}/Exp00{tid}'][1]) - print(torch.tensor(transfer_mat)) - optimizer = SGD(model_init.parameters(), lr=0.001, momentum=0.9) - cl_strategy = Naive( - model_init, optimizer, criterion, train_mb_size=32, device=device, - train_epochs=args.max_epochs, eval_mb_size=128) - cl_strategy.train(train_task) - res = cl_strategy.eval([test_stream[-1]]) + if args.stream != 's_long:': + optimizer = SGD(model_init.parameters(), lr=0.001, momentum=0.9) + cl_strategy = Naive( + model_init, optimizer, criterion, train_mb_size=32, device=device, + train_epochs=args.max_epochs, eval_mb_size=128, + plugins=[EarlyStopper(50, 'val_stream')], eval_every=5 + ) + + cl_strategy.train(train_stream[-1]) + res = cl_strategy.eval([test_stream[-1]]) - acc_last_stream = transfer_mat[-1][-1] - acc_last_only = res['Top1_Acc_Exp/eval_phase/test_stream/Task005/Exp-01'] - transfer_value = acc_last_stream - acc_last_only + acc_last_stream = transfer_mat[-1][-1] + acc_last_only = res[ + 'Top1_Acc_Exp/eval_phase/test_stream/Task005/Exp-01'] + transfer_value = acc_last_stream - acc_last_only - print(f'Accuracy on probe task after training on the whole ' - f'stream: {acc_last_stream}') - print(f'Accuracy on probe task after trained ' - f'independently: {acc_last_only}') - print(f'T({args.stream})={transfer_value}') + print(f'Accuracy on probe task after training on the whole ' + f'stream: {acc_last_stream}') + print(f'Accuracy on probe task after trained ' + f'independently: {acc_last_only}') + print(f'T({args.stream})={transfer_value}') + else: + res = logger.last_metric_results["Top1_Acc_Exp/eval_phase/test_stream"] + print(f'Avg Acc = {res}') if __name__ == '__main__': @@ -97,7 +145,12 @@ def main(args): choices=['s_plus', 's_minus', 's_in', 's_out', 's_pl'], help='Select the CTrL Stream to train on: [s_plus], ' 's_minus, s_in, s_out or s_pl.') - parser.add_argument('--max-epochs', type=int, default=50, + parser.add_argument('--save', type=bool, default=False, + help='Whether to save the generated experiences to' + ' disk or load them all in memory.') + parser.add_argument('--path', type=str, + help='Path used to save the generated stream.') + parser.add_argument('--max-epochs', type=int, default=200, help='The maximum number of training epochs for each ' 'task. Default to 200.') parser.add_argument('--cuda', type=int, default=0, diff --git a/tests/test_ctrl.py b/tests/test_ctrl.py index 2f7c10ac3..159954b3c 100644 --- a/tests/test_ctrl.py +++ b/tests/test_ctrl.py @@ -37,6 +37,10 @@ def test_length(self): bench = CTrL(stream) self.assertEqual(length, bench.n_experiences) + def test_length_long(self): + bench = CTrL('s_long', save_to_disk=True) + self.assertEqual(100, bench.n_experiences) + def test_determinism(self): for stream in self.stream_lengths.keys(): with self.subTest(stream=stream): From ac183eb7cb0067d629d471f527af98a08f406ed1 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 16 Jun 2021 11:56:41 +0200 Subject: [PATCH 09/60] Add modes to the early stopping plugin --- examples/simple_ctrl.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/examples/simple_ctrl.py b/examples/simple_ctrl.py index 54002bd7b..07cb5d443 100644 --- a/examples/simple_ctrl.py +++ b/examples/simple_ctrl.py @@ -19,6 +19,7 @@ from __future__ import print_function import argparse +import operator from copy import deepcopy import torch @@ -34,7 +35,8 @@ class EarlyStopper(StrategyPlugin): - def __init__(self, patience, val_stream_name): + def __init__(self, patience: int, val_stream_name: str, + metric_name: str = 'Top1_Acc_Stream', mode: str = 'max'): """ Simple plugin stopping the training when the accuracy on the corresponding validation task stopped progressing for a few epochs. @@ -43,31 +45,39 @@ def __init__(self, patience, val_stream_name): :param val_stream_name: Name of the validation stream to search in the metrics. The corresponding stream will be used to keep track of the evolution of the performance of a model. + :param metric_name: The name of the metric to watch as it will be + reported in the evaluator. + :param mode: Must be "max" or "min". max (resp. min) means that the + given metric should me maximized (resp. minimized). """ super().__init__() self.val_stream_name = val_stream_name self.patience = patience + self.metric_name = metric_name + self.metric_key = f'{self.metric_name}/eval_phase/' \ + f'{self.val_stream_name}' + if mode not in ('max', 'min'): + raise ValueError(f'Mode must be "max" or "min", got {mode}.') + self.operator = operator.gt if mode == 'max' else operator.lt self.best = None # Contains the best val acc on the current experience - self.best_it = None self.best_epoch = None - self.cur_exp = None def before_training(self, strategy, **kwargs): self.best = None - self.best_it = None self.best_epoch = None - self.cur_exp = 0 if self.cur_exp is None else self.cur_exp + 1 def after_training_epoch(self, strategy, **kwargs): + self._update_best(strategy) + if strategy.epoch - self.best_epoch >= self.patience: + strategy.stop_training() + + def _update_best(self, strategy): res = strategy.evaluator.get_last_metrics() - val_acc = res.get(f'Top1_Acc_Stream/eval_phase/{self.val_stream_name}') - if self.best is None or val_acc > self.best: + val_acc = res.get(self.metric_key) + if self.best is None or self.operator(val_acc, self.best): self.best = val_acc - self.best_it = strategy.mb_it self.best_epoch = strategy.epoch - if strategy.epoch - self.best_epoch > self.patience: - strategy.stop_training() def main(args): @@ -99,7 +109,7 @@ def main(args): cl_strategy = Naive( model, optimizer, criterion, train_mb_size=32, device=device, train_epochs=args.max_epochs, eval_mb_size=128, evaluator=logger, - plugins=[EarlyStopper(100, 'val_stream')], eval_every=5 + plugins=[EarlyStopper(50, 'val_stream')], eval_every=5 ) # train and test loop From 21426fa3bf428f8bc659d3441ec40e1a7e8f2904 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 17 Jun 2021 08:48:57 +0200 Subject: [PATCH 10/60] Add S_long to the demo --- examples/simple_ctrl.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/simple_ctrl.py b/examples/simple_ctrl.py index 07cb5d443..4d52471ee 100644 --- a/examples/simple_ctrl.py +++ b/examples/simple_ctrl.py @@ -123,7 +123,11 @@ def main(args): logger.all_metric_results[f'Top1_Acc_Exp/eval_phase/test_stream/' f'Task00{tid}/Exp00{tid}'][1]) - if args.stream != 's_long:': + if args.stream == 's_long': + res = logger.last_metric_results["Top1_Acc_Stream/eval_phase/" \ + "test_stream"] + print(f'Average accuracy on S_long : {res}') + else: optimizer = SGD(model_init.parameters(), lr=0.001, momentum=0.9) cl_strategy = Naive( model_init, optimizer, criterion, train_mb_size=32, device=device, @@ -144,17 +148,15 @@ def main(args): print(f'Accuracy on probe task after trained ' f'independently: {acc_last_only}') print(f'T({args.stream})={transfer_value}') - else: - res = logger.last_metric_results["Top1_Acc_Exp/eval_phase/test_stream"] - print(f'Avg Acc = {res}') if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--stream', type=str, default='s_plus', - choices=['s_plus', 's_minus', 's_in', 's_out', 's_pl'], + choices=['s_plus', 's_minus', 's_in', 's_out', 's_pl', + 's_long'], help='Select the CTrL Stream to train on: [s_plus], ' - 's_minus, s_in, s_out or s_pl.') + 's_minus, s_in, s_out, s_pl or s_long.') parser.add_argument('--save', type=bool, default=False, help='Whether to save the generated experiences to' ' disk or load them all in memory.') From dc75875a2851985eb4e7cf283152b09fc6d95dd5 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 22 Jun 2021 10:46:21 +0200 Subject: [PATCH 11/60] Use early stopping plugin + add license --- avalanche/benchmarks/classic/ctrl.py | 11 ++++ examples/simple_ctrl.py | 77 ++++++---------------------- 2 files changed, 28 insertions(+), 60 deletions(-) diff --git a/avalanche/benchmarks/classic/ctrl.py b/avalanche/benchmarks/classic/ctrl.py index 9c5fa36d2..79bfd1166 100644 --- a/avalanche/benchmarks/classic/ctrl.py +++ b/avalanche/benchmarks/classic/ctrl.py @@ -1,3 +1,14 @@ +################################################################################ +# Copyright (c) 2021 ContinualAI. # +# Copyrights licensed under the MIT License. # +# See the accompanying LICENSE file for terms. # +# # +# Date: 22-06-2021 # +# Author(s): Tom Veniat # +# E-mail: contact@continualai.org # +# Website: avalanche.continualai.org # +################################################################################ + import random import sys from pathlib import Path diff --git a/examples/simple_ctrl.py b/examples/simple_ctrl.py index 4d52471ee..c03a47261 100644 --- a/examples/simple_ctrl.py +++ b/examples/simple_ctrl.py @@ -3,23 +3,25 @@ # Copyrights licensed under the MIT License. # # See the accompanying LICENSE file for terms. # # # -# Date: 20-11-2020 # -# Author(s): Vincenzo Lomonaco # +# Date: 22-06-2021 # +# Author(s): Tom Veniat # # E-mail: contact@continualai.org # # Website: avalanche.continualai.org # ################################################################################ """ -In this simple example we show a simple way to use the ctrl benchmark using the -s+ stream. -""" +In this example we show a simple way to use the ctrl benchmark presented +in https://arxiv.org/abs/2012.12631. +The training procedure will report the Transfer metric as defined in +eq.3 in the article for all streams but the long one, for which +the average accuracy after training on the whole stream is reported. + """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import argparse -import operator from copy import deepcopy import torch @@ -30,56 +32,11 @@ from avalanche.evaluation.metrics import accuracy_metrics from avalanche.logging import InteractiveLogger from avalanche.models import SimpleCNN -from avalanche.training.plugins import StrategyPlugin, EvaluationPlugin +from avalanche.training.plugins import EvaluationPlugin +from avalanche.training.plugins.early_stopping import EarlyStoppingPlugin from avalanche.training.strategies import Naive -class EarlyStopper(StrategyPlugin): - def __init__(self, patience: int, val_stream_name: str, - metric_name: str = 'Top1_Acc_Stream', mode: str = 'max'): - """ - Simple plugin stopping the training when the accuracy on the - corresponding validation task stopped progressing for a few epochs. - - :param patience: Number of epochs to wait before stopping the training. - :param val_stream_name: Name of the validation stream to search in the - metrics. The corresponding stream will be used to keep track of the - evolution of the performance of a model. - :param metric_name: The name of the metric to watch as it will be - reported in the evaluator. - :param mode: Must be "max" or "min". max (resp. min) means that the - given metric should me maximized (resp. minimized). - """ - super().__init__() - self.val_stream_name = val_stream_name - self.patience = patience - self.metric_name = metric_name - self.metric_key = f'{self.metric_name}/eval_phase/' \ - f'{self.val_stream_name}' - if mode not in ('max', 'min'): - raise ValueError(f'Mode must be "max" or "min", got {mode}.') - self.operator = operator.gt if mode == 'max' else operator.lt - - self.best = None # Contains the best val acc on the current experience - self.best_epoch = None - - def before_training(self, strategy, **kwargs): - self.best = None - self.best_epoch = None - - def after_training_epoch(self, strategy, **kwargs): - self._update_best(strategy) - if strategy.epoch - self.best_epoch >= self.patience: - strategy.stop_training() - - def _update_best(self, strategy): - res = strategy.evaluator.get_last_metrics() - val_acc = res.get(self.metric_key) - if self.best is None or self.operator(val_acc, self.best): - self.best = val_acc - self.best_epoch = strategy.epoch - - def main(args): # Device config device = torch.device(f"cuda:{args.cuda}" @@ -109,7 +66,7 @@ def main(args): cl_strategy = Naive( model, optimizer, criterion, train_mb_size=32, device=device, train_epochs=args.max_epochs, eval_mb_size=128, evaluator=logger, - plugins=[EarlyStopper(50, 'val_stream')], eval_every=5 + plugins=[EarlyStoppingPlugin(50, 'val_stream')], eval_every=5 ) # train and test loop @@ -120,27 +77,27 @@ def main(args): transfer_mat = [] for tid in range(len(train_stream)): transfer_mat.append( - logger.all_metric_results[f'Top1_Acc_Exp/eval_phase/test_stream/' + logger.all_metric_results['Top1_Acc_Exp/eval_phase/test_stream/' f'Task00{tid}/Exp00{tid}'][1]) if args.stream == 's_long': - res = logger.last_metric_results["Top1_Acc_Stream/eval_phase/" \ - "test_stream"] + res = logger.last_metric_results['Top1_Acc_Stream/eval_phase/' + 'test_stream'] print(f'Average accuracy on S_long : {res}') else: optimizer = SGD(model_init.parameters(), lr=0.001, momentum=0.9) cl_strategy = Naive( model_init, optimizer, criterion, train_mb_size=32, device=device, train_epochs=args.max_epochs, eval_mb_size=128, - plugins=[EarlyStopper(50, 'val_stream')], eval_every=5 + plugins=[EarlyStoppingPlugin(50, 'val_stream')], eval_every=5 ) cl_strategy.train(train_stream[-1]) res = cl_strategy.eval([test_stream[-1]]) acc_last_stream = transfer_mat[-1][-1] - acc_last_only = res[ - 'Top1_Acc_Exp/eval_phase/test_stream/Task005/Exp-01'] + acc_last_only = res['Top1_Acc_Exp/eval_phase/test_stream/' + 'Task005/Exp-01'] transfer_value = acc_last_stream - acc_last_only print(f'Accuracy on probe task after training on the whole ' From e8e07413276e03f1fc2a5102d4dc281d0dac3742 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 24 Jun 2021 14:43:31 +0200 Subject: [PATCH 12/60] Shorten the long_stream for testing --- avalanche/benchmarks/classic/ctrl.py | 13 +++++++++++-- tests/test_ctrl.py | 20 ++++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/avalanche/benchmarks/classic/ctrl.py b/avalanche/benchmarks/classic/ctrl.py index 79bfd1166..a8a9994b0 100644 --- a/avalanche/benchmarks/classic/ctrl.py +++ b/avalanche/benchmarks/classic/ctrl.py @@ -25,7 +25,8 @@ def CTrL(stream_name: str, save_to_disk: bool = False, - path: Path = default_dataset_location(''), seed: int = None): + path: Path = default_dataset_location(''), seed: int = None, + n_tasks: int = None): """ Gives access to the Continual Transfer Learning benchmark streams introduced in https://arxiv.org/abs/2012.12631. @@ -39,9 +40,17 @@ def CTrL(stream_name: str, save_to_disk: bool = False, :param seed: The seed to use to generate the streams. If no seed is given, a random one will be used to make sure that the generated stream can be reproduced. + :param n_tasks: The number of tasks to generate. This parameter is only + relevant for the `s_long` stream, as all other streams have a fixed number + of tasks. :return: A scenario containing 3 streams: train, val and test. """ seed = seed or random.randint(0, sys.maxsize) + if stream_name != 's_long' and n_tasks is not None: + raise ValueError('The n_tasks parameter can only be used with the ' + f'"s_long" stream, asked {n_tasks} for {stream_name}') + elif stream_name == 's_long' and n_tasks is None: + n_tasks = 100 stream = ctrl.get_stream(stream_name, seed) @@ -81,7 +90,7 @@ def CTrL(stream_name: str, save_to_disk: bool = False, task_labels=task_labels, transform=trans) exp.append(dataset) - if stream_name == 's_long' and t_id == 99: + if stream_name == 's_long' and t_id == n_tasks - 1: break return dataset_benchmark( diff --git a/tests/test_ctrl.py b/tests/test_ctrl.py index 159954b3c..d917bb28e 100644 --- a/tests/test_ctrl.py +++ b/tests/test_ctrl.py @@ -1,5 +1,7 @@ import unittest +from pathlib import Path +from tempfile import TemporaryDirectory import torch from avalanche.benchmarks.classic.ctrl import CTrL @@ -31,6 +33,8 @@ class CTrLTests(unittest.TestCase): s_pl=5, ) + long_stream_lengths = [8, 15] + def test_length(self): for stream, length in self.stream_lengths.items(): with self.subTest(stream=stream, length=length): @@ -38,8 +42,20 @@ def test_length(self): self.assertEqual(length, bench.n_experiences) def test_length_long(self): - bench = CTrL('s_long', save_to_disk=True) - self.assertEqual(100, bench.n_experiences) + for n_tasks in self.long_stream_lengths: + with self.subTest(n_tasks=n_tasks), TemporaryDirectory() as tmp: + bench = CTrL('s_long', save_to_disk=True, path=Path(tmp), + n_tasks=n_tasks) + self.assertEqual(n_tasks, bench.n_experiences) + + def test_n_tasks_param(self): + for stream in self.stream_lengths.keys(): + with self.subTest(stream=stream): + with self.assertRaises(ValueError): + CTrL(stream, n_tasks=3) + + with self.subTest(stream='s_long'): + CTrL('s_long', n_tasks=3) def test_determinism(self): for stream in self.stream_lengths.keys(): From 6a0e97f75e0251f3e481fae6150f0f1a485150c6 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 24 Jun 2021 14:47:51 +0200 Subject: [PATCH 13/60] Fix PEP8 error --- tests/test_ctrl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_ctrl.py b/tests/test_ctrl.py index d917bb28e..d3720ba6f 100644 --- a/tests/test_ctrl.py +++ b/tests/test_ctrl.py @@ -55,7 +55,7 @@ def test_n_tasks_param(self): CTrL(stream, n_tasks=3) with self.subTest(stream='s_long'): - CTrL('s_long', n_tasks=3) + CTrL('s_long', n_tasks=3) def test_determinism(self): for stream in self.stream_lengths.keys(): From f43544a370e2e73c5be67907ab7d3d07fb50be38 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 16 Aug 2021 19:51:16 +0200 Subject: [PATCH 14/60] update ctrl dependency --- environment-dev.yml | 2 +- environment.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/environment-dev.yml b/environment-dev.yml index 0fd366b7b..411ec5898 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -31,4 +31,4 @@ dependencies: - pip: - pytorchcv - gdown - - ctrl-bench \ No newline at end of file + - ctrl-benchmark \ No newline at end of file diff --git a/environment.yml b/environment.yml index b789fcd07..a6317997f 100644 --- a/environment.yml +++ b/environment.yml @@ -26,4 +26,4 @@ dependencies: - pip: - pytorchcv - gdown - - ctrl-bench \ No newline at end of file + - ctrl-benchmark \ No newline at end of file From d751a05cb84485c791a2cbb2c4e212eb9c92093d Mon Sep 17 00:00:00 2001 From: hamedhemati Date: Fri, 19 Nov 2021 22:20:35 +0100 Subject: [PATCH 15/60] FIX an issue with mem_batch_size in ReplayDataLoader when forcing data_batch_size --- avalanche/benchmarks/utils/data_loader.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/avalanche/benchmarks/utils/data_loader.py b/avalanche/benchmarks/utils/data_loader.py index 104c8d406..0a7581ca5 100644 --- a/avalanche/benchmarks/utils/data_loader.py +++ b/avalanche/benchmarks/utils/data_loader.py @@ -240,7 +240,9 @@ def __init__(self, data: AvalancheDataset, memory: AvalancheDataset = None, combine the mini-batches obtained separately from each task. :param batch_size: the size of the batch. It must be greater than or equal to the number of tasks. - :param ratio_data_mem: How many of the samples should be from + :param force_data_batch_size: How many of the samples should be from the + current `data`. If None, it will equally divide each batch between + samples from all seen tasks in the current `data` and `memory`. :param kwargs: data loader arguments used to instantiate the loader for each task separately. See pytorch :class:`DataLoader`. """ @@ -256,19 +258,22 @@ def __init__(self, data: AvalancheDataset, memory: AvalancheDataset = None, assert force_data_batch_size <= batch_size, \ "Forced batch size of data must be <= entire batch size" - mem_batch_size = batch_size - force_data_batch_size - remaining_example = 0 + remaining_example_data = 0 + mem_keys = len(self.memory.task_set) + mem_batch_size = (batch_size - force_data_batch_size) // mem_keys + remaining_example_mem = (batch_size - force_data_batch_size) % mem_keys + assert mem_batch_size >= mem_keys, \ "Batch size must be greator or equal " \ "to the number of tasks in the memory." self.loader_data, _ = self._create_dataloaders( data, force_data_batch_size, - remaining_example, **kwargs) + remaining_example_data, **kwargs) self.loader_memory, _ = self._create_dataloaders( memory, mem_batch_size, - remaining_example, **kwargs) + remaining_example_mem, **kwargs) else: num_keys = len(self.data.task_set) + len(self.memory.task_set) assert batch_size >= num_keys, \ From 42edd44f16b2306952bed4416f9fd425bbfa6f1d Mon Sep 17 00:00:00 2001 From: hamedhemati Date: Fri, 19 Nov 2021 22:23:14 +0100 Subject: [PATCH 16/60] Add force_data_batch_size option to ReplayPlugin to enable manual assignment of data-memory ratio when creating a ReplayDataLoader. --- avalanche/training/plugins/replay.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/avalanche/training/plugins/replay.py b/avalanche/training/plugins/replay.py index 54e070690..a6fad6d26 100644 --- a/avalanche/training/plugins/replay.py +++ b/avalanche/training/plugins/replay.py @@ -30,12 +30,17 @@ class ReplayPlugin(StrategyPlugin): in the external memory. :param storage_policy: The policy that controls how to add new exemplars in memory + :param force_data_batch_size: How many of the samples should be from the + current `data`. If None, it will equally divide each batch between + samples from all seen tasks in the current `data` and `memory`. """ def __init__(self, mem_size: int = 200, - storage_policy: Optional["ExemplarsBuffer"] = None): + storage_policy: Optional["ExemplarsBuffer"] = None, + force_data_batch_size: int = None): super().__init__() self.mem_size = mem_size + self.force_data_batch_size = force_data_batch_size if storage_policy is not None: # Use other storage policy self.storage_policy = storage_policy @@ -66,6 +71,7 @@ def before_training_exp(self, strategy: "BaseStrategy", oversample_small_tasks=True, num_workers=num_workers, batch_size=strategy.train_mb_size, + force_data_batch_size=self.force_data_batch_size, shuffle=shuffle) def after_training_exp(self, strategy: "BaseStrategy", **kwargs): From 4858b482100319657a6d37e0a79294f548a517c3 Mon Sep 17 00:00:00 2001 From: hamedhemati Date: Fri, 19 Nov 2021 23:00:16 +0100 Subject: [PATCH 17/60] FIX an issue with mem_batch_size in ReplayDataLoader [PEP8-FIX] --- avalanche/benchmarks/utils/data_loader.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/avalanche/benchmarks/utils/data_loader.py b/avalanche/benchmarks/utils/data_loader.py index 0a7581ca5..12e659960 100644 --- a/avalanche/benchmarks/utils/data_loader.py +++ b/avalanche/benchmarks/utils/data_loader.py @@ -261,8 +261,9 @@ def __init__(self, data: AvalancheDataset, memory: AvalancheDataset = None, remaining_example_data = 0 mem_keys = len(self.memory.task_set) - mem_batch_size = (batch_size - force_data_batch_size) // mem_keys - remaining_example_mem = (batch_size - force_data_batch_size) % mem_keys + mem_batch_size = batch_size - force_data_batch_size + mem_batch_size_k = mem_batch_size // mem_keys + remaining_example_mem = mem_batch_size % mem_keys assert mem_batch_size >= mem_keys, \ "Batch size must be greator or equal " \ @@ -272,7 +273,7 @@ def __init__(self, data: AvalancheDataset, memory: AvalancheDataset = None, data, force_data_batch_size, remaining_example_data, **kwargs) self.loader_memory, _ = self._create_dataloaders( - memory, mem_batch_size, + memory, mem_batch_size_k, remaining_example_mem, **kwargs) else: num_keys = len(self.data.task_set) + len(self.memory.task_set) From 17fc1a83e24334f4077cc0602c187638ca3a58aa Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Thu, 25 Nov 2021 14:17:32 +0100 Subject: [PATCH 18/60] minor changes to docstrings --- avalanche/training/storage_policy.py | 13 ++++++++++--- avalanche/training/strategies/deep_slda.py | 7 +++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/avalanche/training/storage_policy.py b/avalanche/training/storage_policy.py index 7574c5046..d2d685e99 100644 --- a/avalanche/training/storage_policy.py +++ b/avalanche/training/storage_policy.py @@ -24,7 +24,8 @@ class ExemplarsBuffer(ABC): """ def __init__(self, max_size: int): - """ + """Init. + :param max_size: max number of input samples in the replay memory. """ self.max_size = max_size @@ -83,6 +84,11 @@ def update(self, strategy: 'BaseStrategy', **kwargs): self.update_from_dataset(strategy.experience.dataset) def update_from_dataset(self, new_data: AvalancheDataset): + """Update the buffer using the given dataset. + + :param new_data: + :return: + """ new_weights = torch.rand(len(new_data)) cat_weights = torch.cat([new_weights, self._buffer_weights]) @@ -317,7 +323,7 @@ def __init__(self, max_size: int, def update(self, strategy: "BaseStrategy", **kwargs): new_data = strategy.experience.dataset - new_groups = self.make_groups(strategy, new_data) + new_groups = self._make_groups(strategy, new_data) self.seen_groups.update(new_groups.keys()) # associate lengths to classes @@ -344,7 +350,8 @@ def update(self, strategy: "BaseStrategy", **kwargs): self.buffer_groups[group_id].resize(strategy, group_to_len[group_id]) - def make_groups(self, strategy, data): + def _make_groups(self, strategy, data): + """Split the data by group according to `self.groupby`.""" if self.groupby is None: return {0: data} elif self.groupby == 'task': diff --git a/avalanche/training/strategies/deep_slda.py b/avalanche/training/strategies/deep_slda.py index 9acfe5257..a7ad76282 100644 --- a/avalanche/training/strategies/deep_slda.py +++ b/avalanche/training/strategies/deep_slda.py @@ -32,8 +32,8 @@ def __init__(self, slda_model, criterion, eval_mb_size: int = 1, device='cpu', plugins: Optional[Sequence['StrategyPlugin']] = None, evaluator=default_logger, eval_every=-1): - """ - Init function for the SLDA model. + """Init function for the SLDA model. + :param slda_model: a PyTorch model :param criterion: loss function :param output_layer_name: if not None, wrap model to retrieve @@ -82,6 +82,7 @@ def __init__(self, slda_model, criterion, self.prev_num_updates = -1 def forward(self, return_features=False): + """Compute the model's output given the current mini-batch.""" self.model.eval() if isinstance(self.model, MultiTaskModule): feat = self.model(self.mb_x, self.mb_task_id) @@ -124,6 +125,8 @@ def training_epoch(self, **kwargs): self._after_training_iteration(**kwargs) def make_optimizer(self): + """Empty function. + Deep SLDA does not need a Pytorch optimizer.""" pass @torch.no_grad() From 89a498919864f7c7c82cc37b2ca10cc2eb31107a Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Thu, 25 Nov 2021 14:19:40 +0100 Subject: [PATCH 19/60] minor changes to docstrings --- avalanche/training/strategies/base_strategy.py | 13 +++++++++++++ avalanche/training/strategies/icarl.py | 2 +- avalanche/training/strategies/joint_training.py | 3 ++- avalanche/training/strategies/strategy_wrappers.py | 12 ++++++++---- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/avalanche/training/strategies/base_strategy.py b/avalanche/training/strategies/base_strategy.py index 1ee40490b..80e01c954 100644 --- a/avalanche/training/strategies/base_strategy.py +++ b/avalanche/training/strategies/base_strategy.py @@ -84,6 +84,8 @@ class BaseStrategy: """ DISABLED_CALLBACKS: Sequence[str] = () + """Internal class attribute used to disable some callbacks if a strategy + does not support them.""" def __init__(self, model: Module, optimizer: Optimizer, criterion=CrossEntropyLoss(), @@ -231,6 +233,7 @@ def mb_y(self): @property def mb_task_id(self): + """Current mini-batch task labels.""" assert len(self.mbatch) >= 3 return self.mbatch[-1] @@ -595,6 +598,7 @@ def _after_eval_dataset_adaptation(self, **kwargs): p.after_eval_dataset_adaptation(self, **kwargs) def eval_epoch(self, **kwargs): + """Evaluation loop over the current `self.dataloader`.""" for self.mbatch in self.dataloader: self._unpack_minibatch() self._before_eval_iteration(**kwargs) @@ -635,6 +639,10 @@ def _before_train_dataset_adaptation(self, **kwargs): p.before_train_dataset_adaptation(self, **kwargs) def model_adaptation(self, model=None): + """Adapts the model to the current data. + + Calls the :class:`~avalanche.models.DynamicModule`s adaptation. + """ if model is None: model = self.model @@ -644,9 +652,14 @@ def model_adaptation(self, model=None): return model.to(self.device) def forward(self): + """Compute the model's output given the current mini-batch.""" return avalanche_forward(self.model, self.mb_x, self.mb_task_id) def make_optimizer(self): + """Optimizer initialization. + + Called before each training experiene to configure the optimizer. + """ # we reset the optimizer's state after each experience. # This allows to add new parameters (new heads) and # freezing old units during the model's adaptation phase. diff --git a/avalanche/training/strategies/icarl.py b/avalanche/training/strategies/icarl.py index 9dda258af..8de9e07e9 100644 --- a/avalanche/training/strategies/icarl.py +++ b/avalanche/training/strategies/icarl.py @@ -31,7 +31,7 @@ def __init__(self, feature_extractor: Module, classifier: Module, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, evaluator: EvaluationPlugin = default_logger, eval_every=-1): - """ + """Init. :param feature_extractor: The feature extractor. :param classifier: The differentiable classifier that takes as input diff --git a/avalanche/training/strategies/joint_training.py b/avalanche/training/strategies/joint_training.py index a785e7648..22ee27162 100644 --- a/avalanche/training/strategies/joint_training.py +++ b/avalanche/training/strategies/joint_training.py @@ -48,7 +48,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, eval_mb_size: int = 1, device='cpu', plugins: Optional[Sequence['StrategyPlugin']] = None, evaluator=default_logger, eval_every=-1): - """ + """Init. + :param model: PyTorch model. :param optimizer: PyTorch optimizer. :param criterion: loss function. diff --git a/avalanche/training/strategies/strategy_wrappers.py b/avalanche/training/strategies/strategy_wrappers.py index 96f5c6f23..813c4f7f6 100644 --- a/avalanche/training/strategies/strategy_wrappers.py +++ b/avalanche/training/strategies/strategy_wrappers.py @@ -171,7 +171,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, evaluator: EvaluationPlugin = default_logger, eval_every=-1): - """ + """ Init. + :param model: The model. :param optimizer: The optimizer to use. :param criterion: The loss criterion to use. @@ -216,7 +217,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, evaluator: EvaluationPlugin = default_logger, eval_every=-1): - """ + """Init. + :param model: The model. :param optimizer: The optimizer to use. :param criterion: The loss criterion to use. @@ -260,7 +262,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, evaluator: EvaluationPlugin = default_logger, eval_every=-1): - """ + """Init. + :param model: The model. :param optimizer: The optimizer to use. :param criterion: The loss criterion to use. @@ -305,7 +308,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, evaluator: EvaluationPlugin = default_logger, eval_every=-1): - """ + """Init. + :param model: The model. :param optimizer: The optimizer to use. :param criterion: The loss criterion to use. From 454534e9228e9e7dca9cd71e51a4d54cf412d1da Mon Sep 17 00:00:00 2001 From: hamedhemati Date: Fri, 26 Nov 2021 13:53:14 +0100 Subject: [PATCH 20/60] change GroupBalancedDataloader to have a fixed batch size for any number of datasets --- avalanche/benchmarks/utils/data_loader.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/avalanche/benchmarks/utils/data_loader.py b/avalanche/benchmarks/utils/data_loader.py index 12e659960..9c96b80ca 100644 --- a/avalanche/benchmarks/utils/data_loader.py +++ b/avalanche/benchmarks/utils/data_loader.py @@ -98,6 +98,7 @@ class GroupBalancedDataLoader: def __init__(self, datasets: Sequence[AvalancheDataset], oversample_small_groups: bool = False, collate_mbatches=_default_collate_mbatches_fn, + batch_size: int = 32, **kwargs): """ Data loader that balances data from multiple datasets. @@ -115,6 +116,8 @@ def __init__(self, datasets: Sequence[AvalancheDataset], :param collate_mbatches: function that given a sequence of mini-batches (one for each task) combines them into a single mini-batch. Used to combine the mini-batches obtained separately from each task. + :param batch_size: the size of the batch. It must be greater than or + equal to the number of groups. :param kwargs: data loader arguments used to instantiate the loader for each group separately. See pytorch :class:`DataLoader`. """ @@ -123,8 +126,19 @@ def __init__(self, datasets: Sequence[AvalancheDataset], self.oversample_small_groups = oversample_small_groups self.collate_mbatches = collate_mbatches + # check if batch_size is larger than or equal to the number of datasets + assert batch_size >= len(datasets) + + # divide the batch between all datasets in the group + ds_batch_size = batch_size // len(datasets) + remaining = batch_size % len(datasets) + for data in self.datasets: - self.dataloaders.append(DataLoader(data, **kwargs)) + bs = ds_batch_size + if remaining > 0: + bs += 1 + remaining -= 1 + self.dataloaders.append(DataLoader(data, batch_size=bs, **kwargs)) self.max_len = max([len(d) for d in self.dataloaders]) def __iter__(self): From d59a9e222005ca672a9fe27fcf588b6a13788f7b Mon Sep 17 00:00:00 2001 From: Lorenzo Pellegrini Date: Sun, 28 Nov 2021 18:35:56 +0100 Subject: [PATCH 21/60] Added first 3 notebooks of the AvalancheDataset How-To series. --- .../avalanche_dataset_creation.ipynb | 440 +++++++++++++ .../avalanche_dataset_transformations.ipynb | 623 ++++++++++++++++++ .../how-tos/avalanche_dataset_preamble.ipynb | 172 +++++ 3 files changed, 1235 insertions(+) create mode 100644 notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb create mode 100644 notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb create mode 100644 notebooks/how-tos/avalanche_dataset_preamble.ipynb diff --git a/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb b/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb new file mode 100644 index 000000000..305243a8f --- /dev/null +++ b/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb @@ -0,0 +1,440 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9d2b1a53-2326-41ca-a439-ad9264cff63b", + "metadata": {}, + "source": [ + "---\n", + "description: Creation and manipulation of AvalancheDatasets and its subclasses.\n", + "---\n", + "\n", + "# Creating AvalancheDatasets\n", + "\n", + "The *AvalancheDataset* is an implementation of the PyTorch Dataset class which comes with many out-of-the-box functionalities. The *AvalancheDataset* (an its few subclass) are extensively used through the whole Avalanche library as the reference way to manipulate datasets:\n", + "\n", + "- The dataset carried by the `experience.dataset` field is always an *AvalancheDataset*.\n", + "- Benchmark creation functions accept *AvalancheDataset*s to create benchmarks where a finer control over task labels is required.\n", + "- Internally, benchmarks are created by manipulating *AvalancheDataset*s.\n", + "\n", + "This first *Mini How-To* will guide through the main ways you can use to **instantiate an _AvalancheDataset_** while the **other Mini How-Tos ([complete list here](https://avalanche.continualai.org/how-tos/avalanche_dataset_preamble)) will show how to use its functionalities**.\n", + "\n", + "It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page).\n", + "\n", + "Let's start by installing avalanche:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e583617f-2ad9-4ba9-a581-1d53eec5e205", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install git+https://github.com/ContinualAI/avalanche.git\n", + "\n", + "# Or, if you cloned Avalanche on your PC:\n", + "# import sys\n", + "# from pathlib import Path\n", + "# sys.path.append(str(Path.cwd() / '../../..'))" + ] + }, + { + "cell_type": "markdown", + "id": "114da4e5-8a44-4faa-b55e-3b0242b7a1c8", + "metadata": { + "tags": [] + }, + "source": [ + "## AvalancheDataset vs PyTorch Dataset\n", + "This mini How-To will guide you through the main ways used to instantiate an *AvalancheDataset*.\n", + "\n", + "First thing: the base class `AvalancheDataset` is a **wrapper for existing datasets**. Only two things must be considered when wrapping an existing dataset:\n", + "\n", + "- Apart from the x and y values, the resulting AvalancheDataset will also return a third value: the task label (which defaults to 0).\n", + "- The wrapped dataset must contain a valid **targets** field.\n", + "\n", + "The **targets field** is available is nearly all *torchvision* datasets. It must be a list containing the label for each data point (usually the y value). In this way, Avalanche can use that field when instantiating benchmarks like the \"Class/Task-Incremental* and *Domain-Incremental* ones.\n", + "\n", + "Avalanche exposes 4 classes of *AvalancheDataset*s which map exactly the 4 *Dataset* classes offered by PyTorch:\n", + "- `AvalancheDataset`: the base class, which acts a wrapper to existing *Dataset* instances.\n", + "- `AvalancheTensorDataset`: equivalent to PyTorch `TesnsorDataset`.\n", + "- `AvalancheSubset`: equivalent to PyTorch `Subset`.\n", + "- `AvalancheConcatDataset`: equivalent to PyTorch `ConcatDataset`.\n", + "\n", + "## 🛠️ Create an AvalancheDataset\n", + "Given a dataset (like MNIST), an *AvalancheDataset* can be instantiated as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2acfeea9-e8fe-4370-9a59-e034087b0acd", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from avalanche.benchmarks.utils import AvalancheDataset\n", + "from torchvision.datasets import MNIST\n", + "\n", + "# Instantiate the MNIST train dataset from torchvision\n", + "mnist_dataset = MNIST('mnist_data', download=True)\n", + "\n", + "# Create the AvalancheDataset\n", + "mnist_avalanche_dataset = AvalancheDataset(mnist_dataset)" + ] + }, + { + "cell_type": "markdown", + "id": "4f0ffacc-3990-41b9-bc85-cf6f053e8847", + "metadata": { + "tags": [] + }, + "source": [ + "Just like any other Dataset, a data point can be obtained using the `x, y = dataset[idx]` syntax. **When obtaining a data point from an AvalancheDataset, an additional third value (the task label) will be returned**:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d4069fe0-a458-4308-b018-3f6a9f9193e3", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=, y=5\n", + "x=, y=5, t=0\n" + ] + } + ], + "source": [ + "# Obtain the first instance from the original dataset\n", + "x, y = mnist_dataset[0]\n", + "print(f'x={x}, y={y}')\n", + "# Output: \"x=, y=5\"\n", + "\n", + "# Obtain the first instance from the AvalancheDataset\n", + "x, y, t = mnist_avalanche_dataset[0]\n", + "print(f'x={x}, y={y}, t={t}')\n", + "# Output: \"x=, y=5, t=0\"" + ] + }, + { + "cell_type": "markdown", + "id": "0df2be15-0471-473b-a603-089f41f54fc6", + "metadata": {}, + "source": [ + "**Useful tip:** if you are not sure if you are dealing with a PyTorch *Dataset* or an *AvalancheDataset*, or if you want to ignore task labels, you can use this syntax:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "ae39b667-6a3f-4c89-bdf8-196a42844ec5", + "metadata": {}, + "outputs": [], + "source": [ + "# You can use \"x, y, *_\" to manage both kinds of Datasets\n", + "x, y, *_ = mnist_dataset[0] # OK\n", + "x, y, *_ = mnist_avalanche_dataset[0] # OK" + ] + }, + { + "cell_type": "markdown", + "id": "1a5cdbbb-d3db-48f7-b946-d5a3c4151353", + "metadata": {}, + "source": [ + "## The AvalancheTensorDataset\n", + "The PyTorch *TensorDataset* is one of the most useful Dataset classes as it can be used to quickly prototype the data loading part of your code.\n", + "\n", + "A *TensorDataset* can be wrapped in an AvalancheDataset just like any Dataset, but this is not much convenient, as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8d3e9ca2-b971-4dea-9a80-f7f368e07ed5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=tensor([0.8383, 0.1409, 0.7622, 0.6625, 0.6322, 0.1188, 0.7383]), y=4, t=0\n" + ] + } + ], + "source": [ + "import torch\n", + "from torch.utils.data import TensorDataset\n", + "\n", + "\n", + "# Create 10 instances described by 7 features \n", + "x_data = torch.rand(10, 7)\n", + "\n", + "# Create the class labels for the 10 instances\n", + "y_data = torch.randint(0, 5, (10,))\n", + "\n", + "# Create the tensor dataset\n", + "tensor_dataset = TensorDataset(x_data, y_data)\n", + "\n", + "# Wrap it in an AvalancheDataset\n", + "wrapped_tensor_dataset = AvalancheDataset(tensor_dataset)\n", + "\n", + "# Obtain the first instance from the dataset\n", + "x, y, t = wrapped_tensor_dataset[0]\n", + "print(f'x={x}, y={y}, t={t}')\n", + "# Output: \"x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0\"" + ] + }, + { + "cell_type": "markdown", + "id": "669670c9-e3ea-44a2-aecf-40bd8cb9cdb8", + "metadata": {}, + "source": [ + "**Instead, it is recommended to use the AvalancheTensorDataset** class to get the same result. In this way, you can just skip one intermediate step." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ab827b7a-de79-4a38-bfd5-18dd582404aa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=tensor([0.8383, 0.1409, 0.7622, 0.6625, 0.6322, 0.1188, 0.7383]), y=4, t=0\n" + ] + } + ], + "source": [ + "from avalanche.benchmarks.utils import AvalancheTensorDataset\n", + "\n", + "# Create the tensor dataset\n", + "avl_tensor_dataset = AvalancheTensorDataset(x_data, y_data)\n", + "\n", + "# Obtain the first instance from the AvalancheTensorDataset\n", + "x, y, t = avl_tensor_dataset[0]\n", + "print(f'x={x}, y={y}, t={t}')\n", + "# Output: \"x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0\"" + ] + }, + { + "cell_type": "markdown", + "id": "6f614c68-c0dc-49d9-a3e2-03348e248ca9", + "metadata": {}, + "source": [ + "In both cases, **AvalancheDataset will automatically populate its _targets_ field by using the values from the second Tensor** (which usually contains the Y values). This behaviour can be customized by passing a custom `targets` constructor parameter (by either passing a list of targets or the index of the Tensor to use).\n", + "\n", + "The cell below shows the content of the target field of the dataset created in the cell above. Notice that the *targets* field has been filled with the content of the second Tensor (*y\\_data*)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "1f81487c-e05e-4b98-addd-25ef5f7d9746", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "y_data= tensor([4, 0, 2, 2, 1, 1, 0, 0, 0, 1])\n", + "targets field= [tensor(4), tensor(0), tensor(2), tensor(2), tensor(1), tensor(1), tensor(0), tensor(0), tensor(0), tensor(1)]\n" + ] + } + ], + "source": [ + "# Check the targets field\n", + "print('y_data=', y_data)\n", + " # Output: \"y_data= tensor([4, 3, 3, 2, 0, 1, 3, 3, 3, 2])\"\n", + "\n", + "print('targets field=', avl_tensor_dataset.targets)\n", + "# Output: \"targets field= [tensor(4), tensor(3), tensor(3), tensor(2), \n", + "# tensor(0), tensor(1), tensor(3), tensor(3), tensor(3), tensor(2)]\"" + ] + }, + { + "cell_type": "markdown", + "id": "62bdf7d2-97be-4e20-9ef5-2b51bebcfecd", + "metadata": {}, + "source": [ + "## The AvalancheSubset and AvalancheConcatDataset classes\n", + "Avalanche offers the `AvalancheSubset` and `AvalancheConcatDataset` implementations that extend the functionalities of PyTorch *Subset* and *ConcatDataset*.\n", + "\n", + "Regarding the subsetting operation, `AvalancheSubset` behaves in the same way the PyTorch `Subset` class does: both implementations accept a dataset and a list of indices as parameters. The resulting Subset is not a copy of the dataset, it's just a view. This is similar to creating a view of a NumPy array by passing a list of indexes using the `numpy_array[list_of_indices]` syntax. This can be used to both *create a smaller dataset* and to *change the order of data points* in the dataset.\n", + "\n", + "Here we create a toy dataset in which each X and Y values are *int*s. We then obtain a subset of it by creating an **AvalancheSubset**:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "02efbde8-5216-4595-acab-999c6386845b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The subset contains 4 instances.\n", + "x=50, y=10, t=0\n", + "x=55, y=15, t=0\n", + "x=58, y=18, t=0\n", + "x=52, y=12, t=0\n" + ] + } + ], + "source": [ + "from avalanche.benchmarks.utils import AvalancheSubset\n", + "\n", + "# Define the X values of 10 instances (each instance is an int)\n", + "x_data_toy = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]\n", + "\n", + "# Define the class labels for the 10 instances\n", + "y_data_toy = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n", + "\n", + "# Create the tensor dataset\n", + "# Note: AvalancheSubset can also be applied to PyTorch TensorDataset directly!\n", + "# However, note that PyTorch TensorDataset doesn't support Python lists...\n", + "# ... (it only supports Tensors) while AvalancheTensorDataset does.\n", + "toy_dataset = AvalancheTensorDataset(x_data_toy, y_data_toy) \n", + "\n", + "# Define the indices for the subset\n", + "# Here we want to obtain a subset containing only the data points...\n", + "# ... at indices 0, 5, 8, 2 (in this specific order)\n", + "subset_indices = [0, 5, 8, 2]\n", + "\n", + "# Create the subset\n", + "avl_subset = AvalancheSubset(toy_dataset, indices=subset_indices)\n", + "print('The subset contains', len(avl_subset), 'instances.')\n", + "# Output: \"The subset contains 4 instances.\"\n", + "\n", + "# Obtain instances from the AvalancheSubset\n", + "for x, y, t in avl_subset:\n", + " print(f'x={x}, y={y}, t={t}')\n", + "# Output:\n", + "# x=50, y=10, t=0\n", + "# x=55, y=15, t=0\n", + "# x=58, y=18, t=0\n", + "# x=52, y=12, t=0" + ] + }, + { + "cell_type": "markdown", + "id": "6ccde422-314d-4c6a-9d36-3bfd2e43d3c3", + "metadata": {}, + "source": [ + "Concatenation is even simpler. Just like with PyTorch *ConcatDataset*, one can easily concatentate datasets with **AvalancheConcatDataset**.\n", + "\n", + "Both *AvalancheConcatDataset* and PyTorch *ConcatDataset* accept a list of datasets to concatenate." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "fdf11bef-17cf-499d-8407-3c7c4e9f0a0b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The concat dataset contains 10 instances.\n", + "x=50, y=10, t=0\n", + "x=51, y=11, t=0\n", + "x=52, y=12, t=0\n", + "x=53, y=13, t=0\n", + "x=54, y=14, t=0\n", + "x=60, y=20, t=0\n", + "x=61, y=21, t=0\n", + "x=62, y=22, t=0\n", + "x=63, y=23, t=0\n", + "x=64, y=24, t=0\n" + ] + } + ], + "source": [ + "from avalanche.benchmarks.utils import AvalancheConcatDataset\n", + "\n", + "# Define the 2 datasets to be concatenated\n", + "x_data_toy_1 = [50, 51, 52, 53, 54]\n", + "y_data_toy_1 = [10, 11, 12, 13, 14]\n", + "x_data_toy_2 = [60, 61, 62, 63, 64]\n", + "y_data_toy_2 = [20, 21, 22, 23, 24]\n", + "\n", + "# Create the datasets\n", + "toy_dataset_1 = AvalancheTensorDataset(x_data_toy_1, y_data_toy_1) \n", + "toy_dataset_2 = AvalancheTensorDataset(x_data_toy_2, y_data_toy_2) \n", + "\n", + "# Create the concat dataset\n", + "avl_concat = AvalancheConcatDataset([toy_dataset_1, toy_dataset_2])\n", + "print('The concat dataset contains', len(avl_concat), 'instances.')\n", + "# Output: \"The concat dataset contains 10 instances.\"\n", + "\n", + "# Obtain instances from the AvalancheConcatDataset\n", + "for x, y, t in avl_concat:\n", + " print(f'x={x}, y={y}, t={t}')\n", + "# Output:\n", + "# x=51, y=11, t=0\n", + "# x=52, y=12, t=0\n", + "# x=53, y=13, t=0\n", + "# x=54, y=14, t=0\n", + "# x=60, y=20, t=0\n", + "# x=61, y=21, t=0\n", + "# x=62, y=22, t=0\n", + "# x=63, y=23, t=0\n", + "# x=64, y=24, t=0" + ] + }, + { + "cell_type": "markdown", + "id": "2901ba6e-7653-4356-852b-e402693897ca", + "metadata": {}, + "source": [ + "## Dataset Creation wrap-up\n", + "This *Mini How-To* showed you how to **create instances of AvalancheDataset (and its subclasses)**.\n", + "\n", + "Other *Mini How-To*s will guide you through the functionalities offered by AvalancheDataset. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanche_dataset_preamble)." + ] + }, + { + "cell_type": "markdown", + "id": "18a9baff-7fab-4757-b155-fdb388bad8c9", + "metadata": {}, + "source": [ + "## 🤝 Run it on Google Colab\n", + "\n", + "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb b/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb new file mode 100644 index 000000000..d3d2535f3 --- /dev/null +++ b/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb @@ -0,0 +1,623 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "53277e45-b7d7-4857-a79d-d7218bd6b6a9", + "metadata": {}, + "source": [ + "---\n", + "description: Dealing with transformations (groups, appending, replacing, freezing).\n", + "---\n", + "\n", + "# Advanced Transformations\n", + "AvalancheDataset (and its subclasses like the Avalanche*Tensor/Subset/Concat*Dataset) allow for a finer control over transformations. While torchvision (and other) datasets allow for a minimal mechanism to apply transformations, with AvalancheDataset one can:\n", + "1. Have multiple **transformation \"groups\"** in the same dataset (like separated train and test transformations).\n", + "2. **Append, replace and remove transformations**, even by using nested Subset/Concat Datasets.\n", + "3. **Freeze transformations**, so that they can't be changed.\n", + "\n", + "The following sub-sections show examples on how to use these features. Please note that all the constructor parameters and the methods described in this How-To can be used on AvalancheDataset subclasses as well. For more info on all the available subclasses, refer to [this Mini How-To](https://avalanche.continualai.org/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation).\n", + "\n", + "It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page).\n", + "\n", + "Let's start by installing Avalanche:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "470e6f0b-ecc2-45c3-af53-112da5d7c37e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/lorenzo/Desktop/github_repos/avalanche/notebooks\n" + ] + } + ], + "source": [ + "!pip install git+https://github.com/ContinualAI/avalanche.git\n", + "\n", + "# Or, if you cloned Avalanche on your PC:\n", + "# import sys\n", + "# from pathlib import Path\n", + "# sys.path.append(str(Path.cwd() / '../../..'))" + ] + }, + { + "cell_type": "markdown", + "id": "e770eef5-86d4-4601-b26f-ea12013515df", + "metadata": {}, + "source": [ + "## Transformation groups\n", + "AvalancheDatasets can contain multiple **transformation groups**. This can be useful to keep train and test transformations in the same dataset and to have different set of transformations. This may come in handy in many situations (for instance, to apply ad-hoc transformations to replay data).\n", + "\n", + "As in torchvision datasets, AvalancheDataset supports the two kind of transformations: the `transform`, which is applied to X values, and the `target_transform`, which is applied to Y values. The latter is rarely used. This means that **a transformation group is a pair of transformations to be applied to the X and Y values** of each instance returned by the dataset. In both torchvision and Avalanche implementations, **a transformation must be a function (or other callable object)** that accepts one input (the X or Y value) and outputs its transformed version. This pair of functions is stored in the `transform` and `target_transform` fields of the dataset. A comprehensive guide on transformations can be found in the [torchvision documentation](https://pytorch.org/vision/stable/transforms.html).\n", + "\n", + "In the following example, a MNIST dataset is created and then wrapped in an AvalancheDataset. When creating the AvalancheDataset, we can set *train* and *eval* transformations by passing a *transform\\_groups* parameter. Train transformations usually include some form of random augmentation, while eval transformations usually include a sequence of deterministic transformations only. Here we define the sequence of train transformations as a random rotation followed by the ToTensor operation. The eval transformations only include the ToTensor operation." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "482132af-cfa8-4c16-ae27-5e7a67c75a0c", + "metadata": {}, + "outputs": [], + "source": [ + "from torchvision import transforms\n", + "from torchvision.datasets import MNIST\n", + "from avalanche.benchmarks.utils import AvalancheDataset\n", + "\n", + "mnist_dataset = MNIST('mnist_data', download=True)\n", + "\n", + "# Define the training transformation for X values\n", + "train_transformation = transforms.Compose([\n", + " transforms.RandomRotation(45),\n", + " transforms.ToTensor(),\n", + "])\n", + "# Define the training transformation for Y values (rarely used)\n", + "train_target_transformation = None\n", + "\n", + "# Define the test transformation for X values\n", + "eval_transformation = transforms.ToTensor()\n", + "# Define the test transformation for Y values (rarely used)\n", + "eval_target_transformation = None\n", + "\n", + "transform_groups = {\n", + " 'train': (train_transformation, train_target_transformation),\n", + " 'eval': (eval_transformation, eval_target_transformation)\n", + "}\n", + "\n", + "avl_mnist_transform = AvalancheDataset(mnist_dataset, transform_groups=transform_groups)" + ] + }, + { + "cell_type": "markdown", + "id": "d5f7cca4-e2e7-456b-b6da-54c1c8579eb1", + "metadata": {}, + "source": [ + "Of course, one can also just use the `transform` and `target_transform` constructor parameters to set the transformations for both the *train* and the *eval* groups. However, it is recommended to use the approach based on *transform\\_groups* (shown in the code above) as it is much more flexible." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "51fbd4bf-9535-446a-bc95-6d1cd0dd96f9", + "metadata": {}, + "outputs": [], + "source": [ + "# Not recommended: use transform_groups instead\n", + "avl_mnist_same_transforms = AvalancheDataset(mnist_dataset, transform=train_transformation)" + ] + }, + { + "cell_type": "markdown", + "id": "fbdfa287-347e-4847-95f8-c5a00a4a2107", + "metadata": {}, + "source": [ + "### Using `.train()` and `.eval()`\n", + "\n", + "**The default behaviour of the AvalancheDataset is to use transformations from the _train_ group.** However, one can easily obtain a version of the dataset where the *eval* group is used. Note: when obtaining the dataset of experiences from the test stream, those datasets will already be using the *eval* group of transformations so you don't need to switch to the eval group ;).\n", + "\n", + "As noted before, transformations for the current group are loaded in the `transform` and `target_transform` fields. These fields can be changed directly, but this is *NOT* recommended, as this will not create a copy of the dataset and may probably affect other parts of the code in which the dataset is used.\n", + "\n", + "The recommended way to switch between the *train* and *eval* groups is to use the `.train()` and `.eval()` methods to obtain a copy (view) of the dataset with the proper transformations enabled. This is another very handy feature of the AvalancheDataset: **methods that manipulate the AvalancheDataset fields (and transformations) always create a view of the dataset. The original dataset is never changed.**\n", + "\n", + "In the following cell we use the *avl\\_mnist\\_transform* dataset created in the cells above. We first obtain a view of it in which *eval* transformations are enabled. Then, starting from this view, we obtain a version of it in which *train* transformations are enabled. We want to double-stress that `.train()` and `.eval()` never change the group of the dataset on which they are called: they always create a view.\n", + "\n", + "One can check that the correct transformation group is in use by looking at the content of the *transform/target_transform* fields." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6f8500fa-0f7f-48f7-a26c-d77a1588a244", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original dataset transformation: Compose(\n", + " RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", + " ToTensor()\n", + ")\n", + "--------------------------------\n", + "Eval version of the dataset: ToTensor()\n", + "--------------------------------\n", + "Back to train transformations: Compose(\n", + " RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", + " ToTensor()\n", + ")\n" + ] + } + ], + "source": [ + "# Obtain a view of the dataset in which eval transformations are enabled\n", + "avl_mnist_eval = avl_mnist_transform.eval()\n", + "\n", + "# Obtain a view of the dataset in which we get back to train transforms\n", + "# Basically, avl_mnist_transform ~= avl_mnist_train\n", + "avl_mnist_train = avl_mnist_eval.train()\n", + "\n", + "# Check the current transformations function for the 3 datasets\n", + "print('Original dataset transformation:', avl_mnist_transform.transform)\n", + "# Output:\n", + "# Original dataset transformation: Compose(\n", + "# RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", + "# ToTensor()\n", + "# )\n", + "print('--------------------------------')\n", + "print('Eval version of the dataset:', avl_mnist_eval.transform)\n", + "# Output: \"Eval version of the dataset: ToTensor()\"\n", + "print('--------------------------------')\n", + "print('Back to train transformations:', avl_mnist_train.transform)\n", + "# Output:\n", + "# Back to train transformations: Compose(\n", + "# RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", + "# ToTensor()\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "id": "8172b379-d60a-43ba-8e0d-c4fcdc0e997e", + "metadata": {}, + "source": [ + "### Custom transformation groups\n", + "In *AvalancheDataset*s the **_train_ and _eval_ transformation groups are always available**. However, *AvalancheDataset* also supports **custom transformation groups**.\n", + "\n", + "The following example shows how to create an AvalancheDataset with an additional group named *replay*. We define the replay transformation as a random crop followed by the ToTensor operation." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "69c8912d-b826-4265-ba71-c33501a1f956", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "replay_transform = transforms.Compose([\n", + " transforms.RandomCrop(28, padding=4),\n", + " transforms.ToTensor()\n", + "])\n", + "\n", + "replay_target_transform = None\n", + "\n", + "transform_groups_with_replay = {\n", + " 'train': (None, None),\n", + " 'eval': (None, None),\n", + " 'replay': (replay_transform, replay_target_transform)\n", + "}\n", + "\n", + "AvalancheDataset(mnist_dataset, transform_groups=transform_groups_with_replay)" + ] + }, + { + "cell_type": "markdown", + "id": "6bc0508a-bc4d-4896-984c-609c5803f9e6", + "metadata": {}, + "source": [ + "However, once created the dataset will use the *train* group. There are two ways to **switch to our custom group**:\n", + "- Set the group when creating the dataset using the `initial_transform_group` constructor parameter\n", + "- Switch to the group using the `.with_transforms(group_name)` method\n", + "\n", + "The `.with_transforms(group_name)` method behaves in the same way `.train()` and `.eval()` do by creating a view of the original dataset.\n", + "\n", + "The following example shows how to use both methods:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2fd29287-da97-4aad-ab3c-cfd514629ad8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Compose(\n", + " RandomCrop(size=(28, 28), padding=4)\n", + " ToTensor()\n", + ")\n", + "Compose(\n", + " RandomCrop(size=(28, 28), padding=4)\n", + " ToTensor()\n", + ")\n" + ] + } + ], + "source": [ + "# Method 1: create the dataset with \"replay\" as the default group\n", + "avl_mnist_custom_transform_1 = AvalancheDataset(\n", + " mnist_dataset,\n", + " transform_groups=transform_groups_with_replay,\n", + " initial_transform_group='replay')\n", + "\n", + "print(avl_mnist_custom_transform_1.transform)\n", + "\n", + "# Method 2: switch to \"replay\" using `.with_transforms(group_name)`\n", + "avl_mnist_custom_transform_not_enabled = AvalancheDataset(\n", + " mnist_dataset,\n", + " transform_groups=transform_groups_with_replay)\n", + "\n", + "avl_mnist_custom_transform_2 = avl_mnist_custom_transform_not_enabled.with_transforms('replay')\n", + "print(avl_mnist_custom_transform_2.transform)\n", + "\n", + "# Both prints output:\n", + "# Compose(\n", + "# RandomCrop(size=(28, 28), padding=4)\n", + "# ToTensor()\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "id": "08fc9f3f-f95a-49ed-ba94-1bbec8960235", + "metadata": {}, + "source": [ + "## Appending transformations\n", + "\n", + "In the standard torchvision datasets the only way to append (that is, add a new transformation step to the list of existing one) is to change the *transform* field directly by doing something like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e4d02b6e-0e73-4205-a497-a1540ff03185", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Compose(\n", + " ToTensor()\n", + " RandomCrop(size=(28, 28), padding=4)\n", + ")\n" + ] + } + ], + "source": [ + "# Append a transform by using torchvision datasets (>>> DON'T DO THIS! <<<)\n", + "\n", + "# Create the dataset\n", + "mnist_dataset_w_totensor = MNIST('mnist_data', download=True, transform=transforms.ToTensor())\n", + "\n", + "# Append a transform\n", + "to_append_transform = transforms.RandomCrop(size=(28, 28), padding=4)\n", + "mnist_dataset_w_totensor.transform = transforms.Compose(\n", + " [mnist_dataset_w_totensor.transform, to_append_transform]\n", + ")\n", + "print(mnist_dataset_w_totensor.transform)\n", + "# Prints:\n", + "# Compose(\n", + "# ToTensor()\n", + "# RandomCrop(size=(28, 28), padding=4)\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "id": "55cb1749-2f72-4780-9699-99b97544f4da", + "metadata": {}, + "source": [ + "This solution has many huge drawbacks:\n", + "- The transformation field of the dataset is changed directly. This will affect other parts of the code that use that dataset instance.\n", + "- If the initial transform is `None`, then `Compose` will not complain, but the process will crash later (try it by yourself: replace the first element of Compose in cell above with `None`, then try obtaining a data point from the dataset).\n", + "- If you need to change transformations only temporarly to do some specific things in a limited part of the code, then you need to store the previous set of transformations in some variable in order to switch back to them later.\n", + "\n", + "AvalancheDataset offers a very simple method to append transformations without incurring in those issues. The `.add_transforms(transform=None, target_transform=None)` method will append the given transform(s) **to the currently enabled transform group** and will return a new (a view actually) dataset with given transformations appended to the existing ones. The original dataset is not affected. One can also use `.add_transforms_to_group(group_name, transform, target_transform)` to change transformations for a different group.\n", + "\n", + "The next cell shows how to use `.add_transforms(...)` to append the *to\\_append\\_transform* transform defined in the cell above." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "99a295d6-7a3f-4e8a-89af-9f97915848c2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "With appended transforms: Compose(\n", + " ToTensor()\n", + " RandomCrop(size=(28, 28), padding=4)\n", + ")\n", + "Original dataset: ToTensor()\n" + ] + } + ], + "source": [ + "# Create the dataset\n", + "avl_mnist = AvalancheDataset(MNIST('mnist_data', download=True), transform=transforms.ToTensor())\n", + "\n", + "# Append a transformation. Simple as:\n", + "avl_mnist_appended_transform = avl_mnist.add_transforms(to_append_transform)\n", + "\n", + "print('With appended transforms:', avl_mnist_appended_transform.transform)\n", + "# Prints:\n", + "# With appended transforms: Compose(\n", + "# ToTensor()\n", + "# RandomCrop(size=(28, 28), padding=4)\n", + "# )\n", + "\n", + "# Check that the original dataset was not affected:\n", + "print('Original dataset:', avl_mnist.transform)\n", + "# Prints: \"Original dataset: ToTensor()\"" + ] + }, + { + "cell_type": "markdown", + "id": "19f6a174-4073-4b24-a604-95df9ff5a0f3", + "metadata": {}, + "source": [ + "Note that by using `.add_transforms(...)`:\n", + "\n", + "- The original dataset is not changed, which means that other parts of the code that use that dataset instance are not affected.\n", + "- You don't need to worry about *None* transformations.\n", + "- In order to revert to the original transformations you don't need to keep a copy of them: the original dataset is not affected!" + ] + }, + { + "cell_type": "markdown", + "id": "c5ee8e22-982d-4fa1-ab2a-97e47bb1d693", + "metadata": {}, + "source": [ + "## Replacing transformations\n", + "\n", + "The replacement operation follows the same idea (and benefits) of the append one. By using `.replace_transforms(transform, target_transform)` one can obtain a view of the original dataset in which the **transformaations for the current group** are replaced with the given ones. One may also change tranformations for other groups by passing the name of the group as the optional parameter `group`. As with any transform-related operation, the original dataset is not affected. \n", + "\n", + "Note: one can use `.replace_transforms(...)` to remove previous transformations (by passing `None` as the new transform).\n", + "\n", + "The following cell shows how to use `.replace_transforms(...)` to replace the transformations of the current group:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "0e9f9c46-9d3d-40eb-b7b8-afad15668bc9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "With replaced transform: RandomCrop(size=(28, 28), padding=4)\n", + "Original dataset: ToTensor()\n" + ] + } + ], + "source": [ + "new_transform = transforms.RandomCrop(size=(28, 28), padding=4)\n", + "\n", + "# Append a transformation. Simple as:\n", + "avl_mnist_replaced_transform = avl_mnist.replace_transforms(new_transform, None)\n", + "\n", + "print('With replaced transform:', avl_mnist_replaced_transform.transform)\n", + "# Prints: \"With replaces transforms: RandomCrop(size=(28, 28), padding=4)\"\n", + "\n", + "# Check that the original dataset was not affected:\n", + "print('Original dataset:', avl_mnist.transform)\n", + "# Prints: \"Original dataset: ToTensor()\"" + ] + }, + { + "cell_type": "markdown", + "id": "bc2e4781-0f2f-4eb6-b61e-affc8aaf255c", + "metadata": {}, + "source": [ + "## Freezing transformations\n", + "\n", + "One last functionality regarding transformations is the ability to \"freeze\" transformations. Freezing transformations menas **permanently glueing transformations to the dataset so that they can't be replaced or changed in any way** (usually by mistake). Frozen transformations cannot be changed by using `.replace_transforms(...)` or even by changing the `transform` field directly.\n", + "\n", + "One may wonder when this may come in handy... in fact, you will probably rarely need to freeze transformations. However, imagine having to instantiate the PermutedMNIST benchmark. You want the permutation transformation to not be changed by mistake. However, the end users do not know how the internal implementations of the benchmark works, so they may end up messing with those transformations. By freezing the permutation transformation, users cannot mess with it.\n", + "\n", + "Transformations for all transform groups can be frozen at once by using `.freeze_transforms()`. Transformations can be frozen for a single group by using `.freeze_group_transforms(group_name)`. As always, those methods return a view of the original dataset.\n", + "\n", + "The cell below shows a simplified excerpt from the [PermutedMNIST benchmark implementation](https://github.com/ContinualAI/avalanche/blob/master/avalanche/benchmarks/classic/cmnist.py). First, a *PixelsPermutation* instance is created. That instance is a transformation that will permute the pixels of the input image. We then create the train end test sets. Once created, transformations for those datasets are frozen using `.freeze_transforms()`." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "af97e04b-01b4-4a32-ba1f-42ed94977c56", + "metadata": {}, + "outputs": [], + "source": [ + "from avalanche.benchmarks.classic.cmnist import PixelsPermutation\n", + "import numpy as np\n", + "import torch\n", + "\n", + "# Instantiate MNIST train and test sets\n", + "mnist_train = MNIST('mnist_data', train=True, download=True)\n", + "mnist_test = MNIST('mnist_data', train=False, download=True)\n", + " \n", + "# Define the transformation used to permute the pixels\n", + "rng_seed = 4321\n", + "rng_permute = np.random.RandomState(rng_seed)\n", + "idx_permute = torch.from_numpy(rng_permute.permutation(784)).type(torch.int64)\n", + "permutation_transform = PixelsPermutation(idx_permute)\n", + "\n", + "# Define the transforms group\n", + "perm_group_transforms = dict(\n", + " train=(permutation_transform, None),\n", + " eval=(permutation_transform, None)\n", + ")\n", + "\n", + "# Create the datasets and freeze transforms\n", + "# Note: one can call \"freeze_transforms\" on constructor result\n", + "# or you can do this in 2 steps. The result is the same (obviously).\n", + "# The next part show both ways:\n", + "\n", + "# Train set\n", + "permuted_train_set = AvalancheDataset(\n", + " mnist_train, \n", + " transform_groups=perm_group_transforms).freeze_transforms()\n", + "\n", + "# Test set\n", + "permuted_test_set = AvalancheDataset(\n", + " mnist_test, transform_groups=perm_group_transforms, \n", + " initial_transform_group='eval')\n", + "permuted_test_set = permuted_test_set.freeze_transforms()" + ] + }, + { + "cell_type": "markdown", + "id": "3f5fea92-cb21-4144-bee8-6e2b37606d25", + "metadata": {}, + "source": [ + "In this way, that transform can't be removed. However, remember that one can always append other transforms atop of frozen transforms.\n", + "\n", + "The cell below shows that `replace_transforms` can't remove frozen transformations:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "62ae4c3e-da05-494c-9df4-394774a42908", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before replace_transforms:\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "After replace_transforms:\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# First, show that the image pixels are permuted\n", + "print('Before replace_transforms:')\n", + "display(permuted_train_set[0][0].resize((192, 192), 0))\n", + "\n", + "# Try to remove the permutation\n", + "with_removed_transforms = permuted_train_set.replace_transforms(None, None)\n", + "\n", + "print('After replace_transforms:')\n", + "display(permuted_train_set[0][0].resize((192, 192), 0))\n", + "display(with_removed_transforms[0][0].resize((192, 192), 0))" + ] + }, + { + "cell_type": "markdown", + "id": "553d4633-c7aa-46c4-b2bb-90267b998e74", + "metadata": {}, + "source": [ + "## Transformations wrap-up\n", + "This completes the *Mini How-To* for the functionalities of the *AvalancheDataset* related to **transformations**. \n", + "\n", + "Here you learned how to use **transformation groups** and how to **append/replace/freeze transformations** in a simple way.\n", + "\n", + "Other *Mini How-To*s will guide you through the other functionalities offered by the *AvalancheDataset* class. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanche_dataset_preamble)." + ] + }, + { + "cell_type": "markdown", + "id": "792a197d-a0c3-44c2-87fb-fae43f687195", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 🤝 Run it on Google Colab\n", + "\n", + "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/how-tos/avalanche_dataset_preamble.ipynb b/notebooks/how-tos/avalanche_dataset_preamble.ipynb new file mode 100644 index 000000000..ea6adc6d4 --- /dev/null +++ b/notebooks/how-tos/avalanche_dataset_preamble.ipynb @@ -0,0 +1,172 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d369a312", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "pycharm": { + "name": "#%% md\n" + }, + "tags": [] + }, + "source": [ + "---\n", + "description: Dealing with AvalancheDatasets\n", + "---\n", + "\n", + "# The AvalancheDataset\n", + "\n", + "The `AvalancheDataset` is an implementation of the PyTorch `Dataset` class that comes with many useful out-of-the-box functionalities. For most users, the *AvalancheDataset* can be used as a plain PyTorch Dataset that will return `x, y, t` elements. However, the AvalancheDataset is much more powerful than a simple PyTorch Dataset. \n", + "\n", + "\n", + "**A serie of _Mini How-Tos_** will guide you through the functionalities of the *AvalancheDataset* and its subclasses:\n", + "\n", + "- [The AvalancheDataset](https://avalanche.continualai.org/how-tos/avalanche_dataset_preamble) (this page)\n", + " - [Preamble](#🎯-Preamble:-Non-Avalanche-PyTorch-Datasets)\n", + "- [Creating AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation)\n", + "- [Advanced Transformations](https://avalanche.continualai.org/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations)\n", + "\n", + "However, brefore jumping to the *Mini How-To*s, **we recommend having a look at the basic notions of Dataset and DataLoader by reading the following short preamble**:" + ] + }, + { + "cell_type": "markdown", + "id": "ccdb4767", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 🎯 Preamble: Non-Avalanche PyTorch Datasets\n", + "This short preamble will briefly go through the basic notions of Dataset offered natively by PyTorch. A solid grasp of these notions are needed to understand:\n", + "1. How PyTorch data loading works in general\n", + "2. How AvalancheDatasets differs from PyTorch Datasets\n", + "\n", + "### 📚 Dataset: general definition\n", + "\n", + "In PyTorch, **a `Dataset` is a class** exposing two methods:\n", + "- `__len__()`, which returns the amount of instances in the dataset (as an `int`). \n", + "- `__getitem__(idx)`, which returns the data point at index `idx`.\n", + "\n", + "In other words, a Dataset instance is just an object for which, similarly to a list, one can simply:\n", + "- Obtain its length using the Python `len(dataset)` function.\n", + "- Obtain a single data point using the `x, y = dataset[idx]` syntax.\n", + "\n", + "The content of the dataset can be either loaded in memory when the dataset is instantiated (like the torchvision MNIST dataset does) or, for big datasets like ImageNet, the content is kept on disk, with the dataset keeping the list of files in an internal field. In this case, data is loaded from the storage on-the-fly when `__getitem__(idx)` is called. The way those things are managed is specific to each dataset implementation.\n", + "\n", + "### PyTorch Datasets\n", + "The PyTorch library offers 4 Dataset implementations:\n", + "- `Dataset`: an interface defining the `__len__` and `__getitem__` methods.\n", + "- `TensorDataset`: instantiated by passing X and Y tensors. Each row of the X and Y tensors is interpreted as a data point. The `__getitem__(idx)` method will simply return the `idx`-th row of X and Y tensors.\n", + "- `ConcatDataset`: instantiated by passing a list of datasets. The resulting dataset is a concatenation of those datasets.\n", + "- `Subset`: instantiated by passing a dataset and a list of indices. The resulting dataset will only contain the data points described by that list of indices.\n", + "\n", + "As explained in the mini *How-To*s, Avalanche offers a customized version for all these 4 datasets.\n", + "\n", + "### Transformations\n", + "Most datasets from the *torchvision* libraries (as well as datasets found \"in the wild\") allow for a `transformation` function to be passed to the dataset constructor. The support for transformations is not mandatory for a dataset, but it is quite common to support them. The transformation is used to process the X value of a data point before returning it. This is used to normalize values, apply augmentations, etcetera.\n", + "\n", + "As explained in the mini *How-To*s, the `AvalancheDataset` class implements a very rich and powerful set of functionalities for managing transformations.\n", + "\n", + "### Quick note on the IterableDataset class\n", + "A variation of the standard `Dataset` exist in PyTorch: the [IterableDataset](https://pytorch.org/docs/stable/data.html#iterable-style-datasets). When using an `IterableDataset`, one can load the data points in a sequential way only (by using a tape-alike approach). The `dataset[idx]` syntax and `len(dataset)` function are not allowed. **Avalanche does NOT support `IterableDataset`s.** You shouldn't worry about this because, realistically, you will never encounter such datasets.\n", + "\n", + "## DataLoader\n", + "The `Dataset` is a very simple object that only returns one data point given its index. In order to create minibatches and speed-up the data loading process, a `DataLoader` is required.\n", + "\n", + "The PyTorch `DataLoader` class is a very efficient mechanism that, given a `Dataset`, will return **minibatches** by optonally **shuffling** data brefore each epoch and by **loading data in parallel** by using multiple workers.\n", + "\n", + "## Preamble wrap-up\n", + "To wrap-up, let's see how the native, *non-Avalanche*, PyTorch components work in practice. In the following code we create a `TensorDataset` and then we load it in minibatches using a `DataLoader`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8bce4be3-91ef-4816-9a3f-5c392ef05027", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n" + ] + } + ], + "source": [ + "import torch\n", + "from torch.utils.data.dataset import TensorDataset\n", + "from torch.utils.data.dataloader import DataLoader\n", + "\n", + "# Create a dataset of 100 data points described by 22 features + 1 class label\n", + "x_data = torch.rand(100, 22)\n", + "y_data = torch.randint(0, 5, (100,))\n", + "\n", + "# Create the Dataset\n", + "my_dataset = TensorDataset(x_data, y_data)\n", + "\n", + "# Create the DataLoader\n", + "my_dataloader = DataLoader(my_dataset, batch_size=10, shuffle=True, num_workers=4)\n", + "\n", + "# Run one epoch\n", + "for x_minibatch, y_minibatch in my_dataloader:\n", + " print('Loaded minibatch of', len(x_minibatch), 'instances')\n", + "# Output: \"Loaded minibatch of 10 instances\" x10 times" + ] + }, + { + "cell_type": "markdown", + "id": "93f6fdec-f1d0-4cdc-a6e0-6e7c0a6b3be7", + "metadata": {}, + "source": [ + "## Next steps\n", + "With these notions in mind, you can start start your journey on understanding the functionalities offered by the AvalancheDatasets by going through the *Mini How-To*s.\n", + "\n", + "Refer to the index found at the beginning of this page for the complete [list of the *Mini How-To*s regarding AvalancheDatasets](#The-AvalancheDataset)." + ] + }, + { + "cell_type": "markdown", + "id": "02de0ce4-2711-4832-8b0e-516040483ae5", + "metadata": {}, + "source": [ + "## 🤝 Run it on Google Colab\n", + "\n", + "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanche_dataset_preamble.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 51c2ac4415252ff1f1ae947556b1c8d6ddf460af Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Mon, 29 Nov 2021 12:21:10 +0100 Subject: [PATCH 22/60] ImageSamples metric now works with tensors --- .../evaluation/metrics/images_samples.py | 81 ++++++++---- tests/test_metrics.py | 117 ++++++++++++++++++ 2 files changed, 171 insertions(+), 27 deletions(-) diff --git a/avalanche/evaluation/metrics/images_samples.py b/avalanche/evaluation/metrics/images_samples.py index 407a3ef5d..8429ff0e8 100644 --- a/avalanche/evaluation/metrics/images_samples.py +++ b/avalanche/evaluation/metrics/images_samples.py @@ -22,35 +22,40 @@ if TYPE_CHECKING: from avalanche.training.strategies import BaseStrategy + from avalanche.benchmarks.utils import AvalancheDataset class ImagesSamplePlugin(PluginMetric): - """ - A metric used to sample images at random. - No data augmentation is shown. + """Metric used to sample random images. + Only images in strategy.adapted dataset are used. Images added in the dataloader (like the replay plugins do) are missed. + By default data augmentation are removed. :param n_rows: The numbers of raws to use in the grid of images. :param n_cols: The numbers of columns to use in the grid of images. :param group: If True, images will be grouped by (task, label) :param mode: The plugin can be used at train or eval time. + :param disable_augmentations: determines whether to show the augmented + images or the raw images (default: True). :return: The corresponding plugins. """ def __init__( self, *, - mode: Literal["train", "eval"], + mode: Literal["train", "eval", "both"], n_cols: int, n_rows: int, group: bool = True, + disable_augmentations: bool = True ): super().__init__() self.group = group self.n_rows = n_rows self.n_cols = n_cols self.mode = mode + self.disable_augmentations = disable_augmentations self.images: List[Tensor] = [] self.n_wanted_images = self.n_cols * self.n_rows @@ -58,17 +63,26 @@ def __init__( def after_train_dataset_adaptation( self, strategy: "BaseStrategy" ) -> "MetricResult": - if self.mode == "train": - return self.make_grid_sample(strategy) + if self.mode == "train" or self.mode == "both": + return self._make_grid_sample(strategy) def after_eval_dataset_adaptation( self, strategy: "BaseStrategy" ) -> "MetricResult": - if self.mode == "eval": - return self.make_grid_sample(strategy) + if self.mode == "eval" or self.mode == "both": + return self._make_grid_sample(strategy) + + def reset(self) -> None: + self.images = [] + + def result(self) -> List[Tensor]: + return self.images + + def __str__(self): + return "images" - def make_grid_sample(self, strategy: "BaseStrategy") -> "MetricResult": - self.load_sorted_images(strategy) + def _make_grid_sample(self, strategy: "BaseStrategy") -> "MetricResult": + self._load_sorted_images(strategy) return [ MetricValue( @@ -88,16 +102,17 @@ def make_grid_sample(self, strategy: "BaseStrategy") -> "MetricResult": ) ] - def load_sorted_images(self, strategy: "BaseStrategy"): + def _load_sorted_images(self, strategy: "BaseStrategy"): self.reset() - self.images, labels, tasks = self.load_data(strategy) + self.images, labels, tasks = self._load_data(strategy) if self.group: - self.sort_images(labels, tasks) + self._sort_images(labels, tasks) - def load_data( + def _load_data( self, strategy: "BaseStrategy" ) -> Tuple[List[Tensor], List[int], List[int]]: - dataloader = self.make_dataloader(strategy) + dataloader = self._make_dataloader(strategy.adapted_dataset, + strategy.eval_mb_size) images, labels, tasks = [], [], [] @@ -109,7 +124,7 @@ def load_data( if len(images) == self.n_wanted_images: return images, labels, tasks - def sort_images(self, labels: List[int], tasks: List[int]): + def _sort_images(self, labels: List[int], tasks: List[int]): self.images = [ image for task, label, image in sorted( @@ -117,23 +132,35 @@ def sort_images(self, labels: List[int], tasks: List[int]): ) ] - def make_dataloader(self, strategy: "BaseStrategy") -> DataLoader: + def _make_dataloader(self, data: "AvalancheDataset", mb_size: int)\ + -> DataLoader: + if self.disable_augmentations: + data = data.replace_transforms( + transform=MaybeToTensor(), target_transform=None, + ) return DataLoader( - dataset=strategy.adapted_dataset.replace_transforms( - transform=ToTensor(), target_transform=None, - ), - batch_size=min(strategy.eval_mb_size, self.n_wanted_images), + dataset=data, + batch_size=min(mb_size, self.n_wanted_images), shuffle=True, ) - def reset(self) -> None: - self.images = [] - def result(self) -> List[Tensor]: - return self.images +class MaybeToTensor(ToTensor): + """Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor. Pytorch tensors + are left as is. + """ - def __str__(self): - return "images" + def __call__(self, pic): + """ + Args: + pic (PIL Image or numpy.ndarray): Image to be converted to tensor. + + Returns: + Tensor: Converted image. + """ + if isinstance(pic, Tensor): + return pic + return super().__call__(pic) def images_samples_metrics( diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 0e2079a8e..144bae352 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -607,5 +607,122 @@ def test_cm(self): self.assertTrue((el == elref).all()) +import unittest +from unittest.mock import MagicMock + +from torch.utils.data import DataLoader +from torchvision.transforms import Resize, Compose, ToTensor +from torchvision.utils import save_image + +from avalanche.benchmarks import SplitMNIST +from avalanche.benchmarks.utils import AvalancheTensorDataset +from avalanche.evaluation.metrics import ImagesSamplePlugin + + +class ImageSamplesTests(unittest.TestCase): + def test_image_samples(args): + p_metric = ImagesSamplePlugin( + n_cols=5, + n_rows=5, + group=True, + mode="train" + ) + + scenario = SplitMNIST(5) + curr_exp = scenario.train_stream[0] + curr_dataset = curr_exp.dataset + strategy_mock = MagicMock( + eval_mb_size=32, + experience=curr_exp, + adapted_dataset=curr_dataset + ) + + mval = p_metric.after_train_dataset_adaptation(strategy_mock) + img_grid = mval[0].value.image + + # save_image(img_grid, './logs/test_image_grid.png') + print("Done.") + + def test_tensor_samples(args): + p_metric = ImagesSamplePlugin( + n_cols=5, + n_rows=5, + group=True, + mode="train" + ) + + scenario = SplitMNIST(5) + curr_exp = scenario.train_stream[0] + for mb in DataLoader(curr_exp.dataset, batch_size=32): + break + curr_dataset = AvalancheTensorDataset(*mb[:2], targets=mb[1]) + + strategy_mock = MagicMock( + eval_mb_size=32, + experience=curr_exp, + adapted_dataset=curr_dataset + ) + + mval = p_metric.after_train_dataset_adaptation(strategy_mock) + img_grid = mval[0].value.image + + save_image(img_grid, './logs/test_tensor_grid.png') + print("Done.") + + def test_samples_augmentations(args): + scenario = SplitMNIST(5) + curr_exp = scenario.train_stream[0] + + # we use a ReSize transform because it's easy to detect if it's been + # applied without looking at the image. + curr_dataset = curr_exp.dataset.replace_transforms( + transform=Compose([Resize(8), ToTensor()]), target_transform=None + ) + + ########################################## + # WITH AUGMENTATIONS + ########################################## + p_metric = ImagesSamplePlugin( + n_cols=5, + n_rows=5, + group=True, + mode="train", + disable_augmentations=False + ) + + strategy_mock = MagicMock( + eval_mb_size=32, + experience=curr_exp, + adapted_dataset=curr_dataset + ) + + mval = p_metric.after_train_dataset_adaptation(strategy_mock) + img_grid = mval[0].value.image + assert img_grid.shape == (3, 52, 52) + save_image(img_grid, './logs/test_image_with_aug.png') + + ########################################## + # WITHOUT AUGMENTATIONS + ########################################## + p_metric = ImagesSamplePlugin( + n_cols=5, + n_rows=5, + group=True, + mode="train", + disable_augmentations=True + ) + + strategy_mock = MagicMock( + eval_mb_size=32, + experience=curr_exp, + adapted_dataset=curr_dataset + ) + + mval = p_metric.after_train_dataset_adaptation(strategy_mock) + img_grid = mval[0].value.image + assert img_grid.shape == (3, 152, 152) + save_image(img_grid, './logs/test_image_with_aug.png') + + if __name__ == '__main__': unittest.main() From ac97308171749cab80ac72bcb0fb67d7554457cc Mon Sep 17 00:00:00 2001 From: Vincenzo Lomonaco Date: Mon, 29 Nov 2021 13:44:09 +0000 Subject: [PATCH 23/60] GitBook: [#123] how-to added --- docs/gitbook/SUMMARY.md | 28 ++++++++++--------- .../how-tos/avalanchedataset/README.md | 2 ++ .../advanced-transformations.md | 2 ++ .../creating-avalanchedatasets.md | 2 ++ 4 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 docs/gitbook/how-tos/avalanchedataset/README.md create mode 100644 docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md create mode 100644 docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md diff --git a/docs/gitbook/SUMMARY.md b/docs/gitbook/SUMMARY.md index 33826d7aa..e2d68c074 100644 --- a/docs/gitbook/SUMMARY.md +++ b/docs/gitbook/SUMMARY.md @@ -19,19 +19,22 @@ ## 📙 From Zero to Hero Tutorial -* [Introduction](from-zero-to-hero-tutorial/01_introduction.md) -* [Models](from-zero-to-hero-tutorial/02_models.md) -* [Benchmarks](from-zero-to-hero-tutorial/03_benchmarks.md) -* [Training](from-zero-to-hero-tutorial/04_training.md) -* [Evaluation](from-zero-to-hero-tutorial/05_evaluation.md) -* [Loggers](from-zero-to-hero-tutorial/06_loggers.md) -* [Putting All Together](from-zero-to-hero-tutorial/07_putting-all-together.md) -* [Extending Avalanche](from-zero-to-hero-tutorial/08_extending-avalanche.md) -* [Contribute to Avalanche](from-zero-to-hero-tutorial/09_contribute-to-avalanche.md) +* [Introduction](from-zero-to-hero-tutorial/01\_introduction.md) +* [Models](from-zero-to-hero-tutorial/02\_models.md) +* [Benchmarks](from-zero-to-hero-tutorial/03\_benchmarks.md) +* [Training](from-zero-to-hero-tutorial/04\_training.md) +* [Evaluation](from-zero-to-hero-tutorial/05\_evaluation.md) +* [Loggers](from-zero-to-hero-tutorial/06\_loggers.md) +* [Putting All Together](from-zero-to-hero-tutorial/07\_putting-all-together.md) +* [Extending Avalanche](from-zero-to-hero-tutorial/08\_extending-avalanche.md) +* [Contribute to Avalanche](from-zero-to-hero-tutorial/09\_contribute-to-avalanche.md) ## How-Tos -* [Dataloaders, Buffers, and Replay](how-tos/dataloading_buffers_replay.md) +* [AvalancheDataset](how-tos/avalanchedataset/README.md) + * [Creating AvalancheDatasets](how-tos/avalanchedataset/creating-avalanchedatasets.md) + * [Advanced Transformations](how-tos/avalanchedataset/advanced-transformations.md) +* [Dataloaders, Buffers, and Replay](how-tos/dataloading\_buffers\_replay.md) ## 💻 Code Documentation @@ -48,11 +51,10 @@ * [Request a Feature](questions-and-issues/request-a-feature.md) * [Give Feedback](questions-and-issues/give-feedback.md) -## 👪 About Us +## 👪 About Us * [The People](contacts-and-links/the-team.md) * [Join Us!](contacts-and-links/join-us.md) -* [Slack](https://join.slack.com/t/continualai/shared_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU) +* [Slack](https://join.slack.com/t/continualai/shared\_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU) * [Email](mailto:contact@continualai.org) * [Twitter](https://twitter.com/AvalancheLib) - diff --git a/docs/gitbook/how-tos/avalanchedataset/README.md b/docs/gitbook/how-tos/avalanchedataset/README.md new file mode 100644 index 000000000..144d54f17 --- /dev/null +++ b/docs/gitbook/how-tos/avalanchedataset/README.md @@ -0,0 +1,2 @@ +# AvalancheDataset + diff --git a/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md b/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md new file mode 100644 index 000000000..0a1d503fb --- /dev/null +++ b/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md @@ -0,0 +1,2 @@ +# Advanced Transformations + diff --git a/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md b/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md new file mode 100644 index 000000000..511949e26 --- /dev/null +++ b/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md @@ -0,0 +1,2 @@ +# Creating AvalancheDatasets + From e03b39933b5ac6675e3564aaf491bf51e182a382 Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Mon, 29 Nov 2021 17:13:34 +0100 Subject: [PATCH 24/60] REFACTOR tests --- tests/evaluation/__init__.py | 0 tests/evaluation/test_image_samples.py | 113 ++++++++++++++++++++++++ tests/test_metrics.py | 117 ------------------------- 3 files changed, 113 insertions(+), 117 deletions(-) create mode 100644 tests/evaluation/__init__.py create mode 100644 tests/evaluation/test_image_samples.py diff --git a/tests/evaluation/__init__.py b/tests/evaluation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/evaluation/test_image_samples.py b/tests/evaluation/test_image_samples.py new file mode 100644 index 000000000..626b58bf9 --- /dev/null +++ b/tests/evaluation/test_image_samples.py @@ -0,0 +1,113 @@ +import unittest +from unittest.mock import MagicMock + +from torch.utils.data import DataLoader +from torchvision.transforms import Resize, Compose, ToTensor +from torchvision.utils import save_image + +from avalanche.benchmarks import SplitMNIST +from avalanche.benchmarks.utils import AvalancheTensorDataset +from avalanche.evaluation.metrics import ImagesSamplePlugin + + +class ImageSamplesTests(unittest.TestCase): + def test_image_samples(args): + p_metric = ImagesSamplePlugin( + n_cols=5, + n_rows=5, + group=True, + mode="train" + ) + + scenario = SplitMNIST(5) + curr_exp = scenario.train_stream[0] + curr_dataset = curr_exp.dataset + strategy_mock = MagicMock( + eval_mb_size=32, + experience=curr_exp, + adapted_dataset=curr_dataset + ) + + mval = p_metric.after_train_dataset_adaptation(strategy_mock) + img_grid = mval[0].value.image + + # save_image(img_grid, './logs/test_image_grid.png') + + def test_tensor_samples(args): + p_metric = ImagesSamplePlugin( + n_cols=5, + n_rows=5, + group=True, + mode="train" + ) + + scenario = SplitMNIST(5) + curr_exp = scenario.train_stream[0] + for mb in DataLoader(curr_exp.dataset, batch_size=32): + break + curr_dataset = AvalancheTensorDataset(*mb[:2], targets=mb[1]) + + strategy_mock = MagicMock( + eval_mb_size=32, + experience=curr_exp, + adapted_dataset=curr_dataset + ) + + mval = p_metric.after_train_dataset_adaptation(strategy_mock) + img_grid = mval[0].value.image + + # save_image(img_grid, './logs/test_tensor_grid.png') + + def test_samples_augmentations(args): + scenario = SplitMNIST(5) + curr_exp = scenario.train_stream[0] + + # we use a ReSize transform because it's easy to detect if it's been + # applied without looking at the image. + curr_dataset = curr_exp.dataset.replace_transforms( + transform=Compose([Resize(8), ToTensor()]), target_transform=None + ) + + ########################################## + # WITH AUGMENTATIONS + ########################################## + p_metric = ImagesSamplePlugin( + n_cols=5, + n_rows=5, + group=True, + mode="train", + disable_augmentations=False + ) + + strategy_mock = MagicMock( + eval_mb_size=32, + experience=curr_exp, + adapted_dataset=curr_dataset + ) + + mval = p_metric.after_train_dataset_adaptation(strategy_mock) + img_grid = mval[0].value.image + assert img_grid.shape == (3, 52, 52) + # save_image(img_grid, './logs/test_image_with_aug.png') + + ########################################## + # WITHOUT AUGMENTATIONS + ########################################## + p_metric = ImagesSamplePlugin( + n_cols=5, + n_rows=5, + group=True, + mode="train", + disable_augmentations=True + ) + + strategy_mock = MagicMock( + eval_mb_size=32, + experience=curr_exp, + adapted_dataset=curr_dataset + ) + + mval = p_metric.after_train_dataset_adaptation(strategy_mock) + img_grid = mval[0].value.image + assert img_grid.shape == (3, 152, 152) + # save_image(img_grid, './logs/test_image_with_aug.png') diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 144bae352..0e2079a8e 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -607,122 +607,5 @@ def test_cm(self): self.assertTrue((el == elref).all()) -import unittest -from unittest.mock import MagicMock - -from torch.utils.data import DataLoader -from torchvision.transforms import Resize, Compose, ToTensor -from torchvision.utils import save_image - -from avalanche.benchmarks import SplitMNIST -from avalanche.benchmarks.utils import AvalancheTensorDataset -from avalanche.evaluation.metrics import ImagesSamplePlugin - - -class ImageSamplesTests(unittest.TestCase): - def test_image_samples(args): - p_metric = ImagesSamplePlugin( - n_cols=5, - n_rows=5, - group=True, - mode="train" - ) - - scenario = SplitMNIST(5) - curr_exp = scenario.train_stream[0] - curr_dataset = curr_exp.dataset - strategy_mock = MagicMock( - eval_mb_size=32, - experience=curr_exp, - adapted_dataset=curr_dataset - ) - - mval = p_metric.after_train_dataset_adaptation(strategy_mock) - img_grid = mval[0].value.image - - # save_image(img_grid, './logs/test_image_grid.png') - print("Done.") - - def test_tensor_samples(args): - p_metric = ImagesSamplePlugin( - n_cols=5, - n_rows=5, - group=True, - mode="train" - ) - - scenario = SplitMNIST(5) - curr_exp = scenario.train_stream[0] - for mb in DataLoader(curr_exp.dataset, batch_size=32): - break - curr_dataset = AvalancheTensorDataset(*mb[:2], targets=mb[1]) - - strategy_mock = MagicMock( - eval_mb_size=32, - experience=curr_exp, - adapted_dataset=curr_dataset - ) - - mval = p_metric.after_train_dataset_adaptation(strategy_mock) - img_grid = mval[0].value.image - - save_image(img_grid, './logs/test_tensor_grid.png') - print("Done.") - - def test_samples_augmentations(args): - scenario = SplitMNIST(5) - curr_exp = scenario.train_stream[0] - - # we use a ReSize transform because it's easy to detect if it's been - # applied without looking at the image. - curr_dataset = curr_exp.dataset.replace_transforms( - transform=Compose([Resize(8), ToTensor()]), target_transform=None - ) - - ########################################## - # WITH AUGMENTATIONS - ########################################## - p_metric = ImagesSamplePlugin( - n_cols=5, - n_rows=5, - group=True, - mode="train", - disable_augmentations=False - ) - - strategy_mock = MagicMock( - eval_mb_size=32, - experience=curr_exp, - adapted_dataset=curr_dataset - ) - - mval = p_metric.after_train_dataset_adaptation(strategy_mock) - img_grid = mval[0].value.image - assert img_grid.shape == (3, 52, 52) - save_image(img_grid, './logs/test_image_with_aug.png') - - ########################################## - # WITHOUT AUGMENTATIONS - ########################################## - p_metric = ImagesSamplePlugin( - n_cols=5, - n_rows=5, - group=True, - mode="train", - disable_augmentations=True - ) - - strategy_mock = MagicMock( - eval_mb_size=32, - experience=curr_exp, - adapted_dataset=curr_dataset - ) - - mval = p_metric.after_train_dataset_adaptation(strategy_mock) - img_grid = mval[0].value.image - assert img_grid.shape == (3, 152, 152) - save_image(img_grid, './logs/test_image_with_aug.png') - - if __name__ == '__main__': unittest.main() From 6d60a28e6dc23a7fd5f77a12bc117a02279d2a0d Mon Sep 17 00:00:00 2001 From: Vincenzo Lomonaco Date: Mon, 29 Nov 2021 17:38:01 +0000 Subject: [PATCH 25/60] GitBook: [#125] Adding preamble page to AvalancheDataset How-To --- docs/gitbook/SUMMARY.md | 3 ++- .../how-tos/avalanchedataset/preamble-pytorch-datasets.md | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md diff --git a/docs/gitbook/SUMMARY.md b/docs/gitbook/SUMMARY.md index e2d68c074..271b5e182 100644 --- a/docs/gitbook/SUMMARY.md +++ b/docs/gitbook/SUMMARY.md @@ -32,6 +32,7 @@ ## How-Tos * [AvalancheDataset](how-tos/avalanchedataset/README.md) + * [Preamble: PyTorch Datasets](how-tos/avalanchedataset/preamble-pytorch-datasets.md) * [Creating AvalancheDatasets](how-tos/avalanchedataset/creating-avalanchedatasets.md) * [Advanced Transformations](how-tos/avalanchedataset/advanced-transformations.md) * [Dataloaders, Buffers, and Replay](how-tos/dataloading\_buffers\_replay.md) @@ -51,7 +52,7 @@ * [Request a Feature](questions-and-issues/request-a-feature.md) * [Give Feedback](questions-and-issues/give-feedback.md) -## 👪 About Us +## 👪 About Us * [The People](contacts-and-links/the-team.md) * [Join Us!](contacts-and-links/join-us.md) diff --git a/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md b/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md new file mode 100644 index 000000000..4293317f8 --- /dev/null +++ b/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md @@ -0,0 +1,2 @@ +# Preamble: PyTorch Datasets + From b6aaf1d47511a25c83ee2318f04ac1a99e39b308 Mon Sep 17 00:00:00 2001 From: Lorenzo Pellegrini Date: Mon, 29 Nov 2021 19:17:38 +0100 Subject: [PATCH 26/60] Adapted AvalancheDataset How-To notebooks to the GitBook structure. --- .../how-tos/avalanchedataset/README.ipynb | 52 ++ .../advanced-transformations.ipynb | 623 ++++++++++++++++++ .../creating-avalanchedatasets.ipynb | 440 +++++++++++++ .../preamble-pytorch-datasets.ipynb | 146 ++++ 4 files changed, 1261 insertions(+) create mode 100644 notebooks/how-tos/avalanchedataset/README.ipynb create mode 100644 notebooks/how-tos/avalanchedataset/advanced-transformations.ipynb create mode 100644 notebooks/how-tos/avalanchedataset/creating-avalanchedatasets.ipynb create mode 100644 notebooks/how-tos/avalanchedataset/preamble-pytorch-datasets.ipynb diff --git a/notebooks/how-tos/avalanchedataset/README.ipynb b/notebooks/how-tos/avalanchedataset/README.ipynb new file mode 100644 index 000000000..35d3fa571 --- /dev/null +++ b/notebooks/how-tos/avalanchedataset/README.ipynb @@ -0,0 +1,52 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d369a312", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "tags": [] + }, + "source": [ + "---\n", + "description: Dealing with AvalancheDatasets\n", + "---\n", + "\n", + "# AvalancheDataset\n", + "\n", + "The `AvalancheDataset` is an implementation of the PyTorch `Dataset` class that comes with many useful out-of-the-box functionalities. For most users, the *AvalancheDataset* can be used as a plain PyTorch Dataset that will return `x, y, t` elements. However, the AvalancheDataset is much more powerful than a simple PyTorch Dataset. \n", + "\n", + "**A serie of _Mini How-Tos_** will guide you through the functionalities of the *AvalancheDataset* and its subclasses:\n", + "\n", + "- [Preamble: PyTorch Datasets](https://avalanche.continualai.org/how-tos/avalanchedataset/preamble-pytorch-datasets)\n", + "- [Creating AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanchedataset/creating-avalanchedatasets)\n", + "- [Advanced Transformations](https://avalanche.continualai.org/how-tos/avalanchedataset/advanced-transformations)\n", + "\n", + "Brefore jumping to the actual *Mini How-To*s, **we recommend having a look at the basic notions of Dataset and DataLoader by reading the [Preamble page](https://avalanche.continualai.org/how-tos/avalanchedataset/preamble-pytorch-datasets)**." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/how-tos/avalanchedataset/advanced-transformations.ipynb b/notebooks/how-tos/avalanchedataset/advanced-transformations.ipynb new file mode 100644 index 000000000..f7eae1cdb --- /dev/null +++ b/notebooks/how-tos/avalanchedataset/advanced-transformations.ipynb @@ -0,0 +1,623 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "53277e45-b7d7-4857-a79d-d7218bd6b6a9", + "metadata": {}, + "source": [ + "---\n", + "description: Dealing with transformations (groups, appending, replacing, freezing).\n", + "---\n", + "\n", + "# Advanced Transformations\n", + "AvalancheDataset (and its subclasses like the Avalanche*Tensor/Subset/Concat*Dataset) allow for a finer control over transformations. While torchvision (and other) datasets allow for a minimal mechanism to apply transformations, with AvalancheDataset one can:\n", + "1. Have multiple **transformation \"groups\"** in the same dataset (like separated train and test transformations).\n", + "2. **Append, replace and remove transformations**, even by using nested Subset/Concat Datasets.\n", + "3. **Freeze transformations**, so that they can't be changed.\n", + "\n", + "The following sub-sections show examples on how to use these features. Please note that all the constructor parameters and the methods described in this How-To can be used on AvalancheDataset subclasses as well. For more info on all the available subclasses, refer to [this Mini How-To](https://avalanche.continualai.org/how-tos/avalanchedataset/creating-avalanchedatasets).\n", + "\n", + "It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page).\n", + "\n", + "Let's start by installing Avalanche:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "470e6f0b-ecc2-45c3-af53-112da5d7c37e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/lorenzo/Desktop/github_repos/avalanche/notebooks\n" + ] + } + ], + "source": [ + "!pip install git+https://github.com/ContinualAI/avalanche.git\n", + "\n", + "# Or, if you cloned Avalanche on your PC:\n", + "# import sys\n", + "# from pathlib import Path\n", + "# sys.path.append(str(Path.cwd() / '../../..'))" + ] + }, + { + "cell_type": "markdown", + "id": "e770eef5-86d4-4601-b26f-ea12013515df", + "metadata": {}, + "source": [ + "## Transformation groups\n", + "AvalancheDatasets can contain multiple **transformation groups**. This can be useful to keep train and test transformations in the same dataset and to have different set of transformations. This may come in handy in many situations (for instance, to apply ad-hoc transformations to replay data).\n", + "\n", + "As in torchvision datasets, AvalancheDataset supports the two kind of transformations: the `transform`, which is applied to X values, and the `target_transform`, which is applied to Y values. The latter is rarely used. This means that **a transformation group is a pair of transformations to be applied to the X and Y values** of each instance returned by the dataset. In both torchvision and Avalanche implementations, **a transformation must be a function (or other callable object)** that accepts one input (the X or Y value) and outputs its transformed version. This pair of functions is stored in the `transform` and `target_transform` fields of the dataset. A comprehensive guide on transformations can be found in the [torchvision documentation](https://pytorch.org/vision/stable/transforms.html).\n", + "\n", + "In the following example, a MNIST dataset is created and then wrapped in an AvalancheDataset. When creating the AvalancheDataset, we can set *train* and *eval* transformations by passing a *transform\\_groups* parameter. Train transformations usually include some form of random augmentation, while eval transformations usually include a sequence of deterministic transformations only. Here we define the sequence of train transformations as a random rotation followed by the ToTensor operation. The eval transformations only include the ToTensor operation." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "482132af-cfa8-4c16-ae27-5e7a67c75a0c", + "metadata": {}, + "outputs": [], + "source": [ + "from torchvision import transforms\n", + "from torchvision.datasets import MNIST\n", + "from avalanche.benchmarks.utils import AvalancheDataset\n", + "\n", + "mnist_dataset = MNIST('mnist_data', download=True)\n", + "\n", + "# Define the training transformation for X values\n", + "train_transformation = transforms.Compose([\n", + " transforms.RandomRotation(45),\n", + " transforms.ToTensor(),\n", + "])\n", + "# Define the training transformation for Y values (rarely used)\n", + "train_target_transformation = None\n", + "\n", + "# Define the test transformation for X values\n", + "eval_transformation = transforms.ToTensor()\n", + "# Define the test transformation for Y values (rarely used)\n", + "eval_target_transformation = None\n", + "\n", + "transform_groups = {\n", + " 'train': (train_transformation, train_target_transformation),\n", + " 'eval': (eval_transformation, eval_target_transformation)\n", + "}\n", + "\n", + "avl_mnist_transform = AvalancheDataset(mnist_dataset, transform_groups=transform_groups)" + ] + }, + { + "cell_type": "markdown", + "id": "d5f7cca4-e2e7-456b-b6da-54c1c8579eb1", + "metadata": {}, + "source": [ + "Of course, one can also just use the `transform` and `target_transform` constructor parameters to set the transformations for both the *train* and the *eval* groups. However, it is recommended to use the approach based on *transform\\_groups* (shown in the code above) as it is much more flexible." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "51fbd4bf-9535-446a-bc95-6d1cd0dd96f9", + "metadata": {}, + "outputs": [], + "source": [ + "# Not recommended: use transform_groups instead\n", + "avl_mnist_same_transforms = AvalancheDataset(mnist_dataset, transform=train_transformation)" + ] + }, + { + "cell_type": "markdown", + "id": "fbdfa287-347e-4847-95f8-c5a00a4a2107", + "metadata": {}, + "source": [ + "### Using `.train()` and `.eval()`\n", + "\n", + "**The default behaviour of the AvalancheDataset is to use transformations from the _train_ group.** However, one can easily obtain a version of the dataset where the *eval* group is used. Note: when obtaining the dataset of experiences from the test stream, those datasets will already be using the *eval* group of transformations so you don't need to switch to the eval group ;).\n", + "\n", + "As noted before, transformations for the current group are loaded in the `transform` and `target_transform` fields. These fields can be changed directly, but this is *NOT* recommended, as this will not create a copy of the dataset and may probably affect other parts of the code in which the dataset is used.\n", + "\n", + "The recommended way to switch between the *train* and *eval* groups is to use the `.train()` and `.eval()` methods to obtain a copy (view) of the dataset with the proper transformations enabled. This is another very handy feature of the AvalancheDataset: **methods that manipulate the AvalancheDataset fields (and transformations) always create a view of the dataset. The original dataset is never changed.**\n", + "\n", + "In the following cell we use the *avl\\_mnist\\_transform* dataset created in the cells above. We first obtain a view of it in which *eval* transformations are enabled. Then, starting from this view, we obtain a version of it in which *train* transformations are enabled. We want to double-stress that `.train()` and `.eval()` never change the group of the dataset on which they are called: they always create a view.\n", + "\n", + "One can check that the correct transformation group is in use by looking at the content of the *transform/target_transform* fields." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6f8500fa-0f7f-48f7-a26c-d77a1588a244", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original dataset transformation: Compose(\n", + " RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", + " ToTensor()\n", + ")\n", + "--------------------------------\n", + "Eval version of the dataset: ToTensor()\n", + "--------------------------------\n", + "Back to train transformations: Compose(\n", + " RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", + " ToTensor()\n", + ")\n" + ] + } + ], + "source": [ + "# Obtain a view of the dataset in which eval transformations are enabled\n", + "avl_mnist_eval = avl_mnist_transform.eval()\n", + "\n", + "# Obtain a view of the dataset in which we get back to train transforms\n", + "# Basically, avl_mnist_transform ~= avl_mnist_train\n", + "avl_mnist_train = avl_mnist_eval.train()\n", + "\n", + "# Check the current transformations function for the 3 datasets\n", + "print('Original dataset transformation:', avl_mnist_transform.transform)\n", + "# Output:\n", + "# Original dataset transformation: Compose(\n", + "# RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", + "# ToTensor()\n", + "# )\n", + "print('--------------------------------')\n", + "print('Eval version of the dataset:', avl_mnist_eval.transform)\n", + "# Output: \"Eval version of the dataset: ToTensor()\"\n", + "print('--------------------------------')\n", + "print('Back to train transformations:', avl_mnist_train.transform)\n", + "# Output:\n", + "# Back to train transformations: Compose(\n", + "# RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", + "# ToTensor()\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "id": "8172b379-d60a-43ba-8e0d-c4fcdc0e997e", + "metadata": {}, + "source": [ + "### Custom transformation groups\n", + "In *AvalancheDataset*s the **_train_ and _eval_ transformation groups are always available**. However, *AvalancheDataset* also supports **custom transformation groups**.\n", + "\n", + "The following example shows how to create an AvalancheDataset with an additional group named *replay*. We define the replay transformation as a random crop followed by the ToTensor operation." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "69c8912d-b826-4265-ba71-c33501a1f956", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "replay_transform = transforms.Compose([\n", + " transforms.RandomCrop(28, padding=4),\n", + " transforms.ToTensor()\n", + "])\n", + "\n", + "replay_target_transform = None\n", + "\n", + "transform_groups_with_replay = {\n", + " 'train': (None, None),\n", + " 'eval': (None, None),\n", + " 'replay': (replay_transform, replay_target_transform)\n", + "}\n", + "\n", + "AvalancheDataset(mnist_dataset, transform_groups=transform_groups_with_replay)" + ] + }, + { + "cell_type": "markdown", + "id": "6bc0508a-bc4d-4896-984c-609c5803f9e6", + "metadata": {}, + "source": [ + "However, once created the dataset will use the *train* group. There are two ways to **switch to our custom group**:\n", + "- Set the group when creating the dataset using the `initial_transform_group` constructor parameter\n", + "- Switch to the group using the `.with_transforms(group_name)` method\n", + "\n", + "The `.with_transforms(group_name)` method behaves in the same way `.train()` and `.eval()` do by creating a view of the original dataset.\n", + "\n", + "The following example shows how to use both methods:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2fd29287-da97-4aad-ab3c-cfd514629ad8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Compose(\n", + " RandomCrop(size=(28, 28), padding=4)\n", + " ToTensor()\n", + ")\n", + "Compose(\n", + " RandomCrop(size=(28, 28), padding=4)\n", + " ToTensor()\n", + ")\n" + ] + } + ], + "source": [ + "# Method 1: create the dataset with \"replay\" as the default group\n", + "avl_mnist_custom_transform_1 = AvalancheDataset(\n", + " mnist_dataset,\n", + " transform_groups=transform_groups_with_replay,\n", + " initial_transform_group='replay')\n", + "\n", + "print(avl_mnist_custom_transform_1.transform)\n", + "\n", + "# Method 2: switch to \"replay\" using `.with_transforms(group_name)`\n", + "avl_mnist_custom_transform_not_enabled = AvalancheDataset(\n", + " mnist_dataset,\n", + " transform_groups=transform_groups_with_replay)\n", + "\n", + "avl_mnist_custom_transform_2 = avl_mnist_custom_transform_not_enabled.with_transforms('replay')\n", + "print(avl_mnist_custom_transform_2.transform)\n", + "\n", + "# Both prints output:\n", + "# Compose(\n", + "# RandomCrop(size=(28, 28), padding=4)\n", + "# ToTensor()\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "id": "08fc9f3f-f95a-49ed-ba94-1bbec8960235", + "metadata": {}, + "source": [ + "## Appending transformations\n", + "\n", + "In the standard torchvision datasets the only way to append (that is, add a new transformation step to the list of existing one) is to change the *transform* field directly by doing something like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e4d02b6e-0e73-4205-a497-a1540ff03185", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Compose(\n", + " ToTensor()\n", + " RandomCrop(size=(28, 28), padding=4)\n", + ")\n" + ] + } + ], + "source": [ + "# Append a transform by using torchvision datasets (>>> DON'T DO THIS! <<<)\n", + "\n", + "# Create the dataset\n", + "mnist_dataset_w_totensor = MNIST('mnist_data', download=True, transform=transforms.ToTensor())\n", + "\n", + "# Append a transform\n", + "to_append_transform = transforms.RandomCrop(size=(28, 28), padding=4)\n", + "mnist_dataset_w_totensor.transform = transforms.Compose(\n", + " [mnist_dataset_w_totensor.transform, to_append_transform]\n", + ")\n", + "print(mnist_dataset_w_totensor.transform)\n", + "# Prints:\n", + "# Compose(\n", + "# ToTensor()\n", + "# RandomCrop(size=(28, 28), padding=4)\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "id": "55cb1749-2f72-4780-9699-99b97544f4da", + "metadata": {}, + "source": [ + "This solution has many huge drawbacks:\n", + "- The transformation field of the dataset is changed directly. This will affect other parts of the code that use that dataset instance.\n", + "- If the initial transform is `None`, then `Compose` will not complain, but the process will crash later (try it by yourself: replace the first element of Compose in cell above with `None`, then try obtaining a data point from the dataset).\n", + "- If you need to change transformations only temporarly to do some specific things in a limited part of the code, then you need to store the previous set of transformations in some variable in order to switch back to them later.\n", + "\n", + "AvalancheDataset offers a very simple method to append transformations without incurring in those issues. The `.add_transforms(transform=None, target_transform=None)` method will append the given transform(s) **to the currently enabled transform group** and will return a new (a view actually) dataset with given transformations appended to the existing ones. The original dataset is not affected. One can also use `.add_transforms_to_group(group_name, transform, target_transform)` to change transformations for a different group.\n", + "\n", + "The next cell shows how to use `.add_transforms(...)` to append the *to\\_append\\_transform* transform defined in the cell above." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "99a295d6-7a3f-4e8a-89af-9f97915848c2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "With appended transforms: Compose(\n", + " ToTensor()\n", + " RandomCrop(size=(28, 28), padding=4)\n", + ")\n", + "Original dataset: ToTensor()\n" + ] + } + ], + "source": [ + "# Create the dataset\n", + "avl_mnist = AvalancheDataset(MNIST('mnist_data', download=True), transform=transforms.ToTensor())\n", + "\n", + "# Append a transformation. Simple as:\n", + "avl_mnist_appended_transform = avl_mnist.add_transforms(to_append_transform)\n", + "\n", + "print('With appended transforms:', avl_mnist_appended_transform.transform)\n", + "# Prints:\n", + "# With appended transforms: Compose(\n", + "# ToTensor()\n", + "# RandomCrop(size=(28, 28), padding=4)\n", + "# )\n", + "\n", + "# Check that the original dataset was not affected:\n", + "print('Original dataset:', avl_mnist.transform)\n", + "# Prints: \"Original dataset: ToTensor()\"" + ] + }, + { + "cell_type": "markdown", + "id": "19f6a174-4073-4b24-a604-95df9ff5a0f3", + "metadata": {}, + "source": [ + "Note that by using `.add_transforms(...)`:\n", + "\n", + "- The original dataset is not changed, which means that other parts of the code that use that dataset instance are not affected.\n", + "- You don't need to worry about *None* transformations.\n", + "- In order to revert to the original transformations you don't need to keep a copy of them: the original dataset is not affected!" + ] + }, + { + "cell_type": "markdown", + "id": "c5ee8e22-982d-4fa1-ab2a-97e47bb1d693", + "metadata": {}, + "source": [ + "## Replacing transformations\n", + "\n", + "The replacement operation follows the same idea (and benefits) of the append one. By using `.replace_transforms(transform, target_transform)` one can obtain a view of the original dataset in which the **transformaations for the current group** are replaced with the given ones. One may also change tranformations for other groups by passing the name of the group as the optional parameter `group`. As with any transform-related operation, the original dataset is not affected. \n", + "\n", + "Note: one can use `.replace_transforms(...)` to remove previous transformations (by passing `None` as the new transform).\n", + "\n", + "The following cell shows how to use `.replace_transforms(...)` to replace the transformations of the current group:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "0e9f9c46-9d3d-40eb-b7b8-afad15668bc9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "With replaced transform: RandomCrop(size=(28, 28), padding=4)\n", + "Original dataset: ToTensor()\n" + ] + } + ], + "source": [ + "new_transform = transforms.RandomCrop(size=(28, 28), padding=4)\n", + "\n", + "# Append a transformation. Simple as:\n", + "avl_mnist_replaced_transform = avl_mnist.replace_transforms(new_transform, None)\n", + "\n", + "print('With replaced transform:', avl_mnist_replaced_transform.transform)\n", + "# Prints: \"With replaces transforms: RandomCrop(size=(28, 28), padding=4)\"\n", + "\n", + "# Check that the original dataset was not affected:\n", + "print('Original dataset:', avl_mnist.transform)\n", + "# Prints: \"Original dataset: ToTensor()\"" + ] + }, + { + "cell_type": "markdown", + "id": "bc2e4781-0f2f-4eb6-b61e-affc8aaf255c", + "metadata": {}, + "source": [ + "## Freezing transformations\n", + "\n", + "One last functionality regarding transformations is the ability to \"freeze\" transformations. Freezing transformations menas **permanently glueing transformations to the dataset so that they can't be replaced or changed in any way** (usually by mistake). Frozen transformations cannot be changed by using `.replace_transforms(...)` or even by changing the `transform` field directly.\n", + "\n", + "One may wonder when this may come in handy... in fact, you will probably rarely need to freeze transformations. However, imagine having to instantiate the PermutedMNIST benchmark. You want the permutation transformation to not be changed by mistake. However, the end users do not know how the internal implementations of the benchmark works, so they may end up messing with those transformations. By freezing the permutation transformation, users cannot mess with it.\n", + "\n", + "Transformations for all transform groups can be frozen at once by using `.freeze_transforms()`. Transformations can be frozen for a single group by using `.freeze_group_transforms(group_name)`. As always, those methods return a view of the original dataset.\n", + "\n", + "The cell below shows a simplified excerpt from the [PermutedMNIST benchmark implementation](https://github.com/ContinualAI/avalanche/blob/master/avalanche/benchmarks/classic/cmnist.py). First, a *PixelsPermutation* instance is created. That instance is a transformation that will permute the pixels of the input image. We then create the train end test sets. Once created, transformations for those datasets are frozen using `.freeze_transforms()`." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "af97e04b-01b4-4a32-ba1f-42ed94977c56", + "metadata": {}, + "outputs": [], + "source": [ + "from avalanche.benchmarks.classic.cmnist import PixelsPermutation\n", + "import numpy as np\n", + "import torch\n", + "\n", + "# Instantiate MNIST train and test sets\n", + "mnist_train = MNIST('mnist_data', train=True, download=True)\n", + "mnist_test = MNIST('mnist_data', train=False, download=True)\n", + " \n", + "# Define the transformation used to permute the pixels\n", + "rng_seed = 4321\n", + "rng_permute = np.random.RandomState(rng_seed)\n", + "idx_permute = torch.from_numpy(rng_permute.permutation(784)).type(torch.int64)\n", + "permutation_transform = PixelsPermutation(idx_permute)\n", + "\n", + "# Define the transforms group\n", + "perm_group_transforms = dict(\n", + " train=(permutation_transform, None),\n", + " eval=(permutation_transform, None)\n", + ")\n", + "\n", + "# Create the datasets and freeze transforms\n", + "# Note: one can call \"freeze_transforms\" on constructor result\n", + "# or you can do this in 2 steps. The result is the same (obviously).\n", + "# The next part show both ways:\n", + "\n", + "# Train set\n", + "permuted_train_set = AvalancheDataset(\n", + " mnist_train, \n", + " transform_groups=perm_group_transforms).freeze_transforms()\n", + "\n", + "# Test set\n", + "permuted_test_set = AvalancheDataset(\n", + " mnist_test, transform_groups=perm_group_transforms, \n", + " initial_transform_group='eval')\n", + "permuted_test_set = permuted_test_set.freeze_transforms()" + ] + }, + { + "cell_type": "markdown", + "id": "3f5fea92-cb21-4144-bee8-6e2b37606d25", + "metadata": {}, + "source": [ + "In this way, that transform can't be removed. However, remember that one can always append other transforms atop of frozen transforms.\n", + "\n", + "The cell below shows that `replace_transforms` can't remove frozen transformations:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "62ae4c3e-da05-494c-9df4-394774a42908", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before replace_transforms:\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "After replace_transforms:\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# First, show that the image pixels are permuted\n", + "print('Before replace_transforms:')\n", + "display(permuted_train_set[0][0].resize((192, 192), 0))\n", + "\n", + "# Try to remove the permutation\n", + "with_removed_transforms = permuted_train_set.replace_transforms(None, None)\n", + "\n", + "print('After replace_transforms:')\n", + "display(permuted_train_set[0][0].resize((192, 192), 0))\n", + "display(with_removed_transforms[0][0].resize((192, 192), 0))" + ] + }, + { + "cell_type": "markdown", + "id": "553d4633-c7aa-46c4-b2bb-90267b998e74", + "metadata": {}, + "source": [ + "## Transformations wrap-up\n", + "This completes the *Mini How-To* for the functionalities of the *AvalancheDataset* related to **transformations**. \n", + "\n", + "Here you learned how to use **transformation groups** and how to **append/replace/freeze transformations** in a simple way.\n", + "\n", + "Other *Mini How-To*s will guide you through the other functionalities offered by the *AvalancheDataset* class. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanchedataset)." + ] + }, + { + "cell_type": "markdown", + "id": "792a197d-a0c3-44c2-87fb-fae43f687195", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 🤝 Run it on Google Colab\n", + "\n", + "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanchedataset/advanced-transformations.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/how-tos/avalanchedataset/creating-avalanchedatasets.ipynb b/notebooks/how-tos/avalanchedataset/creating-avalanchedatasets.ipynb new file mode 100644 index 000000000..d62ac92bf --- /dev/null +++ b/notebooks/how-tos/avalanchedataset/creating-avalanchedatasets.ipynb @@ -0,0 +1,440 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9d2b1a53-2326-41ca-a439-ad9264cff63b", + "metadata": {}, + "source": [ + "---\n", + "description: Creation and manipulation of AvalancheDatasets and its subclasses.\n", + "---\n", + "\n", + "# Creating AvalancheDatasets\n", + "\n", + "The *AvalancheDataset* is an implementation of the PyTorch Dataset class which comes with many out-of-the-box functionalities. The *AvalancheDataset* (an its few subclass) are extensively used through the whole Avalanche library as the reference way to manipulate datasets:\n", + "\n", + "- The dataset carried by the `experience.dataset` field is always an *AvalancheDataset*.\n", + "- Benchmark creation functions accept *AvalancheDataset*s to create benchmarks where a finer control over task labels is required.\n", + "- Internally, benchmarks are created by manipulating *AvalancheDataset*s.\n", + "\n", + "This first *Mini How-To* will guide through the main ways you can use to **instantiate an _AvalancheDataset_** while the **other Mini How-Tos ([complete list here](https://avalanche.continualai.org/how-tos/avalanchedataset)) will show how to use its functionalities**.\n", + "\n", + "It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page).\n", + "\n", + "Let's start by installing avalanche:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e583617f-2ad9-4ba9-a581-1d53eec5e205", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install git+https://github.com/ContinualAI/avalanche.git\n", + "\n", + "# Or, if you cloned Avalanche on your PC:\n", + "# import sys\n", + "# from pathlib import Path\n", + "# sys.path.append(str(Path.cwd() / '../../..'))" + ] + }, + { + "cell_type": "markdown", + "id": "114da4e5-8a44-4faa-b55e-3b0242b7a1c8", + "metadata": { + "tags": [] + }, + "source": [ + "## AvalancheDataset vs PyTorch Dataset\n", + "This mini How-To will guide you through the main ways used to instantiate an *AvalancheDataset*.\n", + "\n", + "First thing: the base class `AvalancheDataset` is a **wrapper for existing datasets**. Only two things must be considered when wrapping an existing dataset:\n", + "\n", + "- Apart from the x and y values, the resulting AvalancheDataset will also return a third value: the task label (which defaults to 0).\n", + "- The wrapped dataset must contain a valid **targets** field.\n", + "\n", + "The **targets field** is available is nearly all *torchvision* datasets. It must be a list containing the label for each data point (usually the y value). In this way, Avalanche can use that field when instantiating benchmarks like the \"Class/Task-Incremental* and *Domain-Incremental* ones.\n", + "\n", + "Avalanche exposes 4 classes of *AvalancheDataset*s which map exactly the 4 *Dataset* classes offered by PyTorch:\n", + "- `AvalancheDataset`: the base class, which acts a wrapper to existing *Dataset* instances.\n", + "- `AvalancheTensorDataset`: equivalent to PyTorch `TesnsorDataset`.\n", + "- `AvalancheSubset`: equivalent to PyTorch `Subset`.\n", + "- `AvalancheConcatDataset`: equivalent to PyTorch `ConcatDataset`.\n", + "\n", + "## 🛠️ Create an AvalancheDataset\n", + "Given a dataset (like MNIST), an *AvalancheDataset* can be instantiated as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2acfeea9-e8fe-4370-9a59-e034087b0acd", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from avalanche.benchmarks.utils import AvalancheDataset\n", + "from torchvision.datasets import MNIST\n", + "\n", + "# Instantiate the MNIST train dataset from torchvision\n", + "mnist_dataset = MNIST('mnist_data', download=True)\n", + "\n", + "# Create the AvalancheDataset\n", + "mnist_avalanche_dataset = AvalancheDataset(mnist_dataset)" + ] + }, + { + "cell_type": "markdown", + "id": "4f0ffacc-3990-41b9-bc85-cf6f053e8847", + "metadata": { + "tags": [] + }, + "source": [ + "Just like any other Dataset, a data point can be obtained using the `x, y = dataset[idx]` syntax. **When obtaining a data point from an AvalancheDataset, an additional third value (the task label) will be returned**:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d4069fe0-a458-4308-b018-3f6a9f9193e3", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=, y=5\n", + "x=, y=5, t=0\n" + ] + } + ], + "source": [ + "# Obtain the first instance from the original dataset\n", + "x, y = mnist_dataset[0]\n", + "print(f'x={x}, y={y}')\n", + "# Output: \"x=, y=5\"\n", + "\n", + "# Obtain the first instance from the AvalancheDataset\n", + "x, y, t = mnist_avalanche_dataset[0]\n", + "print(f'x={x}, y={y}, t={t}')\n", + "# Output: \"x=, y=5, t=0\"" + ] + }, + { + "cell_type": "markdown", + "id": "0df2be15-0471-473b-a603-089f41f54fc6", + "metadata": {}, + "source": [ + "**Useful tip:** if you are not sure if you are dealing with a PyTorch *Dataset* or an *AvalancheDataset*, or if you want to ignore task labels, you can use this syntax:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "ae39b667-6a3f-4c89-bdf8-196a42844ec5", + "metadata": {}, + "outputs": [], + "source": [ + "# You can use \"x, y, *_\" to manage both kinds of Datasets\n", + "x, y, *_ = mnist_dataset[0] # OK\n", + "x, y, *_ = mnist_avalanche_dataset[0] # OK" + ] + }, + { + "cell_type": "markdown", + "id": "1a5cdbbb-d3db-48f7-b946-d5a3c4151353", + "metadata": {}, + "source": [ + "## The AvalancheTensorDataset\n", + "The PyTorch *TensorDataset* is one of the most useful Dataset classes as it can be used to quickly prototype the data loading part of your code.\n", + "\n", + "A *TensorDataset* can be wrapped in an AvalancheDataset just like any Dataset, but this is not much convenient, as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8d3e9ca2-b971-4dea-9a80-f7f368e07ed5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=tensor([0.8383, 0.1409, 0.7622, 0.6625, 0.6322, 0.1188, 0.7383]), y=4, t=0\n" + ] + } + ], + "source": [ + "import torch\n", + "from torch.utils.data import TensorDataset\n", + "\n", + "\n", + "# Create 10 instances described by 7 features \n", + "x_data = torch.rand(10, 7)\n", + "\n", + "# Create the class labels for the 10 instances\n", + "y_data = torch.randint(0, 5, (10,))\n", + "\n", + "# Create the tensor dataset\n", + "tensor_dataset = TensorDataset(x_data, y_data)\n", + "\n", + "# Wrap it in an AvalancheDataset\n", + "wrapped_tensor_dataset = AvalancheDataset(tensor_dataset)\n", + "\n", + "# Obtain the first instance from the dataset\n", + "x, y, t = wrapped_tensor_dataset[0]\n", + "print(f'x={x}, y={y}, t={t}')\n", + "# Output: \"x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0\"" + ] + }, + { + "cell_type": "markdown", + "id": "669670c9-e3ea-44a2-aecf-40bd8cb9cdb8", + "metadata": {}, + "source": [ + "**Instead, it is recommended to use the AvalancheTensorDataset** class to get the same result. In this way, you can just skip one intermediate step." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ab827b7a-de79-4a38-bfd5-18dd582404aa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=tensor([0.8383, 0.1409, 0.7622, 0.6625, 0.6322, 0.1188, 0.7383]), y=4, t=0\n" + ] + } + ], + "source": [ + "from avalanche.benchmarks.utils import AvalancheTensorDataset\n", + "\n", + "# Create the tensor dataset\n", + "avl_tensor_dataset = AvalancheTensorDataset(x_data, y_data)\n", + "\n", + "# Obtain the first instance from the AvalancheTensorDataset\n", + "x, y, t = avl_tensor_dataset[0]\n", + "print(f'x={x}, y={y}, t={t}')\n", + "# Output: \"x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0\"" + ] + }, + { + "cell_type": "markdown", + "id": "6f614c68-c0dc-49d9-a3e2-03348e248ca9", + "metadata": {}, + "source": [ + "In both cases, **AvalancheDataset will automatically populate its _targets_ field by using the values from the second Tensor** (which usually contains the Y values). This behaviour can be customized by passing a custom `targets` constructor parameter (by either passing a list of targets or the index of the Tensor to use).\n", + "\n", + "The cell below shows the content of the target field of the dataset created in the cell above. Notice that the *targets* field has been filled with the content of the second Tensor (*y\\_data*)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "1f81487c-e05e-4b98-addd-25ef5f7d9746", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "y_data= tensor([4, 0, 2, 2, 1, 1, 0, 0, 0, 1])\n", + "targets field= [tensor(4), tensor(0), tensor(2), tensor(2), tensor(1), tensor(1), tensor(0), tensor(0), tensor(0), tensor(1)]\n" + ] + } + ], + "source": [ + "# Check the targets field\n", + "print('y_data=', y_data)\n", + " # Output: \"y_data= tensor([4, 3, 3, 2, 0, 1, 3, 3, 3, 2])\"\n", + "\n", + "print('targets field=', avl_tensor_dataset.targets)\n", + "# Output: \"targets field= [tensor(4), tensor(3), tensor(3), tensor(2), \n", + "# tensor(0), tensor(1), tensor(3), tensor(3), tensor(3), tensor(2)]\"" + ] + }, + { + "cell_type": "markdown", + "id": "62bdf7d2-97be-4e20-9ef5-2b51bebcfecd", + "metadata": {}, + "source": [ + "## The AvalancheSubset and AvalancheConcatDataset classes\n", + "Avalanche offers the `AvalancheSubset` and `AvalancheConcatDataset` implementations that extend the functionalities of PyTorch *Subset* and *ConcatDataset*.\n", + "\n", + "Regarding the subsetting operation, `AvalancheSubset` behaves in the same way the PyTorch `Subset` class does: both implementations accept a dataset and a list of indices as parameters. The resulting Subset is not a copy of the dataset, it's just a view. This is similar to creating a view of a NumPy array by passing a list of indexes using the `numpy_array[list_of_indices]` syntax. This can be used to both *create a smaller dataset* and to *change the order of data points* in the dataset.\n", + "\n", + "Here we create a toy dataset in which each X and Y values are *int*s. We then obtain a subset of it by creating an **AvalancheSubset**:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "02efbde8-5216-4595-acab-999c6386845b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The subset contains 4 instances.\n", + "x=50, y=10, t=0\n", + "x=55, y=15, t=0\n", + "x=58, y=18, t=0\n", + "x=52, y=12, t=0\n" + ] + } + ], + "source": [ + "from avalanche.benchmarks.utils import AvalancheSubset\n", + "\n", + "# Define the X values of 10 instances (each instance is an int)\n", + "x_data_toy = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]\n", + "\n", + "# Define the class labels for the 10 instances\n", + "y_data_toy = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n", + "\n", + "# Create the tensor dataset\n", + "# Note: AvalancheSubset can also be applied to PyTorch TensorDataset directly!\n", + "# However, note that PyTorch TensorDataset doesn't support Python lists...\n", + "# ... (it only supports Tensors) while AvalancheTensorDataset does.\n", + "toy_dataset = AvalancheTensorDataset(x_data_toy, y_data_toy) \n", + "\n", + "# Define the indices for the subset\n", + "# Here we want to obtain a subset containing only the data points...\n", + "# ... at indices 0, 5, 8, 2 (in this specific order)\n", + "subset_indices = [0, 5, 8, 2]\n", + "\n", + "# Create the subset\n", + "avl_subset = AvalancheSubset(toy_dataset, indices=subset_indices)\n", + "print('The subset contains', len(avl_subset), 'instances.')\n", + "# Output: \"The subset contains 4 instances.\"\n", + "\n", + "# Obtain instances from the AvalancheSubset\n", + "for x, y, t in avl_subset:\n", + " print(f'x={x}, y={y}, t={t}')\n", + "# Output:\n", + "# x=50, y=10, t=0\n", + "# x=55, y=15, t=0\n", + "# x=58, y=18, t=0\n", + "# x=52, y=12, t=0" + ] + }, + { + "cell_type": "markdown", + "id": "6ccde422-314d-4c6a-9d36-3bfd2e43d3c3", + "metadata": {}, + "source": [ + "Concatenation is even simpler. Just like with PyTorch *ConcatDataset*, one can easily concatentate datasets with **AvalancheConcatDataset**.\n", + "\n", + "Both *AvalancheConcatDataset* and PyTorch *ConcatDataset* accept a list of datasets to concatenate." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "fdf11bef-17cf-499d-8407-3c7c4e9f0a0b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The concat dataset contains 10 instances.\n", + "x=50, y=10, t=0\n", + "x=51, y=11, t=0\n", + "x=52, y=12, t=0\n", + "x=53, y=13, t=0\n", + "x=54, y=14, t=0\n", + "x=60, y=20, t=0\n", + "x=61, y=21, t=0\n", + "x=62, y=22, t=0\n", + "x=63, y=23, t=0\n", + "x=64, y=24, t=0\n" + ] + } + ], + "source": [ + "from avalanche.benchmarks.utils import AvalancheConcatDataset\n", + "\n", + "# Define the 2 datasets to be concatenated\n", + "x_data_toy_1 = [50, 51, 52, 53, 54]\n", + "y_data_toy_1 = [10, 11, 12, 13, 14]\n", + "x_data_toy_2 = [60, 61, 62, 63, 64]\n", + "y_data_toy_2 = [20, 21, 22, 23, 24]\n", + "\n", + "# Create the datasets\n", + "toy_dataset_1 = AvalancheTensorDataset(x_data_toy_1, y_data_toy_1) \n", + "toy_dataset_2 = AvalancheTensorDataset(x_data_toy_2, y_data_toy_2) \n", + "\n", + "# Create the concat dataset\n", + "avl_concat = AvalancheConcatDataset([toy_dataset_1, toy_dataset_2])\n", + "print('The concat dataset contains', len(avl_concat), 'instances.')\n", + "# Output: \"The concat dataset contains 10 instances.\"\n", + "\n", + "# Obtain instances from the AvalancheConcatDataset\n", + "for x, y, t in avl_concat:\n", + " print(f'x={x}, y={y}, t={t}')\n", + "# Output:\n", + "# x=51, y=11, t=0\n", + "# x=52, y=12, t=0\n", + "# x=53, y=13, t=0\n", + "# x=54, y=14, t=0\n", + "# x=60, y=20, t=0\n", + "# x=61, y=21, t=0\n", + "# x=62, y=22, t=0\n", + "# x=63, y=23, t=0\n", + "# x=64, y=24, t=0" + ] + }, + { + "cell_type": "markdown", + "id": "2901ba6e-7653-4356-852b-e402693897ca", + "metadata": {}, + "source": [ + "## Dataset Creation wrap-up\n", + "This *Mini How-To* showed you how to **create instances of AvalancheDataset (and its subclasses)**.\n", + "\n", + "Other *Mini How-To*s will guide you through the functionalities offered by AvalancheDataset. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanchedataset)." + ] + }, + { + "cell_type": "markdown", + "id": "18a9baff-7fab-4757-b155-fdb388bad8c9", + "metadata": {}, + "source": [ + "## 🤝 Run it on Google Colab\n", + "\n", + "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanchedataset/creating-avalanchedatasets.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/how-tos/avalanchedataset/preamble-pytorch-datasets.ipynb b/notebooks/how-tos/avalanchedataset/preamble-pytorch-datasets.ipynb new file mode 100644 index 000000000..93b75136f --- /dev/null +++ b/notebooks/how-tos/avalanchedataset/preamble-pytorch-datasets.ipynb @@ -0,0 +1,146 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ccdb4767", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "---\n", + "description: Few words about PyTorch Datasets\n", + "---\n", + "\n", + "# Preamble: PyTorch Datasets\n", + "This short preamble will briefly go through the basic notions of Dataset offered natively by PyTorch. A solid grasp of these notions are needed to understand:\n", + "1. How PyTorch data loading works in general\n", + "2. How AvalancheDatasets differs from PyTorch Datasets\n", + "\n", + "## 📚 Dataset: general definition\n", + "\n", + "In PyTorch, **a `Dataset` is a class** exposing two methods:\n", + "- `__len__()`, which returns the amount of instances in the dataset (as an `int`). \n", + "- `__getitem__(idx)`, which returns the data point at index `idx`.\n", + "\n", + "In other words, a Dataset instance is just an object for which, similarly to a list, one can simply:\n", + "- Obtain its length using the Python `len(dataset)` function.\n", + "- Obtain a single data point using the `x, y = dataset[idx]` syntax.\n", + "\n", + "The content of the dataset can be either loaded in memory when the dataset is instantiated (like the torchvision MNIST dataset does) or, for big datasets like ImageNet, the content is kept on disk, with the dataset keeping the list of files in an internal field. In this case, data is loaded from the storage on-the-fly when `__getitem__(idx)` is called. The way those things are managed is specific to each dataset implementation.\n", + "\n", + "## PyTorch Datasets\n", + "The PyTorch library offers 4 Dataset implementations:\n", + "- `Dataset`: an interface defining the `__len__` and `__getitem__` methods.\n", + "- `TensorDataset`: instantiated by passing X and Y tensors. Each row of the X and Y tensors is interpreted as a data point. The `__getitem__(idx)` method will simply return the `idx`-th row of X and Y tensors.\n", + "- `ConcatDataset`: instantiated by passing a list of datasets. The resulting dataset is a concatenation of those datasets.\n", + "- `Subset`: instantiated by passing a dataset and a list of indices. The resulting dataset will only contain the data points described by that list of indices.\n", + "\n", + "As explained in the mini *How-To*s, Avalanche offers a customized version for all these 4 datasets.\n", + "\n", + "## Transformations\n", + "Most datasets from the *torchvision* libraries (as well as datasets found \"in the wild\") allow for a `transformation` function to be passed to the dataset constructor. The support for transformations is not mandatory for a dataset, but it is quite common to support them. The transformation is used to process the X value of a data point before returning it. This is used to normalize values, apply augmentations, etcetera.\n", + "\n", + "As explained in the mini *How-To*s, the `AvalancheDataset` class implements a very rich and powerful set of functionalities for managing transformations.\n", + "\n", + "## Quick note on the IterableDataset class\n", + "A variation of the standard `Dataset` exist in PyTorch: the [IterableDataset](https://pytorch.org/docs/stable/data.html#iterable-style-datasets). When using an `IterableDataset`, one can load the data points in a sequential way only (by using a tape-alike approach). The `dataset[idx]` syntax and `len(dataset)` function are not allowed. **Avalanche does NOT support `IterableDataset`s.** You shouldn't worry about this because, realistically, you will never encounter such datasets.\n", + "\n", + "## DataLoader\n", + "The `Dataset` is a very simple object that only returns one data point given its index. In order to create minibatches and speed-up the data loading process, a `DataLoader` is required.\n", + "\n", + "The PyTorch `DataLoader` class is a very efficient mechanism that, given a `Dataset`, will return **minibatches** by optonally **shuffling** data brefore each epoch and by **loading data in parallel** by using multiple workers.\n", + "\n", + "## Preamble wrap-up\n", + "To wrap-up, let's see how the native, *non-Avalanche*, PyTorch components work in practice. In the following code we create a `TensorDataset` and then we load it in minibatches using a `DataLoader`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8bce4be3-91ef-4816-9a3f-5c392ef05027", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n", + "Loaded minibatch of 10 instances\n" + ] + } + ], + "source": [ + "import torch\n", + "from torch.utils.data.dataset import TensorDataset\n", + "from torch.utils.data.dataloader import DataLoader\n", + "\n", + "# Create a dataset of 100 data points described by 22 features + 1 class label\n", + "x_data = torch.rand(100, 22)\n", + "y_data = torch.randint(0, 5, (100,))\n", + "\n", + "# Create the Dataset\n", + "my_dataset = TensorDataset(x_data, y_data)\n", + "\n", + "# Create the DataLoader\n", + "my_dataloader = DataLoader(my_dataset, batch_size=10, shuffle=True, num_workers=4)\n", + "\n", + "# Run one epoch\n", + "for x_minibatch, y_minibatch in my_dataloader:\n", + " print('Loaded minibatch of', len(x_minibatch), 'instances')\n", + "# Output: \"Loaded minibatch of 10 instances\" x10 times" + ] + }, + { + "cell_type": "markdown", + "id": "93f6fdec-f1d0-4cdc-a6e0-6e7c0a6b3be7", + "metadata": {}, + "source": [ + "## Next steps\n", + "With these notions in mind, you can start start your journey on understanding the functionalities offered by the AvalancheDatasets by going through the *Mini How-To*s.\n", + "\n", + "Please refer to the [list of the *Mini How-To*s regarding AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanchedataset) for a complete list. It is recommended to start with the **\"Creating AvalancheDatasets\"** *Mini How-To*." + ] + }, + { + "cell_type": "markdown", + "id": "02de0ce4-2711-4832-8b0e-516040483ae5", + "metadata": {}, + "source": [ + "## 🤝 Run it on Google Colab\n", + "\n", + "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanchedataset/preamble-pytorch-datasets.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 508821bd18a945d00787c2e6868d92b7d4681da8 Mon Sep 17 00:00:00 2001 From: Lorenzo Pellegrini Date: Mon, 29 Nov 2021 19:22:51 +0100 Subject: [PATCH 27/60] Remove old How-To structure. --- .../avalanche_dataset_creation.ipynb | 440 ------------- .../avalanche_dataset_transformations.ipynb | 623 ------------------ .../how-tos/avalanche_dataset_preamble.ipynb | 172 ----- 3 files changed, 1235 deletions(-) delete mode 100644 notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb delete mode 100644 notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb delete mode 100644 notebooks/how-tos/avalanche_dataset_preamble.ipynb diff --git a/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb b/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb deleted file mode 100644 index 305243a8f..000000000 --- a/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb +++ /dev/null @@ -1,440 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "9d2b1a53-2326-41ca-a439-ad9264cff63b", - "metadata": {}, - "source": [ - "---\n", - "description: Creation and manipulation of AvalancheDatasets and its subclasses.\n", - "---\n", - "\n", - "# Creating AvalancheDatasets\n", - "\n", - "The *AvalancheDataset* is an implementation of the PyTorch Dataset class which comes with many out-of-the-box functionalities. The *AvalancheDataset* (an its few subclass) are extensively used through the whole Avalanche library as the reference way to manipulate datasets:\n", - "\n", - "- The dataset carried by the `experience.dataset` field is always an *AvalancheDataset*.\n", - "- Benchmark creation functions accept *AvalancheDataset*s to create benchmarks where a finer control over task labels is required.\n", - "- Internally, benchmarks are created by manipulating *AvalancheDataset*s.\n", - "\n", - "This first *Mini How-To* will guide through the main ways you can use to **instantiate an _AvalancheDataset_** while the **other Mini How-Tos ([complete list here](https://avalanche.continualai.org/how-tos/avalanche_dataset_preamble)) will show how to use its functionalities**.\n", - "\n", - "It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page).\n", - "\n", - "Let's start by installing avalanche:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "e583617f-2ad9-4ba9-a581-1d53eec5e205", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install git+https://github.com/ContinualAI/avalanche.git\n", - "\n", - "# Or, if you cloned Avalanche on your PC:\n", - "# import sys\n", - "# from pathlib import Path\n", - "# sys.path.append(str(Path.cwd() / '../../..'))" - ] - }, - { - "cell_type": "markdown", - "id": "114da4e5-8a44-4faa-b55e-3b0242b7a1c8", - "metadata": { - "tags": [] - }, - "source": [ - "## AvalancheDataset vs PyTorch Dataset\n", - "This mini How-To will guide you through the main ways used to instantiate an *AvalancheDataset*.\n", - "\n", - "First thing: the base class `AvalancheDataset` is a **wrapper for existing datasets**. Only two things must be considered when wrapping an existing dataset:\n", - "\n", - "- Apart from the x and y values, the resulting AvalancheDataset will also return a third value: the task label (which defaults to 0).\n", - "- The wrapped dataset must contain a valid **targets** field.\n", - "\n", - "The **targets field** is available is nearly all *torchvision* datasets. It must be a list containing the label for each data point (usually the y value). In this way, Avalanche can use that field when instantiating benchmarks like the \"Class/Task-Incremental* and *Domain-Incremental* ones.\n", - "\n", - "Avalanche exposes 4 classes of *AvalancheDataset*s which map exactly the 4 *Dataset* classes offered by PyTorch:\n", - "- `AvalancheDataset`: the base class, which acts a wrapper to existing *Dataset* instances.\n", - "- `AvalancheTensorDataset`: equivalent to PyTorch `TesnsorDataset`.\n", - "- `AvalancheSubset`: equivalent to PyTorch `Subset`.\n", - "- `AvalancheConcatDataset`: equivalent to PyTorch `ConcatDataset`.\n", - "\n", - "## 🛠️ Create an AvalancheDataset\n", - "Given a dataset (like MNIST), an *AvalancheDataset* can be instantiated as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "2acfeea9-e8fe-4370-9a59-e034087b0acd", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "from avalanche.benchmarks.utils import AvalancheDataset\n", - "from torchvision.datasets import MNIST\n", - "\n", - "# Instantiate the MNIST train dataset from torchvision\n", - "mnist_dataset = MNIST('mnist_data', download=True)\n", - "\n", - "# Create the AvalancheDataset\n", - "mnist_avalanche_dataset = AvalancheDataset(mnist_dataset)" - ] - }, - { - "cell_type": "markdown", - "id": "4f0ffacc-3990-41b9-bc85-cf6f053e8847", - "metadata": { - "tags": [] - }, - "source": [ - "Just like any other Dataset, a data point can be obtained using the `x, y = dataset[idx]` syntax. **When obtaining a data point from an AvalancheDataset, an additional third value (the task label) will be returned**:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "d4069fe0-a458-4308-b018-3f6a9f9193e3", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "x=, y=5\n", - "x=, y=5, t=0\n" - ] - } - ], - "source": [ - "# Obtain the first instance from the original dataset\n", - "x, y = mnist_dataset[0]\n", - "print(f'x={x}, y={y}')\n", - "# Output: \"x=, y=5\"\n", - "\n", - "# Obtain the first instance from the AvalancheDataset\n", - "x, y, t = mnist_avalanche_dataset[0]\n", - "print(f'x={x}, y={y}, t={t}')\n", - "# Output: \"x=, y=5, t=0\"" - ] - }, - { - "cell_type": "markdown", - "id": "0df2be15-0471-473b-a603-089f41f54fc6", - "metadata": {}, - "source": [ - "**Useful tip:** if you are not sure if you are dealing with a PyTorch *Dataset* or an *AvalancheDataset*, or if you want to ignore task labels, you can use this syntax:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "ae39b667-6a3f-4c89-bdf8-196a42844ec5", - "metadata": {}, - "outputs": [], - "source": [ - "# You can use \"x, y, *_\" to manage both kinds of Datasets\n", - "x, y, *_ = mnist_dataset[0] # OK\n", - "x, y, *_ = mnist_avalanche_dataset[0] # OK" - ] - }, - { - "cell_type": "markdown", - "id": "1a5cdbbb-d3db-48f7-b946-d5a3c4151353", - "metadata": {}, - "source": [ - "## The AvalancheTensorDataset\n", - "The PyTorch *TensorDataset* is one of the most useful Dataset classes as it can be used to quickly prototype the data loading part of your code.\n", - "\n", - "A *TensorDataset* can be wrapped in an AvalancheDataset just like any Dataset, but this is not much convenient, as shown below:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "8d3e9ca2-b971-4dea-9a80-f7f368e07ed5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "x=tensor([0.8383, 0.1409, 0.7622, 0.6625, 0.6322, 0.1188, 0.7383]), y=4, t=0\n" - ] - } - ], - "source": [ - "import torch\n", - "from torch.utils.data import TensorDataset\n", - "\n", - "\n", - "# Create 10 instances described by 7 features \n", - "x_data = torch.rand(10, 7)\n", - "\n", - "# Create the class labels for the 10 instances\n", - "y_data = torch.randint(0, 5, (10,))\n", - "\n", - "# Create the tensor dataset\n", - "tensor_dataset = TensorDataset(x_data, y_data)\n", - "\n", - "# Wrap it in an AvalancheDataset\n", - "wrapped_tensor_dataset = AvalancheDataset(tensor_dataset)\n", - "\n", - "# Obtain the first instance from the dataset\n", - "x, y, t = wrapped_tensor_dataset[0]\n", - "print(f'x={x}, y={y}, t={t}')\n", - "# Output: \"x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0\"" - ] - }, - { - "cell_type": "markdown", - "id": "669670c9-e3ea-44a2-aecf-40bd8cb9cdb8", - "metadata": {}, - "source": [ - "**Instead, it is recommended to use the AvalancheTensorDataset** class to get the same result. In this way, you can just skip one intermediate step." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "ab827b7a-de79-4a38-bfd5-18dd582404aa", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "x=tensor([0.8383, 0.1409, 0.7622, 0.6625, 0.6322, 0.1188, 0.7383]), y=4, t=0\n" - ] - } - ], - "source": [ - "from avalanche.benchmarks.utils import AvalancheTensorDataset\n", - "\n", - "# Create the tensor dataset\n", - "avl_tensor_dataset = AvalancheTensorDataset(x_data, y_data)\n", - "\n", - "# Obtain the first instance from the AvalancheTensorDataset\n", - "x, y, t = avl_tensor_dataset[0]\n", - "print(f'x={x}, y={y}, t={t}')\n", - "# Output: \"x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0\"" - ] - }, - { - "cell_type": "markdown", - "id": "6f614c68-c0dc-49d9-a3e2-03348e248ca9", - "metadata": {}, - "source": [ - "In both cases, **AvalancheDataset will automatically populate its _targets_ field by using the values from the second Tensor** (which usually contains the Y values). This behaviour can be customized by passing a custom `targets` constructor parameter (by either passing a list of targets or the index of the Tensor to use).\n", - "\n", - "The cell below shows the content of the target field of the dataset created in the cell above. Notice that the *targets* field has been filled with the content of the second Tensor (*y\\_data*)." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "1f81487c-e05e-4b98-addd-25ef5f7d9746", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "y_data= tensor([4, 0, 2, 2, 1, 1, 0, 0, 0, 1])\n", - "targets field= [tensor(4), tensor(0), tensor(2), tensor(2), tensor(1), tensor(1), tensor(0), tensor(0), tensor(0), tensor(1)]\n" - ] - } - ], - "source": [ - "# Check the targets field\n", - "print('y_data=', y_data)\n", - " # Output: \"y_data= tensor([4, 3, 3, 2, 0, 1, 3, 3, 3, 2])\"\n", - "\n", - "print('targets field=', avl_tensor_dataset.targets)\n", - "# Output: \"targets field= [tensor(4), tensor(3), tensor(3), tensor(2), \n", - "# tensor(0), tensor(1), tensor(3), tensor(3), tensor(3), tensor(2)]\"" - ] - }, - { - "cell_type": "markdown", - "id": "62bdf7d2-97be-4e20-9ef5-2b51bebcfecd", - "metadata": {}, - "source": [ - "## The AvalancheSubset and AvalancheConcatDataset classes\n", - "Avalanche offers the `AvalancheSubset` and `AvalancheConcatDataset` implementations that extend the functionalities of PyTorch *Subset* and *ConcatDataset*.\n", - "\n", - "Regarding the subsetting operation, `AvalancheSubset` behaves in the same way the PyTorch `Subset` class does: both implementations accept a dataset and a list of indices as parameters. The resulting Subset is not a copy of the dataset, it's just a view. This is similar to creating a view of a NumPy array by passing a list of indexes using the `numpy_array[list_of_indices]` syntax. This can be used to both *create a smaller dataset* and to *change the order of data points* in the dataset.\n", - "\n", - "Here we create a toy dataset in which each X and Y values are *int*s. We then obtain a subset of it by creating an **AvalancheSubset**:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "02efbde8-5216-4595-acab-999c6386845b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The subset contains 4 instances.\n", - "x=50, y=10, t=0\n", - "x=55, y=15, t=0\n", - "x=58, y=18, t=0\n", - "x=52, y=12, t=0\n" - ] - } - ], - "source": [ - "from avalanche.benchmarks.utils import AvalancheSubset\n", - "\n", - "# Define the X values of 10 instances (each instance is an int)\n", - "x_data_toy = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]\n", - "\n", - "# Define the class labels for the 10 instances\n", - "y_data_toy = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n", - "\n", - "# Create the tensor dataset\n", - "# Note: AvalancheSubset can also be applied to PyTorch TensorDataset directly!\n", - "# However, note that PyTorch TensorDataset doesn't support Python lists...\n", - "# ... (it only supports Tensors) while AvalancheTensorDataset does.\n", - "toy_dataset = AvalancheTensorDataset(x_data_toy, y_data_toy) \n", - "\n", - "# Define the indices for the subset\n", - "# Here we want to obtain a subset containing only the data points...\n", - "# ... at indices 0, 5, 8, 2 (in this specific order)\n", - "subset_indices = [0, 5, 8, 2]\n", - "\n", - "# Create the subset\n", - "avl_subset = AvalancheSubset(toy_dataset, indices=subset_indices)\n", - "print('The subset contains', len(avl_subset), 'instances.')\n", - "# Output: \"The subset contains 4 instances.\"\n", - "\n", - "# Obtain instances from the AvalancheSubset\n", - "for x, y, t in avl_subset:\n", - " print(f'x={x}, y={y}, t={t}')\n", - "# Output:\n", - "# x=50, y=10, t=0\n", - "# x=55, y=15, t=0\n", - "# x=58, y=18, t=0\n", - "# x=52, y=12, t=0" - ] - }, - { - "cell_type": "markdown", - "id": "6ccde422-314d-4c6a-9d36-3bfd2e43d3c3", - "metadata": {}, - "source": [ - "Concatenation is even simpler. Just like with PyTorch *ConcatDataset*, one can easily concatentate datasets with **AvalancheConcatDataset**.\n", - "\n", - "Both *AvalancheConcatDataset* and PyTorch *ConcatDataset* accept a list of datasets to concatenate." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "fdf11bef-17cf-499d-8407-3c7c4e9f0a0b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The concat dataset contains 10 instances.\n", - "x=50, y=10, t=0\n", - "x=51, y=11, t=0\n", - "x=52, y=12, t=0\n", - "x=53, y=13, t=0\n", - "x=54, y=14, t=0\n", - "x=60, y=20, t=0\n", - "x=61, y=21, t=0\n", - "x=62, y=22, t=0\n", - "x=63, y=23, t=0\n", - "x=64, y=24, t=0\n" - ] - } - ], - "source": [ - "from avalanche.benchmarks.utils import AvalancheConcatDataset\n", - "\n", - "# Define the 2 datasets to be concatenated\n", - "x_data_toy_1 = [50, 51, 52, 53, 54]\n", - "y_data_toy_1 = [10, 11, 12, 13, 14]\n", - "x_data_toy_2 = [60, 61, 62, 63, 64]\n", - "y_data_toy_2 = [20, 21, 22, 23, 24]\n", - "\n", - "# Create the datasets\n", - "toy_dataset_1 = AvalancheTensorDataset(x_data_toy_1, y_data_toy_1) \n", - "toy_dataset_2 = AvalancheTensorDataset(x_data_toy_2, y_data_toy_2) \n", - "\n", - "# Create the concat dataset\n", - "avl_concat = AvalancheConcatDataset([toy_dataset_1, toy_dataset_2])\n", - "print('The concat dataset contains', len(avl_concat), 'instances.')\n", - "# Output: \"The concat dataset contains 10 instances.\"\n", - "\n", - "# Obtain instances from the AvalancheConcatDataset\n", - "for x, y, t in avl_concat:\n", - " print(f'x={x}, y={y}, t={t}')\n", - "# Output:\n", - "# x=51, y=11, t=0\n", - "# x=52, y=12, t=0\n", - "# x=53, y=13, t=0\n", - "# x=54, y=14, t=0\n", - "# x=60, y=20, t=0\n", - "# x=61, y=21, t=0\n", - "# x=62, y=22, t=0\n", - "# x=63, y=23, t=0\n", - "# x=64, y=24, t=0" - ] - }, - { - "cell_type": "markdown", - "id": "2901ba6e-7653-4356-852b-e402693897ca", - "metadata": {}, - "source": [ - "## Dataset Creation wrap-up\n", - "This *Mini How-To* showed you how to **create instances of AvalancheDataset (and its subclasses)**.\n", - "\n", - "Other *Mini How-To*s will guide you through the functionalities offered by AvalancheDataset. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanche_dataset_preamble)." - ] - }, - { - "cell_type": "markdown", - "id": "18a9baff-7fab-4757-b155-fdb388bad8c9", - "metadata": {}, - "source": [ - "## 🤝 Run it on Google Colab\n", - "\n", - "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation.ipynb)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.8.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb b/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb deleted file mode 100644 index d3d2535f3..000000000 --- a/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb +++ /dev/null @@ -1,623 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "53277e45-b7d7-4857-a79d-d7218bd6b6a9", - "metadata": {}, - "source": [ - "---\n", - "description: Dealing with transformations (groups, appending, replacing, freezing).\n", - "---\n", - "\n", - "# Advanced Transformations\n", - "AvalancheDataset (and its subclasses like the Avalanche*Tensor/Subset/Concat*Dataset) allow for a finer control over transformations. While torchvision (and other) datasets allow for a minimal mechanism to apply transformations, with AvalancheDataset one can:\n", - "1. Have multiple **transformation \"groups\"** in the same dataset (like separated train and test transformations).\n", - "2. **Append, replace and remove transformations**, even by using nested Subset/Concat Datasets.\n", - "3. **Freeze transformations**, so that they can't be changed.\n", - "\n", - "The following sub-sections show examples on how to use these features. Please note that all the constructor parameters and the methods described in this How-To can be used on AvalancheDataset subclasses as well. For more info on all the available subclasses, refer to [this Mini How-To](https://avalanche.continualai.org/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation).\n", - "\n", - "It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page).\n", - "\n", - "Let's start by installing Avalanche:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "470e6f0b-ecc2-45c3-af53-112da5d7c37e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/home/lorenzo/Desktop/github_repos/avalanche/notebooks\n" - ] - } - ], - "source": [ - "!pip install git+https://github.com/ContinualAI/avalanche.git\n", - "\n", - "# Or, if you cloned Avalanche on your PC:\n", - "# import sys\n", - "# from pathlib import Path\n", - "# sys.path.append(str(Path.cwd() / '../../..'))" - ] - }, - { - "cell_type": "markdown", - "id": "e770eef5-86d4-4601-b26f-ea12013515df", - "metadata": {}, - "source": [ - "## Transformation groups\n", - "AvalancheDatasets can contain multiple **transformation groups**. This can be useful to keep train and test transformations in the same dataset and to have different set of transformations. This may come in handy in many situations (for instance, to apply ad-hoc transformations to replay data).\n", - "\n", - "As in torchvision datasets, AvalancheDataset supports the two kind of transformations: the `transform`, which is applied to X values, and the `target_transform`, which is applied to Y values. The latter is rarely used. This means that **a transformation group is a pair of transformations to be applied to the X and Y values** of each instance returned by the dataset. In both torchvision and Avalanche implementations, **a transformation must be a function (or other callable object)** that accepts one input (the X or Y value) and outputs its transformed version. This pair of functions is stored in the `transform` and `target_transform` fields of the dataset. A comprehensive guide on transformations can be found in the [torchvision documentation](https://pytorch.org/vision/stable/transforms.html).\n", - "\n", - "In the following example, a MNIST dataset is created and then wrapped in an AvalancheDataset. When creating the AvalancheDataset, we can set *train* and *eval* transformations by passing a *transform\\_groups* parameter. Train transformations usually include some form of random augmentation, while eval transformations usually include a sequence of deterministic transformations only. Here we define the sequence of train transformations as a random rotation followed by the ToTensor operation. The eval transformations only include the ToTensor operation." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "482132af-cfa8-4c16-ae27-5e7a67c75a0c", - "metadata": {}, - "outputs": [], - "source": [ - "from torchvision import transforms\n", - "from torchvision.datasets import MNIST\n", - "from avalanche.benchmarks.utils import AvalancheDataset\n", - "\n", - "mnist_dataset = MNIST('mnist_data', download=True)\n", - "\n", - "# Define the training transformation for X values\n", - "train_transformation = transforms.Compose([\n", - " transforms.RandomRotation(45),\n", - " transforms.ToTensor(),\n", - "])\n", - "# Define the training transformation for Y values (rarely used)\n", - "train_target_transformation = None\n", - "\n", - "# Define the test transformation for X values\n", - "eval_transformation = transforms.ToTensor()\n", - "# Define the test transformation for Y values (rarely used)\n", - "eval_target_transformation = None\n", - "\n", - "transform_groups = {\n", - " 'train': (train_transformation, train_target_transformation),\n", - " 'eval': (eval_transformation, eval_target_transformation)\n", - "}\n", - "\n", - "avl_mnist_transform = AvalancheDataset(mnist_dataset, transform_groups=transform_groups)" - ] - }, - { - "cell_type": "markdown", - "id": "d5f7cca4-e2e7-456b-b6da-54c1c8579eb1", - "metadata": {}, - "source": [ - "Of course, one can also just use the `transform` and `target_transform` constructor parameters to set the transformations for both the *train* and the *eval* groups. However, it is recommended to use the approach based on *transform\\_groups* (shown in the code above) as it is much more flexible." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "51fbd4bf-9535-446a-bc95-6d1cd0dd96f9", - "metadata": {}, - "outputs": [], - "source": [ - "# Not recommended: use transform_groups instead\n", - "avl_mnist_same_transforms = AvalancheDataset(mnist_dataset, transform=train_transformation)" - ] - }, - { - "cell_type": "markdown", - "id": "fbdfa287-347e-4847-95f8-c5a00a4a2107", - "metadata": {}, - "source": [ - "### Using `.train()` and `.eval()`\n", - "\n", - "**The default behaviour of the AvalancheDataset is to use transformations from the _train_ group.** However, one can easily obtain a version of the dataset where the *eval* group is used. Note: when obtaining the dataset of experiences from the test stream, those datasets will already be using the *eval* group of transformations so you don't need to switch to the eval group ;).\n", - "\n", - "As noted before, transformations for the current group are loaded in the `transform` and `target_transform` fields. These fields can be changed directly, but this is *NOT* recommended, as this will not create a copy of the dataset and may probably affect other parts of the code in which the dataset is used.\n", - "\n", - "The recommended way to switch between the *train* and *eval* groups is to use the `.train()` and `.eval()` methods to obtain a copy (view) of the dataset with the proper transformations enabled. This is another very handy feature of the AvalancheDataset: **methods that manipulate the AvalancheDataset fields (and transformations) always create a view of the dataset. The original dataset is never changed.**\n", - "\n", - "In the following cell we use the *avl\\_mnist\\_transform* dataset created in the cells above. We first obtain a view of it in which *eval* transformations are enabled. Then, starting from this view, we obtain a version of it in which *train* transformations are enabled. We want to double-stress that `.train()` and `.eval()` never change the group of the dataset on which they are called: they always create a view.\n", - "\n", - "One can check that the correct transformation group is in use by looking at the content of the *transform/target_transform* fields." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "6f8500fa-0f7f-48f7-a26c-d77a1588a244", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original dataset transformation: Compose(\n", - " RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", - " ToTensor()\n", - ")\n", - "--------------------------------\n", - "Eval version of the dataset: ToTensor()\n", - "--------------------------------\n", - "Back to train transformations: Compose(\n", - " RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", - " ToTensor()\n", - ")\n" - ] - } - ], - "source": [ - "# Obtain a view of the dataset in which eval transformations are enabled\n", - "avl_mnist_eval = avl_mnist_transform.eval()\n", - "\n", - "# Obtain a view of the dataset in which we get back to train transforms\n", - "# Basically, avl_mnist_transform ~= avl_mnist_train\n", - "avl_mnist_train = avl_mnist_eval.train()\n", - "\n", - "# Check the current transformations function for the 3 datasets\n", - "print('Original dataset transformation:', avl_mnist_transform.transform)\n", - "# Output:\n", - "# Original dataset transformation: Compose(\n", - "# RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", - "# ToTensor()\n", - "# )\n", - "print('--------------------------------')\n", - "print('Eval version of the dataset:', avl_mnist_eval.transform)\n", - "# Output: \"Eval version of the dataset: ToTensor()\"\n", - "print('--------------------------------')\n", - "print('Back to train transformations:', avl_mnist_train.transform)\n", - "# Output:\n", - "# Back to train transformations: Compose(\n", - "# RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)\n", - "# ToTensor()\n", - "# )" - ] - }, - { - "cell_type": "markdown", - "id": "8172b379-d60a-43ba-8e0d-c4fcdc0e997e", - "metadata": {}, - "source": [ - "### Custom transformation groups\n", - "In *AvalancheDataset*s the **_train_ and _eval_ transformation groups are always available**. However, *AvalancheDataset* also supports **custom transformation groups**.\n", - "\n", - "The following example shows how to create an AvalancheDataset with an additional group named *replay*. We define the replay transformation as a random crop followed by the ToTensor operation." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "69c8912d-b826-4265-ba71-c33501a1f956", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "replay_transform = transforms.Compose([\n", - " transforms.RandomCrop(28, padding=4),\n", - " transforms.ToTensor()\n", - "])\n", - "\n", - "replay_target_transform = None\n", - "\n", - "transform_groups_with_replay = {\n", - " 'train': (None, None),\n", - " 'eval': (None, None),\n", - " 'replay': (replay_transform, replay_target_transform)\n", - "}\n", - "\n", - "AvalancheDataset(mnist_dataset, transform_groups=transform_groups_with_replay)" - ] - }, - { - "cell_type": "markdown", - "id": "6bc0508a-bc4d-4896-984c-609c5803f9e6", - "metadata": {}, - "source": [ - "However, once created the dataset will use the *train* group. There are two ways to **switch to our custom group**:\n", - "- Set the group when creating the dataset using the `initial_transform_group` constructor parameter\n", - "- Switch to the group using the `.with_transforms(group_name)` method\n", - "\n", - "The `.with_transforms(group_name)` method behaves in the same way `.train()` and `.eval()` do by creating a view of the original dataset.\n", - "\n", - "The following example shows how to use both methods:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "2fd29287-da97-4aad-ab3c-cfd514629ad8", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Compose(\n", - " RandomCrop(size=(28, 28), padding=4)\n", - " ToTensor()\n", - ")\n", - "Compose(\n", - " RandomCrop(size=(28, 28), padding=4)\n", - " ToTensor()\n", - ")\n" - ] - } - ], - "source": [ - "# Method 1: create the dataset with \"replay\" as the default group\n", - "avl_mnist_custom_transform_1 = AvalancheDataset(\n", - " mnist_dataset,\n", - " transform_groups=transform_groups_with_replay,\n", - " initial_transform_group='replay')\n", - "\n", - "print(avl_mnist_custom_transform_1.transform)\n", - "\n", - "# Method 2: switch to \"replay\" using `.with_transforms(group_name)`\n", - "avl_mnist_custom_transform_not_enabled = AvalancheDataset(\n", - " mnist_dataset,\n", - " transform_groups=transform_groups_with_replay)\n", - "\n", - "avl_mnist_custom_transform_2 = avl_mnist_custom_transform_not_enabled.with_transforms('replay')\n", - "print(avl_mnist_custom_transform_2.transform)\n", - "\n", - "# Both prints output:\n", - "# Compose(\n", - "# RandomCrop(size=(28, 28), padding=4)\n", - "# ToTensor()\n", - "# )" - ] - }, - { - "cell_type": "markdown", - "id": "08fc9f3f-f95a-49ed-ba94-1bbec8960235", - "metadata": {}, - "source": [ - "## Appending transformations\n", - "\n", - "In the standard torchvision datasets the only way to append (that is, add a new transformation step to the list of existing one) is to change the *transform* field directly by doing something like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "e4d02b6e-0e73-4205-a497-a1540ff03185", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Compose(\n", - " ToTensor()\n", - " RandomCrop(size=(28, 28), padding=4)\n", - ")\n" - ] - } - ], - "source": [ - "# Append a transform by using torchvision datasets (>>> DON'T DO THIS! <<<)\n", - "\n", - "# Create the dataset\n", - "mnist_dataset_w_totensor = MNIST('mnist_data', download=True, transform=transforms.ToTensor())\n", - "\n", - "# Append a transform\n", - "to_append_transform = transforms.RandomCrop(size=(28, 28), padding=4)\n", - "mnist_dataset_w_totensor.transform = transforms.Compose(\n", - " [mnist_dataset_w_totensor.transform, to_append_transform]\n", - ")\n", - "print(mnist_dataset_w_totensor.transform)\n", - "# Prints:\n", - "# Compose(\n", - "# ToTensor()\n", - "# RandomCrop(size=(28, 28), padding=4)\n", - "# )" - ] - }, - { - "cell_type": "markdown", - "id": "55cb1749-2f72-4780-9699-99b97544f4da", - "metadata": {}, - "source": [ - "This solution has many huge drawbacks:\n", - "- The transformation field of the dataset is changed directly. This will affect other parts of the code that use that dataset instance.\n", - "- If the initial transform is `None`, then `Compose` will not complain, but the process will crash later (try it by yourself: replace the first element of Compose in cell above with `None`, then try obtaining a data point from the dataset).\n", - "- If you need to change transformations only temporarly to do some specific things in a limited part of the code, then you need to store the previous set of transformations in some variable in order to switch back to them later.\n", - "\n", - "AvalancheDataset offers a very simple method to append transformations without incurring in those issues. The `.add_transforms(transform=None, target_transform=None)` method will append the given transform(s) **to the currently enabled transform group** and will return a new (a view actually) dataset with given transformations appended to the existing ones. The original dataset is not affected. One can also use `.add_transforms_to_group(group_name, transform, target_transform)` to change transformations for a different group.\n", - "\n", - "The next cell shows how to use `.add_transforms(...)` to append the *to\\_append\\_transform* transform defined in the cell above." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "99a295d6-7a3f-4e8a-89af-9f97915848c2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "With appended transforms: Compose(\n", - " ToTensor()\n", - " RandomCrop(size=(28, 28), padding=4)\n", - ")\n", - "Original dataset: ToTensor()\n" - ] - } - ], - "source": [ - "# Create the dataset\n", - "avl_mnist = AvalancheDataset(MNIST('mnist_data', download=True), transform=transforms.ToTensor())\n", - "\n", - "# Append a transformation. Simple as:\n", - "avl_mnist_appended_transform = avl_mnist.add_transforms(to_append_transform)\n", - "\n", - "print('With appended transforms:', avl_mnist_appended_transform.transform)\n", - "# Prints:\n", - "# With appended transforms: Compose(\n", - "# ToTensor()\n", - "# RandomCrop(size=(28, 28), padding=4)\n", - "# )\n", - "\n", - "# Check that the original dataset was not affected:\n", - "print('Original dataset:', avl_mnist.transform)\n", - "# Prints: \"Original dataset: ToTensor()\"" - ] - }, - { - "cell_type": "markdown", - "id": "19f6a174-4073-4b24-a604-95df9ff5a0f3", - "metadata": {}, - "source": [ - "Note that by using `.add_transforms(...)`:\n", - "\n", - "- The original dataset is not changed, which means that other parts of the code that use that dataset instance are not affected.\n", - "- You don't need to worry about *None* transformations.\n", - "- In order to revert to the original transformations you don't need to keep a copy of them: the original dataset is not affected!" - ] - }, - { - "cell_type": "markdown", - "id": "c5ee8e22-982d-4fa1-ab2a-97e47bb1d693", - "metadata": {}, - "source": [ - "## Replacing transformations\n", - "\n", - "The replacement operation follows the same idea (and benefits) of the append one. By using `.replace_transforms(transform, target_transform)` one can obtain a view of the original dataset in which the **transformaations for the current group** are replaced with the given ones. One may also change tranformations for other groups by passing the name of the group as the optional parameter `group`. As with any transform-related operation, the original dataset is not affected. \n", - "\n", - "Note: one can use `.replace_transforms(...)` to remove previous transformations (by passing `None` as the new transform).\n", - "\n", - "The following cell shows how to use `.replace_transforms(...)` to replace the transformations of the current group:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "0e9f9c46-9d3d-40eb-b7b8-afad15668bc9", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "With replaced transform: RandomCrop(size=(28, 28), padding=4)\n", - "Original dataset: ToTensor()\n" - ] - } - ], - "source": [ - "new_transform = transforms.RandomCrop(size=(28, 28), padding=4)\n", - "\n", - "# Append a transformation. Simple as:\n", - "avl_mnist_replaced_transform = avl_mnist.replace_transforms(new_transform, None)\n", - "\n", - "print('With replaced transform:', avl_mnist_replaced_transform.transform)\n", - "# Prints: \"With replaces transforms: RandomCrop(size=(28, 28), padding=4)\"\n", - "\n", - "# Check that the original dataset was not affected:\n", - "print('Original dataset:', avl_mnist.transform)\n", - "# Prints: \"Original dataset: ToTensor()\"" - ] - }, - { - "cell_type": "markdown", - "id": "bc2e4781-0f2f-4eb6-b61e-affc8aaf255c", - "metadata": {}, - "source": [ - "## Freezing transformations\n", - "\n", - "One last functionality regarding transformations is the ability to \"freeze\" transformations. Freezing transformations menas **permanently glueing transformations to the dataset so that they can't be replaced or changed in any way** (usually by mistake). Frozen transformations cannot be changed by using `.replace_transforms(...)` or even by changing the `transform` field directly.\n", - "\n", - "One may wonder when this may come in handy... in fact, you will probably rarely need to freeze transformations. However, imagine having to instantiate the PermutedMNIST benchmark. You want the permutation transformation to not be changed by mistake. However, the end users do not know how the internal implementations of the benchmark works, so they may end up messing with those transformations. By freezing the permutation transformation, users cannot mess with it.\n", - "\n", - "Transformations for all transform groups can be frozen at once by using `.freeze_transforms()`. Transformations can be frozen for a single group by using `.freeze_group_transforms(group_name)`. As always, those methods return a view of the original dataset.\n", - "\n", - "The cell below shows a simplified excerpt from the [PermutedMNIST benchmark implementation](https://github.com/ContinualAI/avalanche/blob/master/avalanche/benchmarks/classic/cmnist.py). First, a *PixelsPermutation* instance is created. That instance is a transformation that will permute the pixels of the input image. We then create the train end test sets. Once created, transformations for those datasets are frozen using `.freeze_transforms()`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "af97e04b-01b4-4a32-ba1f-42ed94977c56", - "metadata": {}, - "outputs": [], - "source": [ - "from avalanche.benchmarks.classic.cmnist import PixelsPermutation\n", - "import numpy as np\n", - "import torch\n", - "\n", - "# Instantiate MNIST train and test sets\n", - "mnist_train = MNIST('mnist_data', train=True, download=True)\n", - "mnist_test = MNIST('mnist_data', train=False, download=True)\n", - " \n", - "# Define the transformation used to permute the pixels\n", - "rng_seed = 4321\n", - "rng_permute = np.random.RandomState(rng_seed)\n", - "idx_permute = torch.from_numpy(rng_permute.permutation(784)).type(torch.int64)\n", - "permutation_transform = PixelsPermutation(idx_permute)\n", - "\n", - "# Define the transforms group\n", - "perm_group_transforms = dict(\n", - " train=(permutation_transform, None),\n", - " eval=(permutation_transform, None)\n", - ")\n", - "\n", - "# Create the datasets and freeze transforms\n", - "# Note: one can call \"freeze_transforms\" on constructor result\n", - "# or you can do this in 2 steps. The result is the same (obviously).\n", - "# The next part show both ways:\n", - "\n", - "# Train set\n", - "permuted_train_set = AvalancheDataset(\n", - " mnist_train, \n", - " transform_groups=perm_group_transforms).freeze_transforms()\n", - "\n", - "# Test set\n", - "permuted_test_set = AvalancheDataset(\n", - " mnist_test, transform_groups=perm_group_transforms, \n", - " initial_transform_group='eval')\n", - "permuted_test_set = permuted_test_set.freeze_transforms()" - ] - }, - { - "cell_type": "markdown", - "id": "3f5fea92-cb21-4144-bee8-6e2b37606d25", - "metadata": {}, - "source": [ - "In this way, that transform can't be removed. However, remember that one can always append other transforms atop of frozen transforms.\n", - "\n", - "The cell below shows that `replace_transforms` can't remove frozen transformations:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "62ae4c3e-da05-494c-9df4-394774a42908", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Before replace_transforms:\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "After replace_transforms:\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAEP0lEQVR4nO2dTYhOYRTHzzWDBTFZ+WrIR5ShJoXU1DRKkYXJR1HKyoJZKQtNEeWjpqywkCLKAg2lEJms1IiUYSULFpjNGKLJ51j8z9Q9t+e997kz7+u8p85v8+957vP179TpPu99n3sTEvxJF/pFXRtF0gHpi23/BTKzsCEvpEGUJsVOU6+4AW3cgDbmDSQyN7VDHqXrmI2QZ5AhyDfI9Njp5Gyi7gmkrbChXJb5CLgBbdyANuYNNMqk9BgiKpdAHoS6R+dP5lSgriFQl2FruqHMpuYj4Aa0cQPamDeQRLdcAXkNkbnsAhER7cvpfROyPXCpFbIAcrvyGL8gk0Wl+Qi4AW3cgDZJO7QXMit9TWaaUe5Qeax7kE2ha8H9rKi7A9lcefwg5iPgBrRxA9qYN5DJinchZXPZPCIieo9CxB53fPATo7UQzvjmI+AGtHED2pg3EL8nZq5Bdhc2nAbZBrlS2J5/utwAic7F5iPgBrRxA9qYNxCRRvdALkEGIbMhfIO4vnAQuanvISKig6JF2ZvYZRDzEXAD2rgBbcwbCKdRJL0a7M5zfiKN5hWkBWI+Am5AGzegjXkDjbJ4GrIj0PI6ZGdolOK0WzZ/roK8DFxqESXzEXAD2rgBbcwbyNyN8k+e+AH0BQqtoX68IT8DmUNERB9R6ITcqs4CU7yDzIf43y7rBDegjXkD1dwTv4EsFZUXIYsgfEzxCBERHQ/MGT/pD4j5CLgBbdyANuYNjB1F5F1wb+WWbPUv5CtkRvVWMhfyIba9n+iuE9yANm5Am2QY2lSyX+g84BYUnkI+xQ7FUw+XXAGlpzaMG9DGDWhj3kDpv11KJvTgegSyEDI4vhWYj4Ab0MYNaGPeQEQa5Scu9yEj4hqfxDlERESHS86dl4MPQM5BmomIaBcKJ0U38xFwA9q4AW3MG5jg3WjZeX5DRObks5H9oW58PLO58sDmI+AGtHED2pg3EE6jsMVPk9ZBBiDfQ+2PEhHRsSquKwQ/z/oM8bvROsENaGPeQBL946Bs+ByyumQ3BlvpIdGCYhfC+FHEOsENaOMGtKnBnjiYOFdCBqo+m/kIuAFt3IA25g00FjcpS0+oMpQ/b0BC5x7z8FeX1xduQBs3oE1yAtpd2LIJMlyrlfDRxc6S3cxHwA1o4wa0MW8gs6k/D9lPRNV5GVA8/HbM4BOgHMxHwA1o4wa0MW8gk0ZzMuf/TarRmI+AG9DGDWhj3kDt/nb5ELIGUvwJcUZm68uQvUQ09tWzzGfPzEfADWjjBrTJZKHFkLeF/XJu7fhV51ch/BIhvFFIvoKoD4WOwhHzMB8BN6CNG9DGvIGIPXHwHkqCT8fKL8fyyKOFS+DP1S4XlcXZlFO9+Qi4AW3cgDbmDYylUT6aPVTY4SdkCuQspCvdoktckvArKqem6/JuQ8U1fucuf8ybj5abj4Ab0MYNaGPewD8mbYqJbB1JxgAAAABJRU5ErkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# First, show that the image pixels are permuted\n", - "print('Before replace_transforms:')\n", - "display(permuted_train_set[0][0].resize((192, 192), 0))\n", - "\n", - "# Try to remove the permutation\n", - "with_removed_transforms = permuted_train_set.replace_transforms(None, None)\n", - "\n", - "print('After replace_transforms:')\n", - "display(permuted_train_set[0][0].resize((192, 192), 0))\n", - "display(with_removed_transforms[0][0].resize((192, 192), 0))" - ] - }, - { - "cell_type": "markdown", - "id": "553d4633-c7aa-46c4-b2bb-90267b998e74", - "metadata": {}, - "source": [ - "## Transformations wrap-up\n", - "This completes the *Mini How-To* for the functionalities of the *AvalancheDataset* related to **transformations**. \n", - "\n", - "Here you learned how to use **transformation groups** and how to **append/replace/freeze transformations** in a simple way.\n", - "\n", - "Other *Mini How-To*s will guide you through the other functionalities offered by the *AvalancheDataset* class. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanche_dataset_preamble)." - ] - }, - { - "cell_type": "markdown", - "id": "792a197d-a0c3-44c2-87fb-fae43f687195", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "## 🤝 Run it on Google Colab\n", - "\n", - "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations.ipynb)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.8.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/how-tos/avalanche_dataset_preamble.ipynb b/notebooks/how-tos/avalanche_dataset_preamble.ipynb deleted file mode 100644 index ea6adc6d4..000000000 --- a/notebooks/how-tos/avalanche_dataset_preamble.ipynb +++ /dev/null @@ -1,172 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "d369a312", - "metadata": { - "jp-MarkdownHeadingCollapsed": true, - "pycharm": { - "name": "#%% md\n" - }, - "tags": [] - }, - "source": [ - "---\n", - "description: Dealing with AvalancheDatasets\n", - "---\n", - "\n", - "# The AvalancheDataset\n", - "\n", - "The `AvalancheDataset` is an implementation of the PyTorch `Dataset` class that comes with many useful out-of-the-box functionalities. For most users, the *AvalancheDataset* can be used as a plain PyTorch Dataset that will return `x, y, t` elements. However, the AvalancheDataset is much more powerful than a simple PyTorch Dataset. \n", - "\n", - "\n", - "**A serie of _Mini How-Tos_** will guide you through the functionalities of the *AvalancheDataset* and its subclasses:\n", - "\n", - "- [The AvalancheDataset](https://avalanche.continualai.org/how-tos/avalanche_dataset_preamble) (this page)\n", - " - [Preamble](#🎯-Preamble:-Non-Avalanche-PyTorch-Datasets)\n", - "- [Creating AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanche_dataset_how_to/avalanche_dataset_creation)\n", - "- [Advanced Transformations](https://avalanche.continualai.org/how-tos/avalanche_dataset_how_to/avalanche_dataset_transformations)\n", - "\n", - "However, brefore jumping to the *Mini How-To*s, **we recommend having a look at the basic notions of Dataset and DataLoader by reading the following short preamble**:" - ] - }, - { - "cell_type": "markdown", - "id": "ccdb4767", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "## 🎯 Preamble: Non-Avalanche PyTorch Datasets\n", - "This short preamble will briefly go through the basic notions of Dataset offered natively by PyTorch. A solid grasp of these notions are needed to understand:\n", - "1. How PyTorch data loading works in general\n", - "2. How AvalancheDatasets differs from PyTorch Datasets\n", - "\n", - "### 📚 Dataset: general definition\n", - "\n", - "In PyTorch, **a `Dataset` is a class** exposing two methods:\n", - "- `__len__()`, which returns the amount of instances in the dataset (as an `int`). \n", - "- `__getitem__(idx)`, which returns the data point at index `idx`.\n", - "\n", - "In other words, a Dataset instance is just an object for which, similarly to a list, one can simply:\n", - "- Obtain its length using the Python `len(dataset)` function.\n", - "- Obtain a single data point using the `x, y = dataset[idx]` syntax.\n", - "\n", - "The content of the dataset can be either loaded in memory when the dataset is instantiated (like the torchvision MNIST dataset does) or, for big datasets like ImageNet, the content is kept on disk, with the dataset keeping the list of files in an internal field. In this case, data is loaded from the storage on-the-fly when `__getitem__(idx)` is called. The way those things are managed is specific to each dataset implementation.\n", - "\n", - "### PyTorch Datasets\n", - "The PyTorch library offers 4 Dataset implementations:\n", - "- `Dataset`: an interface defining the `__len__` and `__getitem__` methods.\n", - "- `TensorDataset`: instantiated by passing X and Y tensors. Each row of the X and Y tensors is interpreted as a data point. The `__getitem__(idx)` method will simply return the `idx`-th row of X and Y tensors.\n", - "- `ConcatDataset`: instantiated by passing a list of datasets. The resulting dataset is a concatenation of those datasets.\n", - "- `Subset`: instantiated by passing a dataset and a list of indices. The resulting dataset will only contain the data points described by that list of indices.\n", - "\n", - "As explained in the mini *How-To*s, Avalanche offers a customized version for all these 4 datasets.\n", - "\n", - "### Transformations\n", - "Most datasets from the *torchvision* libraries (as well as datasets found \"in the wild\") allow for a `transformation` function to be passed to the dataset constructor. The support for transformations is not mandatory for a dataset, but it is quite common to support them. The transformation is used to process the X value of a data point before returning it. This is used to normalize values, apply augmentations, etcetera.\n", - "\n", - "As explained in the mini *How-To*s, the `AvalancheDataset` class implements a very rich and powerful set of functionalities for managing transformations.\n", - "\n", - "### Quick note on the IterableDataset class\n", - "A variation of the standard `Dataset` exist in PyTorch: the [IterableDataset](https://pytorch.org/docs/stable/data.html#iterable-style-datasets). When using an `IterableDataset`, one can load the data points in a sequential way only (by using a tape-alike approach). The `dataset[idx]` syntax and `len(dataset)` function are not allowed. **Avalanche does NOT support `IterableDataset`s.** You shouldn't worry about this because, realistically, you will never encounter such datasets.\n", - "\n", - "## DataLoader\n", - "The `Dataset` is a very simple object that only returns one data point given its index. In order to create minibatches and speed-up the data loading process, a `DataLoader` is required.\n", - "\n", - "The PyTorch `DataLoader` class is a very efficient mechanism that, given a `Dataset`, will return **minibatches** by optonally **shuffling** data brefore each epoch and by **loading data in parallel** by using multiple workers.\n", - "\n", - "## Preamble wrap-up\n", - "To wrap-up, let's see how the native, *non-Avalanche*, PyTorch components work in practice. In the following code we create a `TensorDataset` and then we load it in minibatches using a `DataLoader`." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "8bce4be3-91ef-4816-9a3f-5c392ef05027", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n", - "Loaded minibatch of 10 instances\n" - ] - } - ], - "source": [ - "import torch\n", - "from torch.utils.data.dataset import TensorDataset\n", - "from torch.utils.data.dataloader import DataLoader\n", - "\n", - "# Create a dataset of 100 data points described by 22 features + 1 class label\n", - "x_data = torch.rand(100, 22)\n", - "y_data = torch.randint(0, 5, (100,))\n", - "\n", - "# Create the Dataset\n", - "my_dataset = TensorDataset(x_data, y_data)\n", - "\n", - "# Create the DataLoader\n", - "my_dataloader = DataLoader(my_dataset, batch_size=10, shuffle=True, num_workers=4)\n", - "\n", - "# Run one epoch\n", - "for x_minibatch, y_minibatch in my_dataloader:\n", - " print('Loaded minibatch of', len(x_minibatch), 'instances')\n", - "# Output: \"Loaded minibatch of 10 instances\" x10 times" - ] - }, - { - "cell_type": "markdown", - "id": "93f6fdec-f1d0-4cdc-a6e0-6e7c0a6b3be7", - "metadata": {}, - "source": [ - "## Next steps\n", - "With these notions in mind, you can start start your journey on understanding the functionalities offered by the AvalancheDatasets by going through the *Mini How-To*s.\n", - "\n", - "Refer to the index found at the beginning of this page for the complete [list of the *Mini How-To*s regarding AvalancheDatasets](#The-AvalancheDataset)." - ] - }, - { - "cell_type": "markdown", - "id": "02de0ce4-2711-4832-8b0e-516040483ae5", - "metadata": {}, - "source": [ - "## 🤝 Run it on Google Colab\n", - "\n", - "You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanche_dataset_preamble.ipynb)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.8.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 11e374f97cb699e6f0cd88a7f7b86d42bb1e346b Mon Sep 17 00:00:00 2001 From: ContinualAI-bot Date: Mon, 29 Nov 2021 18:55:02 +0000 Subject: [PATCH 28/60] Update gitbook documentation --- .../how-tos/avalanchedataset/README.md | 13 + .../advanced-transformations.md | 323 ++++++++++++++++++ .../creating-avalanchedatasets.md | 229 +++++++++++++ .../preamble-pytorch-datasets.md | 74 ++++ 4 files changed, 639 insertions(+) diff --git a/docs/gitbook/how-tos/avalanchedataset/README.md b/docs/gitbook/how-tos/avalanchedataset/README.md index 144d54f17..0fa3820b4 100644 --- a/docs/gitbook/how-tos/avalanchedataset/README.md +++ b/docs/gitbook/how-tos/avalanchedataset/README.md @@ -1,2 +1,15 @@ +--- +description: Dealing with AvalancheDatasets +--- + # AvalancheDataset +The `AvalancheDataset` is an implementation of the PyTorch `Dataset` class that comes with many useful out-of-the-box functionalities. For most users, the *AvalancheDataset* can be used as a plain PyTorch Dataset that will return `x, y, t` elements. However, the AvalancheDataset is much more powerful than a simple PyTorch Dataset. + +**A serie of _Mini How-Tos_** will guide you through the functionalities of the *AvalancheDataset* and its subclasses: + +- [Preamble: PyTorch Datasets](https://avalanche.continualai.org/how-tos/avalanchedataset/preamble-pytorch-datasets) +- [Creating AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanchedataset/creating-avalanchedatasets) +- [Advanced Transformations](https://avalanche.continualai.org/how-tos/avalanchedataset/advanced-transformations) + +Brefore jumping to the actual *Mini How-To*s, **we recommend having a look at the basic notions of Dataset and DataLoader by reading the [Preamble page](https://avalanche.continualai.org/how-tos/avalanchedataset/preamble-pytorch-datasets)**. diff --git a/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md b/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md index 0a1d503fb..c67d6a145 100644 --- a/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md +++ b/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md @@ -1,2 +1,325 @@ +--- +description: Dealing with transformations (groups, appending, replacing, freezing). +--- + # Advanced Transformations +AvalancheDataset (and its subclasses like the Avalanche*Tensor/Subset/Concat*Dataset) allow for a finer control over transformations. While torchvision (and other) datasets allow for a minimal mechanism to apply transformations, with AvalancheDataset one can: +1. Have multiple **transformation "groups"** in the same dataset (like separated train and test transformations). +2. **Append, replace and remove transformations**, even by using nested Subset/Concat Datasets. +3. **Freeze transformations**, so that they can't be changed. + +The following sub-sections show examples on how to use these features. Please note that all the constructor parameters and the methods described in this How-To can be used on AvalancheDataset subclasses as well. For more info on all the available subclasses, refer to [this Mini How-To](https://avalanche.continualai.org/how-tos/avalanchedataset/creating-avalanchedatasets). + +It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page). + +Let's start by installing Avalanche: + + +```python +!pip install git+https://github.com/ContinualAI/avalanche.git + +# Or, if you cloned Avalanche on your PC: +# import sys +# from pathlib import Path +# sys.path.append(str(Path.cwd() / '../../..')) +``` + +## Transformation groups +AvalancheDatasets can contain multiple **transformation groups**. This can be useful to keep train and test transformations in the same dataset and to have different set of transformations. This may come in handy in many situations (for instance, to apply ad-hoc transformations to replay data). + +As in torchvision datasets, AvalancheDataset supports the two kind of transformations: the `transform`, which is applied to X values, and the `target_transform`, which is applied to Y values. The latter is rarely used. This means that **a transformation group is a pair of transformations to be applied to the X and Y values** of each instance returned by the dataset. In both torchvision and Avalanche implementations, **a transformation must be a function (or other callable object)** that accepts one input (the X or Y value) and outputs its transformed version. This pair of functions is stored in the `transform` and `target_transform` fields of the dataset. A comprehensive guide on transformations can be found in the [torchvision documentation](https://pytorch.org/vision/stable/transforms.html). + +In the following example, a MNIST dataset is created and then wrapped in an AvalancheDataset. When creating the AvalancheDataset, we can set *train* and *eval* transformations by passing a *transform\_groups* parameter. Train transformations usually include some form of random augmentation, while eval transformations usually include a sequence of deterministic transformations only. Here we define the sequence of train transformations as a random rotation followed by the ToTensor operation. The eval transformations only include the ToTensor operation. + + +```python +from torchvision import transforms +from torchvision.datasets import MNIST +from avalanche.benchmarks.utils import AvalancheDataset + +mnist_dataset = MNIST('mnist_data', download=True) + +# Define the training transformation for X values +train_transformation = transforms.Compose([ + transforms.RandomRotation(45), + transforms.ToTensor(), +]) +# Define the training transformation for Y values (rarely used) +train_target_transformation = None + +# Define the test transformation for X values +eval_transformation = transforms.ToTensor() +# Define the test transformation for Y values (rarely used) +eval_target_transformation = None + +transform_groups = { + 'train': (train_transformation, train_target_transformation), + 'eval': (eval_transformation, eval_target_transformation) +} + +avl_mnist_transform = AvalancheDataset(mnist_dataset, transform_groups=transform_groups) +``` + +Of course, one can also just use the `transform` and `target_transform` constructor parameters to set the transformations for both the *train* and the *eval* groups. However, it is recommended to use the approach based on *transform\_groups* (shown in the code above) as it is much more flexible. + + +```python +# Not recommended: use transform_groups instead +avl_mnist_same_transforms = AvalancheDataset(mnist_dataset, transform=train_transformation) +``` + +### Using `.train()` and `.eval()` + +**The default behaviour of the AvalancheDataset is to use transformations from the _train_ group.** However, one can easily obtain a version of the dataset where the *eval* group is used. Note: when obtaining the dataset of experiences from the test stream, those datasets will already be using the *eval* group of transformations so you don't need to switch to the eval group ;). + +As noted before, transformations for the current group are loaded in the `transform` and `target_transform` fields. These fields can be changed directly, but this is *NOT* recommended, as this will not create a copy of the dataset and may probably affect other parts of the code in which the dataset is used. + +The recommended way to switch between the *train* and *eval* groups is to use the `.train()` and `.eval()` methods to obtain a copy (view) of the dataset with the proper transformations enabled. This is another very handy feature of the AvalancheDataset: **methods that manipulate the AvalancheDataset fields (and transformations) always create a view of the dataset. The original dataset is never changed.** + +In the following cell we use the *avl\_mnist\_transform* dataset created in the cells above. We first obtain a view of it in which *eval* transformations are enabled. Then, starting from this view, we obtain a version of it in which *train* transformations are enabled. We want to double-stress that `.train()` and `.eval()` never change the group of the dataset on which they are called: they always create a view. + +One can check that the correct transformation group is in use by looking at the content of the *transform/target_transform* fields. + + +```python +# Obtain a view of the dataset in which eval transformations are enabled +avl_mnist_eval = avl_mnist_transform.eval() + +# Obtain a view of the dataset in which we get back to train transforms +# Basically, avl_mnist_transform ~= avl_mnist_train +avl_mnist_train = avl_mnist_eval.train() + +# Check the current transformations function for the 3 datasets +print('Original dataset transformation:', avl_mnist_transform.transform) +# Output: +# Original dataset transformation: Compose( +# RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0) +# ToTensor() +# ) +print('--------------------------------') +print('Eval version of the dataset:', avl_mnist_eval.transform) +# Output: "Eval version of the dataset: ToTensor()" +print('--------------------------------') +print('Back to train transformations:', avl_mnist_train.transform) +# Output: +# Back to train transformations: Compose( +# RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0) +# ToTensor() +# ) +``` + +### Custom transformation groups +In *AvalancheDataset*s the **_train_ and _eval_ transformation groups are always available**. However, *AvalancheDataset* also supports **custom transformation groups**. + +The following example shows how to create an AvalancheDataset with an additional group named *replay*. We define the replay transformation as a random crop followed by the ToTensor operation. + + +```python +replay_transform = transforms.Compose([ + transforms.RandomCrop(28, padding=4), + transforms.ToTensor() +]) + +replay_target_transform = None + +transform_groups_with_replay = { + 'train': (None, None), + 'eval': (None, None), + 'replay': (replay_transform, replay_target_transform) +} + +AvalancheDataset(mnist_dataset, transform_groups=transform_groups_with_replay) +``` + +However, once created the dataset will use the *train* group. There are two ways to **switch to our custom group**: +- Set the group when creating the dataset using the `initial_transform_group` constructor parameter +- Switch to the group using the `.with_transforms(group_name)` method + +The `.with_transforms(group_name)` method behaves in the same way `.train()` and `.eval()` do by creating a view of the original dataset. + +The following example shows how to use both methods: + + +```python +# Method 1: create the dataset with "replay" as the default group +avl_mnist_custom_transform_1 = AvalancheDataset( + mnist_dataset, + transform_groups=transform_groups_with_replay, + initial_transform_group='replay') + +print(avl_mnist_custom_transform_1.transform) + +# Method 2: switch to "replay" using `.with_transforms(group_name)` +avl_mnist_custom_transform_not_enabled = AvalancheDataset( + mnist_dataset, + transform_groups=transform_groups_with_replay) + +avl_mnist_custom_transform_2 = avl_mnist_custom_transform_not_enabled.with_transforms('replay') +print(avl_mnist_custom_transform_2.transform) + +# Both prints output: +# Compose( +# RandomCrop(size=(28, 28), padding=4) +# ToTensor() +# ) +``` + +## Appending transformations + +In the standard torchvision datasets the only way to append (that is, add a new transformation step to the list of existing one) is to change the *transform* field directly by doing something like this: + + +```python +# Append a transform by using torchvision datasets (>>> DON'T DO THIS! <<<) + +# Create the dataset +mnist_dataset_w_totensor = MNIST('mnist_data', download=True, transform=transforms.ToTensor()) + +# Append a transform +to_append_transform = transforms.RandomCrop(size=(28, 28), padding=4) +mnist_dataset_w_totensor.transform = transforms.Compose( + [mnist_dataset_w_totensor.transform, to_append_transform] +) +print(mnist_dataset_w_totensor.transform) +# Prints: +# Compose( +# ToTensor() +# RandomCrop(size=(28, 28), padding=4) +# ) +``` + +This solution has many huge drawbacks: +- The transformation field of the dataset is changed directly. This will affect other parts of the code that use that dataset instance. +- If the initial transform is `None`, then `Compose` will not complain, but the process will crash later (try it by yourself: replace the first element of Compose in cell above with `None`, then try obtaining a data point from the dataset). +- If you need to change transformations only temporarly to do some specific things in a limited part of the code, then you need to store the previous set of transformations in some variable in order to switch back to them later. + +AvalancheDataset offers a very simple method to append transformations without incurring in those issues. The `.add_transforms(transform=None, target_transform=None)` method will append the given transform(s) **to the currently enabled transform group** and will return a new (a view actually) dataset with given transformations appended to the existing ones. The original dataset is not affected. One can also use `.add_transforms_to_group(group_name, transform, target_transform)` to change transformations for a different group. + +The next cell shows how to use `.add_transforms(...)` to append the *to\_append\_transform* transform defined in the cell above. + + +```python +# Create the dataset +avl_mnist = AvalancheDataset(MNIST('mnist_data', download=True), transform=transforms.ToTensor()) + +# Append a transformation. Simple as: +avl_mnist_appended_transform = avl_mnist.add_transforms(to_append_transform) + +print('With appended transforms:', avl_mnist_appended_transform.transform) +# Prints: +# With appended transforms: Compose( +# ToTensor() +# RandomCrop(size=(28, 28), padding=4) +# ) + +# Check that the original dataset was not affected: +print('Original dataset:', avl_mnist.transform) +# Prints: "Original dataset: ToTensor()" +``` + +Note that by using `.add_transforms(...)`: + +- The original dataset is not changed, which means that other parts of the code that use that dataset instance are not affected. +- You don't need to worry about *None* transformations. +- In order to revert to the original transformations you don't need to keep a copy of them: the original dataset is not affected! + +## Replacing transformations + +The replacement operation follows the same idea (and benefits) of the append one. By using `.replace_transforms(transform, target_transform)` one can obtain a view of the original dataset in which the **transformaations for the current group** are replaced with the given ones. One may also change tranformations for other groups by passing the name of the group as the optional parameter `group`. As with any transform-related operation, the original dataset is not affected. + +Note: one can use `.replace_transforms(...)` to remove previous transformations (by passing `None` as the new transform). + +The following cell shows how to use `.replace_transforms(...)` to replace the transformations of the current group: + + +```python +new_transform = transforms.RandomCrop(size=(28, 28), padding=4) + +# Append a transformation. Simple as: +avl_mnist_replaced_transform = avl_mnist.replace_transforms(new_transform, None) + +print('With replaced transform:', avl_mnist_replaced_transform.transform) +# Prints: "With replaces transforms: RandomCrop(size=(28, 28), padding=4)" + +# Check that the original dataset was not affected: +print('Original dataset:', avl_mnist.transform) +# Prints: "Original dataset: ToTensor()" +``` + +## Freezing transformations + +One last functionality regarding transformations is the ability to "freeze" transformations. Freezing transformations menas **permanently glueing transformations to the dataset so that they can't be replaced or changed in any way** (usually by mistake). Frozen transformations cannot be changed by using `.replace_transforms(...)` or even by changing the `transform` field directly. + +One may wonder when this may come in handy... in fact, you will probably rarely need to freeze transformations. However, imagine having to instantiate the PermutedMNIST benchmark. You want the permutation transformation to not be changed by mistake. However, the end users do not know how the internal implementations of the benchmark works, so they may end up messing with those transformations. By freezing the permutation transformation, users cannot mess with it. + +Transformations for all transform groups can be frozen at once by using `.freeze_transforms()`. Transformations can be frozen for a single group by using `.freeze_group_transforms(group_name)`. As always, those methods return a view of the original dataset. + +The cell below shows a simplified excerpt from the [PermutedMNIST benchmark implementation](https://github.com/ContinualAI/avalanche/blob/master/avalanche/benchmarks/classic/cmnist.py). First, a *PixelsPermutation* instance is created. That instance is a transformation that will permute the pixels of the input image. We then create the train end test sets. Once created, transformations for those datasets are frozen using `.freeze_transforms()`. + + +```python +from avalanche.benchmarks.classic.cmnist import PixelsPermutation +import numpy as np +import torch + +# Instantiate MNIST train and test sets +mnist_train = MNIST('mnist_data', train=True, download=True) +mnist_test = MNIST('mnist_data', train=False, download=True) + +# Define the transformation used to permute the pixels +rng_seed = 4321 +rng_permute = np.random.RandomState(rng_seed) +idx_permute = torch.from_numpy(rng_permute.permutation(784)).type(torch.int64) +permutation_transform = PixelsPermutation(idx_permute) + +# Define the transforms group +perm_group_transforms = dict( + train=(permutation_transform, None), + eval=(permutation_transform, None) +) + +# Create the datasets and freeze transforms +# Note: one can call "freeze_transforms" on constructor result +# or you can do this in 2 steps. The result is the same (obviously). +# The next part show both ways: + +# Train set +permuted_train_set = AvalancheDataset( + mnist_train, + transform_groups=perm_group_transforms).freeze_transforms() + +# Test set +permuted_test_set = AvalancheDataset( + mnist_test, transform_groups=perm_group_transforms, + initial_transform_group='eval') +permuted_test_set = permuted_test_set.freeze_transforms() +``` + +In this way, that transform can't be removed. However, remember that one can always append other transforms atop of frozen transforms. + +The cell below shows that `replace_transforms` can't remove frozen transformations: + + +```python +# First, show that the image pixels are permuted +print('Before replace_transforms:') +display(permuted_train_set[0][0].resize((192, 192), 0)) + +# Try to remove the permutation +with_removed_transforms = permuted_train_set.replace_transforms(None, None) + +print('After replace_transforms:') +display(permuted_train_set[0][0].resize((192, 192), 0)) +display(with_removed_transforms[0][0].resize((192, 192), 0)) +``` + +## Transformations wrap-up +This completes the *Mini How-To* for the functionalities of the *AvalancheDataset* related to **transformations**. + +Here you learned how to use **transformation groups** and how to **append/replace/freeze transformations** in a simple way. + +Other *Mini How-To*s will guide you through the other functionalities offered by the *AvalancheDataset* class. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanchedataset). + +## 🤝 Run it on Google Colab +You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanchedataset/advanced-transformations.ipynb) diff --git a/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md b/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md index 511949e26..07ca936f4 100644 --- a/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md +++ b/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md @@ -1,2 +1,231 @@ +--- +description: Creation and manipulation of AvalancheDatasets and its subclasses. +--- + # Creating AvalancheDatasets +The *AvalancheDataset* is an implementation of the PyTorch Dataset class which comes with many out-of-the-box functionalities. The *AvalancheDataset* (an its few subclass) are extensively used through the whole Avalanche library as the reference way to manipulate datasets: + +- The dataset carried by the `experience.dataset` field is always an *AvalancheDataset*. +- Benchmark creation functions accept *AvalancheDataset*s to create benchmarks where a finer control over task labels is required. +- Internally, benchmarks are created by manipulating *AvalancheDataset*s. + +This first *Mini How-To* will guide through the main ways you can use to **instantiate an _AvalancheDataset_** while the **other Mini How-Tos ([complete list here](https://avalanche.continualai.org/how-tos/avalanchedataset)) will show how to use its functionalities**. + +It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page). + +Let's start by installing avalanche: + + +```python +!pip install git+https://github.com/ContinualAI/avalanche.git + +# Or, if you cloned Avalanche on your PC: +# import sys +# from pathlib import Path +# sys.path.append(str(Path.cwd() / '../../..')) +``` + +## AvalancheDataset vs PyTorch Dataset +This mini How-To will guide you through the main ways used to instantiate an *AvalancheDataset*. + +First thing: the base class `AvalancheDataset` is a **wrapper for existing datasets**. Only two things must be considered when wrapping an existing dataset: + +- Apart from the x and y values, the resulting AvalancheDataset will also return a third value: the task label (which defaults to 0). +- The wrapped dataset must contain a valid **targets** field. + +The **targets field** is available is nearly all *torchvision* datasets. It must be a list containing the label for each data point (usually the y value). In this way, Avalanche can use that field when instantiating benchmarks like the "Class/Task-Incremental* and *Domain-Incremental* ones. + +Avalanche exposes 4 classes of *AvalancheDataset*s which map exactly the 4 *Dataset* classes offered by PyTorch: +- `AvalancheDataset`: the base class, which acts a wrapper to existing *Dataset* instances. +- `AvalancheTensorDataset`: equivalent to PyTorch `TesnsorDataset`. +- `AvalancheSubset`: equivalent to PyTorch `Subset`. +- `AvalancheConcatDataset`: equivalent to PyTorch `ConcatDataset`. + +## 🛠️ Create an AvalancheDataset +Given a dataset (like MNIST), an *AvalancheDataset* can be instantiated as follows: + + +```python +from avalanche.benchmarks.utils import AvalancheDataset +from torchvision.datasets import MNIST + +# Instantiate the MNIST train dataset from torchvision +mnist_dataset = MNIST('mnist_data', download=True) + +# Create the AvalancheDataset +mnist_avalanche_dataset = AvalancheDataset(mnist_dataset) +``` + +Just like any other Dataset, a data point can be obtained using the `x, y = dataset[idx]` syntax. **When obtaining a data point from an AvalancheDataset, an additional third value (the task label) will be returned**: + + +```python +# Obtain the first instance from the original dataset +x, y = mnist_dataset[0] +print(f'x={x}, y={y}') +# Output: "x=, y=5" + +# Obtain the first instance from the AvalancheDataset +x, y, t = mnist_avalanche_dataset[0] +print(f'x={x}, y={y}, t={t}') +# Output: "x=, y=5, t=0" +``` + +**Useful tip:** if you are not sure if you are dealing with a PyTorch *Dataset* or an *AvalancheDataset*, or if you want to ignore task labels, you can use this syntax: + + +```python +# You can use "x, y, *_" to manage both kinds of Datasets +x, y, *_ = mnist_dataset[0] # OK +x, y, *_ = mnist_avalanche_dataset[0] # OK +``` + +## The AvalancheTensorDataset +The PyTorch *TensorDataset* is one of the most useful Dataset classes as it can be used to quickly prototype the data loading part of your code. + +A *TensorDataset* can be wrapped in an AvalancheDataset just like any Dataset, but this is not much convenient, as shown below: + + +```python +import torch +from torch.utils.data import TensorDataset + + +# Create 10 instances described by 7 features +x_data = torch.rand(10, 7) + +# Create the class labels for the 10 instances +y_data = torch.randint(0, 5, (10,)) + +# Create the tensor dataset +tensor_dataset = TensorDataset(x_data, y_data) + +# Wrap it in an AvalancheDataset +wrapped_tensor_dataset = AvalancheDataset(tensor_dataset) + +# Obtain the first instance from the dataset +x, y, t = wrapped_tensor_dataset[0] +print(f'x={x}, y={y}, t={t}') +# Output: "x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0" +``` + +**Instead, it is recommended to use the AvalancheTensorDataset** class to get the same result. In this way, you can just skip one intermediate step. + + +```python +from avalanche.benchmarks.utils import AvalancheTensorDataset + +# Create the tensor dataset +avl_tensor_dataset = AvalancheTensorDataset(x_data, y_data) + +# Obtain the first instance from the AvalancheTensorDataset +x, y, t = avl_tensor_dataset[0] +print(f'x={x}, y={y}, t={t}') +# Output: "x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0" +``` + +In both cases, **AvalancheDataset will automatically populate its _targets_ field by using the values from the second Tensor** (which usually contains the Y values). This behaviour can be customized by passing a custom `targets` constructor parameter (by either passing a list of targets or the index of the Tensor to use). + +The cell below shows the content of the target field of the dataset created in the cell above. Notice that the *targets* field has been filled with the content of the second Tensor (*y\_data*). + + +```python +# Check the targets field +print('y_data=', y_data) + # Output: "y_data= tensor([4, 3, 3, 2, 0, 1, 3, 3, 3, 2])" + +print('targets field=', avl_tensor_dataset.targets) +# Output: "targets field= [tensor(4), tensor(3), tensor(3), tensor(2), +# tensor(0), tensor(1), tensor(3), tensor(3), tensor(3), tensor(2)]" +``` + +## The AvalancheSubset and AvalancheConcatDataset classes +Avalanche offers the `AvalancheSubset` and `AvalancheConcatDataset` implementations that extend the functionalities of PyTorch *Subset* and *ConcatDataset*. + +Regarding the subsetting operation, `AvalancheSubset` behaves in the same way the PyTorch `Subset` class does: both implementations accept a dataset and a list of indices as parameters. The resulting Subset is not a copy of the dataset, it's just a view. This is similar to creating a view of a NumPy array by passing a list of indexes using the `numpy_array[list_of_indices]` syntax. This can be used to both *create a smaller dataset* and to *change the order of data points* in the dataset. + +Here we create a toy dataset in which each X and Y values are *int*s. We then obtain a subset of it by creating an **AvalancheSubset**: + + +```python +from avalanche.benchmarks.utils import AvalancheSubset + +# Define the X values of 10 instances (each instance is an int) +x_data_toy = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59] + +# Define the class labels for the 10 instances +y_data_toy = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + +# Create the tensor dataset +# Note: AvalancheSubset can also be applied to PyTorch TensorDataset directly! +# However, note that PyTorch TensorDataset doesn't support Python lists... +# ... (it only supports Tensors) while AvalancheTensorDataset does. +toy_dataset = AvalancheTensorDataset(x_data_toy, y_data_toy) + +# Define the indices for the subset +# Here we want to obtain a subset containing only the data points... +# ... at indices 0, 5, 8, 2 (in this specific order) +subset_indices = [0, 5, 8, 2] + +# Create the subset +avl_subset = AvalancheSubset(toy_dataset, indices=subset_indices) +print('The subset contains', len(avl_subset), 'instances.') +# Output: "The subset contains 4 instances." + +# Obtain instances from the AvalancheSubset +for x, y, t in avl_subset: + print(f'x={x}, y={y}, t={t}') +# Output: +# x=50, y=10, t=0 +# x=55, y=15, t=0 +# x=58, y=18, t=0 +# x=52, y=12, t=0 +``` + +Concatenation is even simpler. Just like with PyTorch *ConcatDataset*, one can easily concatentate datasets with **AvalancheConcatDataset**. + +Both *AvalancheConcatDataset* and PyTorch *ConcatDataset* accept a list of datasets to concatenate. + + +```python +from avalanche.benchmarks.utils import AvalancheConcatDataset + +# Define the 2 datasets to be concatenated +x_data_toy_1 = [50, 51, 52, 53, 54] +y_data_toy_1 = [10, 11, 12, 13, 14] +x_data_toy_2 = [60, 61, 62, 63, 64] +y_data_toy_2 = [20, 21, 22, 23, 24] + +# Create the datasets +toy_dataset_1 = AvalancheTensorDataset(x_data_toy_1, y_data_toy_1) +toy_dataset_2 = AvalancheTensorDataset(x_data_toy_2, y_data_toy_2) + +# Create the concat dataset +avl_concat = AvalancheConcatDataset([toy_dataset_1, toy_dataset_2]) +print('The concat dataset contains', len(avl_concat), 'instances.') +# Output: "The concat dataset contains 10 instances." + +# Obtain instances from the AvalancheConcatDataset +for x, y, t in avl_concat: + print(f'x={x}, y={y}, t={t}') +# Output: +# x=51, y=11, t=0 +# x=52, y=12, t=0 +# x=53, y=13, t=0 +# x=54, y=14, t=0 +# x=60, y=20, t=0 +# x=61, y=21, t=0 +# x=62, y=22, t=0 +# x=63, y=23, t=0 +# x=64, y=24, t=0 +``` + +## Dataset Creation wrap-up +This *Mini How-To* showed you how to **create instances of AvalancheDataset (and its subclasses)**. + +Other *Mini How-To*s will guide you through the functionalities offered by AvalancheDataset. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanchedataset). + +## 🤝 Run it on Google Colab + +You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanchedataset/creating-avalanchedatasets.ipynb) diff --git a/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md b/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md index 4293317f8..a23811b7e 100644 --- a/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md +++ b/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md @@ -1,2 +1,76 @@ +--- +description: Few words about PyTorch Datasets +--- + # Preamble: PyTorch Datasets +This short preamble will briefly go through the basic notions of Dataset offered natively by PyTorch. A solid grasp of these notions are needed to understand: +1. How PyTorch data loading works in general +2. How AvalancheDatasets differs from PyTorch Datasets + +## 📚 Dataset: general definition + +In PyTorch, **a `Dataset` is a class** exposing two methods: +- `__len__()`, which returns the amount of instances in the dataset (as an `int`). +- `__getitem__(idx)`, which returns the data point at index `idx`. + +In other words, a Dataset instance is just an object for which, similarly to a list, one can simply: +- Obtain its length using the Python `len(dataset)` function. +- Obtain a single data point using the `x, y = dataset[idx]` syntax. + +The content of the dataset can be either loaded in memory when the dataset is instantiated (like the torchvision MNIST dataset does) or, for big datasets like ImageNet, the content is kept on disk, with the dataset keeping the list of files in an internal field. In this case, data is loaded from the storage on-the-fly when `__getitem__(idx)` is called. The way those things are managed is specific to each dataset implementation. + +## PyTorch Datasets +The PyTorch library offers 4 Dataset implementations: +- `Dataset`: an interface defining the `__len__` and `__getitem__` methods. +- `TensorDataset`: instantiated by passing X and Y tensors. Each row of the X and Y tensors is interpreted as a data point. The `__getitem__(idx)` method will simply return the `idx`-th row of X and Y tensors. +- `ConcatDataset`: instantiated by passing a list of datasets. The resulting dataset is a concatenation of those datasets. +- `Subset`: instantiated by passing a dataset and a list of indices. The resulting dataset will only contain the data points described by that list of indices. + +As explained in the mini *How-To*s, Avalanche offers a customized version for all these 4 datasets. + +## Transformations +Most datasets from the *torchvision* libraries (as well as datasets found "in the wild") allow for a `transformation` function to be passed to the dataset constructor. The support for transformations is not mandatory for a dataset, but it is quite common to support them. The transformation is used to process the X value of a data point before returning it. This is used to normalize values, apply augmentations, etcetera. + +As explained in the mini *How-To*s, the `AvalancheDataset` class implements a very rich and powerful set of functionalities for managing transformations. + +## Quick note on the IterableDataset class +A variation of the standard `Dataset` exist in PyTorch: the [IterableDataset](https://pytorch.org/docs/stable/data.html#iterable-style-datasets). When using an `IterableDataset`, one can load the data points in a sequential way only (by using a tape-alike approach). The `dataset[idx]` syntax and `len(dataset)` function are not allowed. **Avalanche does NOT support `IterableDataset`s.** You shouldn't worry about this because, realistically, you will never encounter such datasets. + +## DataLoader +The `Dataset` is a very simple object that only returns one data point given its index. In order to create minibatches and speed-up the data loading process, a `DataLoader` is required. + +The PyTorch `DataLoader` class is a very efficient mechanism that, given a `Dataset`, will return **minibatches** by optonally **shuffling** data brefore each epoch and by **loading data in parallel** by using multiple workers. + +## Preamble wrap-up +To wrap-up, let's see how the native, *non-Avalanche*, PyTorch components work in practice. In the following code we create a `TensorDataset` and then we load it in minibatches using a `DataLoader`. + + +```python +import torch +from torch.utils.data.dataset import TensorDataset +from torch.utils.data.dataloader import DataLoader + +# Create a dataset of 100 data points described by 22 features + 1 class label +x_data = torch.rand(100, 22) +y_data = torch.randint(0, 5, (100,)) + +# Create the Dataset +my_dataset = TensorDataset(x_data, y_data) + +# Create the DataLoader +my_dataloader = DataLoader(my_dataset, batch_size=10, shuffle=True, num_workers=4) + +# Run one epoch +for x_minibatch, y_minibatch in my_dataloader: + print('Loaded minibatch of', len(x_minibatch), 'instances') +# Output: "Loaded minibatch of 10 instances" x10 times +``` + +## Next steps +With these notions in mind, you can start start your journey on understanding the functionalities offered by the AvalancheDatasets by going through the *Mini How-To*s. + +Please refer to the [list of the *Mini How-To*s regarding AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanchedataset) for a complete list. It is recommended to start with the **"Creating AvalancheDatasets"** *Mini How-To*. + +## 🤝 Run it on Google Colab +You can run _this chapter_ and play with it on Google Colaboratory by clicking here: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/how-tos/avalanchedataset/preamble-pytorch-datasets.ipynb) From f25043b207a57d71c2a63254c497b3b7c128d8ab Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Tue, 30 Nov 2021 11:42:06 +0100 Subject: [PATCH 29/60] update notebooks --- .../04_training.ipynb | 77 ++++--- .../how-tos/dataloading_buffers_replay.ipynb | 217 +++++++++--------- 2 files changed, 153 insertions(+), 141 deletions(-) diff --git a/notebooks/from-zero-to-hero-tutorial/04_training.ipynb b/notebooks/from-zero-to-hero-tutorial/04_training.ipynb index dbeda1573..92e48b45e 100644 --- a/notebooks/from-zero-to-hero-tutorial/04_training.ipynb +++ b/notebooks/from-zero-to-hero-tutorial/04_training.ipynb @@ -13,7 +13,9 @@ "---\n", "# Training\n", "\n", - "Welcome to the \"_Training_\" tutorial of the \"_From Zero to Hero_\" series. In this part we will present the functionalities offered by the `training` module." + "Welcome to the \"_Training_\" tutorial of the \"_From Zero to Hero_\" series. In this part we will present the functionalities offered by the `training` module.\n", + "\n", + "First, let's install Avalanche. You can skip this step if you have installed it already." ] }, { @@ -33,9 +35,9 @@ " Cloning https://github.com/ContinualAI/avalanche.git to /tmp/pip-req-build-f00959wq\r\n", " Running command git clone -q https://github.com/ContinualAI/avalanche.git /tmp/pip-req-build-f00959wq\r\n", "^C\r\n", - "\u001B[31mERROR: Operation cancelled by user\u001B[0m\r\n", - "\u001B[33mWARNING: You are using pip version 21.2.4; however, version 21.3 is available.\r\n", - "You should consider upgrading via the '/home/carta/anaconda3/envs/avalanche/bin/python -m pip install --upgrade pip' command.\u001B[0m\r\n" + "\u001b[31mERROR: Operation cancelled by user\u001b[0m\r\n", + "\u001b[33mWARNING: You are using pip version 21.2.4; however, version 21.3 is available.\r\n", + "You should consider upgrading via the '/home/carta/anaconda3/envs/avalanche/bin/python -m pip install --upgrade pip' command.\u001b[0m\r\n" ] } ], @@ -435,6 +437,11 @@ { "cell_type": "code", "execution_count": 3, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "from avalanche.training.plugins import EarlyStoppingPlugin\n", @@ -442,29 +449,27 @@ "strategy = Naive(\n", " model, optimizer, criterion,\n", " plugins=[EarlyStoppingPlugin(patience=10, val_stream_name='train')])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "In Avalanche, most continual learning strategies are implemented using plugins, which makes it easy to combine them together. For example, it is extremely easy to create a hybrid strategy that combines replay and EWC together by passing the appropriate `plugins` list to the `BaseStrategy`:" - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "In Avalanche, most continual learning strategies are implemented using plugins, which makes it easy to combine them together. For example, it is extremely easy to create a hybrid strategy that combines replay and EWC together by passing the appropriate `plugins` list to the `BaseStrategy`:" + ] }, { "cell_type": "code", "execution_count": 4, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "from avalanche.training.strategies import BaseStrategy\n", @@ -475,25 +480,18 @@ "strategy = BaseStrategy(\n", " model, optimizer, criterion,\n", " plugins=[replay, ewc])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "Beware that most strategy plugins modify the internal state. As a result, not all the strategy plugins can be combined together. For example, it does not make sense to use multiple replay plugins since they will try to modify the same strategy variables (mini-batches, dataloaders), and therefore they will be in conflict." - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "Beware that most strategy plugins modify the internal state. As a result, not all the strategy plugins can be combined together. For example, it does not make sense to use multiple replay plugins since they will try to modify the same strategy variables (mini-batches, dataloaders), and therefore they will be in conflict." + ] }, { "cell_type": "markdown", @@ -580,7 +578,7 @@ "- `self.experience`: the current experience.\n", "- `self.adapted_dataset`: the data modified by the dataset adaptation phase.\n", "- `self.dataloader`: the current dataloader.\n", - "- `self.mbatch`: the current mini-batch. For classification problems, mini-batches have the form ``, where `x` is the input, `y` is the label, and `t` is the target.\n", + "- `self.mbatch`: the current mini-batch. For classification problems, mini-batches have the form ``, where `x` is the input, `y` is the target class, and `t` is the task label.\n", "- `self.mb_output`: the current model's output.\n", "- `self.loss`: the current loss.\n", "- `self.is_training`: `True` if the strategy is in training mode.\n", @@ -664,7 +662,22 @@ }, { "data": { - "text/plain": "{'Top1_Acc_Epoch/train_phase/train_stream/Task000': 0.8854205685270737,\n 'Loss_Epoch/train_phase/train_stream/Task000': 0.4297838434443649,\n 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp000': 0.9205405405405406,\n 'Loss_Exp/eval_phase/test_stream/Task000/Exp000': 0.23593760548411188,\n 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp001': 0.9418550992155053,\n 'Loss_Exp/eval_phase/test_stream/Task000/Exp001': 0.2060205339147685,\n 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp002': 0.9365404298874105,\n 'Loss_Exp/eval_phase/test_stream/Task000/Exp002': 0.225994213805472,\n 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp003': 0.8395245170876672,\n 'Loss_Exp/eval_phase/test_stream/Task000/Exp003': 0.48296057826637806,\n 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp004': 0.9701492537313433,\n 'Loss_Exp/eval_phase/test_stream/Task000/Exp004': 0.10016660355772841,\n 'Top1_Acc_Stream/eval_phase/test_stream/Task000': 0.9219,\n 'Loss_Stream/eval_phase/test_stream/Task000': 0.2500956041585654}" + "text/plain": [ + "{'Top1_Acc_Epoch/train_phase/train_stream/Task000': 0.8854205685270737,\n", + " 'Loss_Epoch/train_phase/train_stream/Task000': 0.4297838434443649,\n", + " 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp000': 0.9205405405405406,\n", + " 'Loss_Exp/eval_phase/test_stream/Task000/Exp000': 0.23593760548411188,\n", + " 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp001': 0.9418550992155053,\n", + " 'Loss_Exp/eval_phase/test_stream/Task000/Exp001': 0.2060205339147685,\n", + " 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp002': 0.9365404298874105,\n", + " 'Loss_Exp/eval_phase/test_stream/Task000/Exp002': 0.225994213805472,\n", + " 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp003': 0.8395245170876672,\n", + " 'Loss_Exp/eval_phase/test_stream/Task000/Exp003': 0.48296057826637806,\n", + " 'Top1_Acc_Exp/eval_phase/test_stream/Task000/Exp004': 0.9701492537313433,\n", + " 'Loss_Exp/eval_phase/test_stream/Task000/Exp004': 0.10016660355772841,\n", + " 'Top1_Acc_Stream/eval_phase/test_stream/Task000': 0.9219,\n", + " 'Loss_Stream/eval_phase/test_stream/Task000': 0.2500956041585654}" + ] }, "execution_count": 7, "metadata": {}, @@ -811,9 +824,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.7.4" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/notebooks/how-tos/dataloading_buffers_replay.ipynb b/notebooks/how-tos/dataloading_buffers_replay.ipynb index d2a04cf3b..b14515f46 100644 --- a/notebooks/how-tos/dataloading_buffers_replay.ipynb +++ b/notebooks/how-tos/dataloading_buffers_replay.ipynb @@ -9,6 +9,9 @@ } }, "source": [ + "---\n", + "description: How to implement replay and data loading\n", + "---\n", "# Dataloading, Memory Buffers, and Replay\n", "\n", "Avalanche provides several components that help you to balance data loading and implement rehearsal strategies.\n", @@ -17,27 +20,42 @@ "\n", "**Buffers** are used to store data from the previous experiences. They are dynamic datasets with a fixed maximum size, and they can be updated with new data continuously.\n", "\n", - "Finally, **Replay** strategies implement rehearsal by using Avalanche's plugin system. Most rehearsal strategies use a custom dataloader to balance the buffer with the current experience and a buffer that is updated for each experience." + "Finally, **Replay** strategies implement rehearsal by using Avalanche's plugin system. Most rehearsal strategies use a custom dataloader to balance the buffer with the current experience and a buffer that is updated for each experience.\n", + "\n", + "First, let's install Avalanche. You can skip this step if you have installed it already." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install git+https://github.com/ContinualAI/avalanche.git" ] }, { "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, "source": [ "## Dataloaders\n", "Avalanche dataloaders are simple iterators, located under `avalanche.benchmarks.utils.data_loader`. Their interface is equivalent to pytorch's dataloaders. For example, `GroupBalancedDataLoader` takes a sequence of datasets and iterates over them by providing balanced mini-batches, where the number of samples is split equally among groups. Internally, it instantiate a `DataLoader` for each separate group. More specialized dataloaders exist such as `TaskBalancedDataLoader`.\n", "\n", "All the dataloaders accept keyword arguments (`**kwargs`) that are passed directly to the dataloaders for each group." - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } + ] }, { "cell_type": "code", "execution_count": 2, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -56,16 +74,15 @@ "for x, y, t in dl:\n", " print(t.tolist())\n", " break" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, "source": [ "## Memory Buffers\n", "Memory buffers store data up to a maximum capacity, and they implement policies to select which data to store and which the to remove when the buffer is full. They are available in the module `avalanche.training.storage_policy`. The base class is the `ExemplarsBuffer`, which implements two methods:\n", @@ -73,17 +90,16 @@ "- `resize(strategy, new_size)` - updates the maximum size and updates the buffer accordingly.\n", "\n", "The data can be access using the attribute `buffer`." - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } + ] }, { "cell_type": "code", "execution_count": 29, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -101,31 +117,29 @@ "storage_p = ReservoirSamplingBuffer(max_size=30)\n", "\n", "print(f\"Max buffer size: {storage_p.max_size}, current size: {len(storage_p.buffer)}\")" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "At first, the buffer is empty. We can update it with data from a new experience.\n", - "\n", - "Notice that we use a `SimpleNamespace` because we want to use the buffer standalone, without instantiating an Avalanche strategy. Reservoir sampling requires only the `experience` from the strategy's state." - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "At first, the buffer is empty. We can update it with data from a new experience.\n", + "\n", + "Notice that we use a `SimpleNamespace` because we want to use the buffer standalone, without instantiating an Avalanche strategy. Reservoir sampling requires only the `experience` from the strategy's state." + ] }, { "cell_type": "code", "execution_count": 30, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -155,31 +169,29 @@ " storage_p.update(strategy_state)\n", " print(f\"Max buffer size: {storage_p.max_size}, current size: {len(storage_p.buffer)}\")\n", " print(f\"class targets: {storage_p.buffer.targets}\\n\")" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "Notice after each update some samples are substituted with new data. Reservoir sampling select these samples randomly.\n", - "\n", - "Avalanche offers many more storage policies. For example, `ParametricBuffer` is a buffer split into several groups according to the `groupby` parameters (`None`, 'class', 'task', 'experience'), and according to an optional `ExemplarsSelectionStrategy` (random selection is the default choice)." - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "Notice after each update some samples are substituted with new data. Reservoir sampling select these samples randomly.\n", + "\n", + "Avalanche offers many more storage policies. For example, `ParametricBuffer` is a buffer split into several groups according to the `groupby` parameters (`None`, 'class', 'task', 'experience'), and according to an optional `ExemplarsSelectionStrategy` (random selection is the default choice)." + ] }, { "cell_type": "code", "execution_count": 31, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -218,29 +230,27 @@ " storage_p.update(strategy_state)\n", " print(f\"Max buffer size: {storage_p.max_size}, current size: {len(storage_p.buffer)}\")\n", " print(f\"class targets: {storage_p.buffer.targets}\\n\")" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "The advantage of using grouping buffers is that you get a balanced rehearsal buffer. You can even access the groups separately with the `buffer_groups` attribute. Combined with balanced dataloaders, you can ensure that the mini-batches stay balanced during training." - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "The advantage of using grouping buffers is that you get a balanced rehearsal buffer. You can even access the groups separately with the `buffer_groups` attribute. Combined with balanced dataloaders, you can ensure that the mini-batches stay balanced during training." + ] }, { "cell_type": "code", "execution_count": 34, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -262,17 +272,16 @@ "source": [ "for k, v in storage_p.buffer_groups.items():\n", " print(f\"(group {k}) -> size {len(v.buffer)}\")" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "code", "execution_count": 35, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -289,31 +298,29 @@ "for x, y, t in dl:\n", " print(y.tolist())\n", " break" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "## Replay Plugins\n", - "\n", - "Avalanche's strategy plugins can be used to update the rehearsal buffer and set the dataloader. This allows to easily implement replay strategies:" - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "## Replay Plugins\n", + "\n", + "Avalanche's strategy plugins can be used to update the rehearsal buffer and set the dataloader. This allows to easily implement replay strategies:" + ] }, { "cell_type": "code", "execution_count": 36, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "from avalanche.benchmarks.utils.data_loader import ReplayDataLoader\n", @@ -350,29 +357,27 @@ " \"\"\"\n", " print(\"Buffer update.\")\n", " self.storage_policy.update(strategy, **kwargs)\n" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "And of course, we can use the plugin to train our continual model" - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "And of course, we can use the plugin to train our continual model" + ] }, { "cell_type": "code", "execution_count": 38, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -588,34 +593,28 @@ "\n", " print('Computing accuracy on the whole test set')\n", " results.append(cl_strategy.eval(scenario.test_stream))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] } ], "metadata": { "kernelspec": { - "name": "avalanche-env", + "display_name": "Python 3", "language": "python", - "display_name": "Python (avalanche-env)" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" + "pygments_lexer": "ipython3", + "version": "3.7.4" } }, "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + "nbformat_minor": 1 +} From 5d012908207ca13eae3be5cfe5a4fd9f719b1ef8 Mon Sep 17 00:00:00 2001 From: ContinualAI-bot Date: Tue, 30 Nov 2021 10:55:53 +0000 Subject: [PATCH 30/60] Update gitbook documentation --- docs/gitbook/from-zero-to-hero-tutorial/04_training.md | 4 +++- docs/gitbook/how-tos/dataloading_buffers_replay.md | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/gitbook/from-zero-to-hero-tutorial/04_training.md b/docs/gitbook/from-zero-to-hero-tutorial/04_training.md index d81c55abe..467e1f34e 100644 --- a/docs/gitbook/from-zero-to-hero-tutorial/04_training.md +++ b/docs/gitbook/from-zero-to-hero-tutorial/04_training.md @@ -5,6 +5,8 @@ description: Continual Learning Algorithms Prototyping Made Easy Welcome to the "_Training_" tutorial of the "_From Zero to Hero_" series. In this part we will present the functionalities offered by the `training` module. +First, let's install Avalanche. You can skip this step if you have installed it already. + ```python !pip install git+https://github.com/ContinualAI/avalanche.git @@ -187,7 +189,7 @@ The strategy state is accessible via several attributes. Most of these can be mo - `self.experience`: the current experience. - `self.adapted_dataset`: the data modified by the dataset adaptation phase. - `self.dataloader`: the current dataloader. -- `self.mbatch`: the current mini-batch. For classification problems, mini-batches have the form ``, where `x` is the input, `y` is the label, and `t` is the target. +- `self.mbatch`: the current mini-batch. For classification problems, mini-batches have the form ``, where `x` is the input, `y` is the target class, and `t` is the task label. - `self.mb_output`: the current model's output. - `self.loss`: the current loss. - `self.is_training`: `True` if the strategy is in training mode. diff --git a/docs/gitbook/how-tos/dataloading_buffers_replay.md b/docs/gitbook/how-tos/dataloading_buffers_replay.md index 4675437fa..8d31204b8 100644 --- a/docs/gitbook/how-tos/dataloading_buffers_replay.md +++ b/docs/gitbook/how-tos/dataloading_buffers_replay.md @@ -1,3 +1,6 @@ +--- +description: How to implement replay and data loading +--- # Dataloading, Memory Buffers, and Replay Avalanche provides several components that help you to balance data loading and implement rehearsal strategies. @@ -8,6 +11,13 @@ Avalanche provides several components that help you to balance data loading and Finally, **Replay** strategies implement rehearsal by using Avalanche's plugin system. Most rehearsal strategies use a custom dataloader to balance the buffer with the current experience and a buffer that is updated for each experience. +First, let's install Avalanche. You can skip this step if you have installed it already. + + +```python +!pip install git+https://github.com/ContinualAI/avalanche.git +``` + ## Dataloaders Avalanche dataloaders are simple iterators, located under `avalanche.benchmarks.utils.data_loader`. Their interface is equivalent to pytorch's dataloaders. For example, `GroupBalancedDataLoader` takes a sequence of datasets and iterates over them by providing balanced mini-batches, where the number of samples is split equally among groups. Internally, it instantiate a `DataLoader` for each separate group. More specialized dataloaders exist such as `TaskBalancedDataLoader`. From 8bba427449ec4125becd8a8ef5223557998345cc Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Wed, 1 Dec 2021 10:12:44 +0100 Subject: [PATCH 31/60] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d37000545..264a18a27 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,7 +11,7 @@ assignees: '' A clear and concise description of what the bug is. 🐜 **To Reproduce** -Steps / minimal snipped of code to reproduce the issue. +A [minimal working example](https://en.wikipedia.org/wiki/Minimal_working_example) to reproduce the issue. The code should be executable without modifications. 🐝 **Expected behavior** A clear and concise description of what you expected to happen. From 466d1aa44911e59714697c3bcba3369a7265b5ac Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Thu, 2 Dec 2021 10:48:53 +0100 Subject: [PATCH 32/60] ADD periodic eval at each iteration --- .../training/strategies/base_strategy.py | 155 +++++++++++++----- .../training/strategies/strategy_wrappers.py | 89 +++++++--- tests/training/test_strategies.py | 14 +- 3 files changed, 189 insertions(+), 69 deletions(-) diff --git a/avalanche/training/strategies/base_strategy.py b/avalanche/training/strategies/base_strategy.py index 80e01c954..a279030fc 100644 --- a/avalanche/training/strategies/base_strategy.py +++ b/avalanche/training/strategies/base_strategy.py @@ -28,10 +28,10 @@ from typing import TYPE_CHECKING from avalanche.training.plugins import EvaluationPlugin +from avalanche.training.plugins import StrategyPlugin if TYPE_CHECKING: from avalanche.core import StrategyCallbacks - from avalanche.training.plugins import StrategyPlugin logger = logging.getLogger(__name__) @@ -92,7 +92,7 @@ def __init__(self, model: Module, optimizer: Optimizer, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = 1, device='cpu', plugins: Optional[Sequence['StrategyPlugin']] = None, - evaluator=default_logger, eval_every=-1): + evaluator=default_logger, eval_every=-1, peval_mode='epoch'): """ Init. :param model: PyTorch model. @@ -110,6 +110,9 @@ def __init__(self, model: Module, optimizer: Optimizer, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param peval_mode: one of {'epoch', 'iteration'}. Decides whether the + periodic evaluation during training should execute every + `eval_every` epochs or iterations (Default='epoch'). """ self._criterion = criterion @@ -141,15 +144,17 @@ def __init__(self, model: Module, optimizer: Optimizer, self.evaluator = evaluator """ EvaluationPlugin used for logging and metric computations. """ + # Configure periodic evaluation. + assert peval_mode in {'epoch', 'iteration'} + peval = PeriodicEval(eval_every, peval_mode) + self.plugins.append(peval) + self.clock = Clock() """ Incremental counters for strategy events. """ # WARNING: Clock needs to be the last plugin, otherwise # counters will be wrong for plugins called after it. self.plugins.append(self.clock) - self.eval_every = eval_every - """ Frequency of the evaluation during training. """ - ################################################################### # State variables. These are updated during the train/eval loops. # ################################################################### @@ -270,11 +275,10 @@ def train(self, experiences: Union[Experience, Sequence[Experience]], experiences = [experiences] if eval_streams is None: eval_streams = [experiences] + self._eval_streams = eval_streams self._before_training(**kwargs) - self._periodic_eval(eval_streams, do_final=False, do_initial=True) - for self.experience in experiences: self.train_exp(self.experience, eval_streams, **kwargs) self._after_training(**kwargs) @@ -311,11 +315,6 @@ def train_exp(self, experience: Experience, eval_streams=None, **kwargs): self.make_optimizer() self._before_training_exp(**kwargs) - - do_final = True - if self.eval_every > 0 and \ - (self.train_epochs - 1) % self.eval_every == 0: - do_final = False for _ in range(self.train_epochs): self._before_training_epoch(**kwargs) @@ -326,46 +325,37 @@ def train_exp(self, experience: Experience, eval_streams=None, **kwargs): self.training_epoch(**kwargs) self._after_training_epoch(**kwargs) - self._periodic_eval(eval_streams, do_final=False) - # Final evaluation - self._periodic_eval(eval_streams, do_final=do_final) self._after_training_exp(**kwargs) - def _periodic_eval(self, eval_streams, do_final, do_initial=False): - """ Periodic eval controlled by `self.eval_every`. """ - # Since we are switching from train to eval model inside the training - # loop, we need to save the training state, and restore it after the - # eval is done. + def _load_train_state(self, _prev_model_training_modes, _prev_state): + # restore train-state variables and training mode. + self.experience, self.adapted_dataset = _prev_state[:2] + self.dataloader = _prev_state[2] + self.is_training = _prev_state[3] + # restore each layer's training mode to original + for name, layer in self.model.named_modules(): + prev_mode = _prev_model_training_modes[name] + layer.train(mode=prev_mode) + + def _save_train_state(self): + """Save the training state which may be modified by the eval loop. + + This currently includes: experience, adapted_dataset, dataloader, + is_training, and train/eval modes for each module. + + TODO: we probably need a better way to do this. + """ _prev_state = ( self.experience, self.adapted_dataset, self.dataloader, self.is_training) - # save each layer's training mode, to restore it later _prev_model_training_modes = {} for name, layer in self.model.named_modules(): _prev_model_training_modes[name] = layer.training - - curr_epoch = self.clock.train_exp_epochs - if (self.eval_every == 0 and (do_final or do_initial)) or \ - (self.eval_every > 0 and do_initial) or \ - (self.eval_every > 0 and curr_epoch % self.eval_every == 0): - # in the first case we are outside epoch loop - # in the second case we are within epoch loop - for exp in eval_streams: - self.eval(exp) - - # restore train-state variables and training mode. - self.experience, self.adapted_dataset = _prev_state[:2] - self.dataloader = _prev_state[2] - self.is_training = _prev_state[3] - - # restore each layer's training mode to original - for name, layer in self.model.named_modules(): - prev_mode = _prev_model_training_modes[name] - layer.train(mode=prev_mode) + return _prev_model_training_modes, _prev_state def stop_training(self): """ Signals to stop training at the next iteration. """ @@ -390,6 +380,9 @@ def eval(self, :return: dictionary containing last recorded value for each metric name """ + # eval can be called inside the train method. + # Save the shared state here to restore before returning. + train_state = self._save_train_state() self.is_training = False self.model.eval() @@ -413,9 +406,10 @@ def eval(self, self._after_eval_exp(**kwargs) self._after_eval(**kwargs) - res = self.evaluator.get_last_metrics() + # restore previous shared state. + self._load_train_state(*train_state) return res def _before_training_exp(self, **kwargs): @@ -696,4 +690,83 @@ def _warn_for_disabled_callbacks( ) +class PeriodicEval(StrategyPlugin): + """Schedules periodic evaluation during training. + + This plugin is automatically configured and added by the BaseStrategy. + """ + + def __init__(self, eval_every=-1, peval_mode='epoch', do_initial=True): + """Init. + + :param eval_every: the frequency of the calls to `eval` inside the + training loop. -1 disables the evaluation. 0 means `eval` is called + only at the end of the learning experience. Values >0 mean that + `eval` is called every `eval_every` epochs and at the end of the + learning experience. + :param peval_mode: one of {'epoch', 'iteration'}. Decides whether the + periodic evaluation during training should execute every + `eval_every` epochs or iterations (Default='epoch'). + :param do_initial: whether to evaluate before each `train` call. + Occasionally needed becuase some metrics need to know the + accuracy before training. + """ + super().__init__() + assert peval_mode in {'epoch', 'iteration'} + self.eval_every = eval_every + self.peval_mode = peval_mode + self.do_initial = do_initial and eval_every > -1 + self.do_final = None + self._is_eval_updated = False + + def before_training(self, strategy, **kwargs): + """Eval before each learning experience. + + Occasionally needed because some metrics need the accuracy before + training. + """ + if self.do_initial: + self._peval(strategy) + + def before_training_exp(self, strategy, **kwargs): + # We evaluate at the start of each experience because train_epochs + # could change. + self.do_final = True + if self.peval_mode == 'epoch': + if self.eval_every > 0 and \ + (strategy.train_epochs - 1) % self.eval_every == 0: + self.do_final = False + else: # peval_mode == 'iteration' + # we may need to fix this but we don't have a way to know + # the number of total iterations. + # Right now there may be two eval calls at the last iterations. + pass + self.do_final = self.do_final and self.eval_every > -1 + + def after_training_exp(self, strategy, **kwargs): + """Final eval after a learning experience.""" + if self.do_final: + self._peval(strategy) + + def _peval(self, strategy): + for el in strategy._eval_streams: + strategy.eval(el) + + def _maybe_peval(self, strategy, counter): + if self.eval_every > 0 and counter % self.eval_every == 0: + self._peval(strategy) + + def after_training_epoch(self, strategy: 'BaseStrategy', **kwargs): + """Periodic eval controlled by `self.eval_every` and + `self.peval_mode`.""" + if self.peval_mode == 'epoch': + self._maybe_peval(strategy, strategy.clock.train_exp_epochs) + + def after_training_iteration(self, strategy: 'BaseStrategy', **kwargs): + """Periodic eval controlled by `self.eval_every` and + `self.peval_mode`.""" + if self.peval_mode == 'iteration': + self._maybe_peval(strategy, strategy.clock.train_exp_iterations) + + __all__ = ['BaseStrategy'] diff --git a/avalanche/training/strategies/strategy_wrappers.py b/avalanche/training/strategies/strategy_wrappers.py index 813c4f7f6..91498d8ea 100644 --- a/avalanche/training/strategies/strategy_wrappers.py +++ b/avalanche/training/strategies/strategy_wrappers.py @@ -39,7 +39,8 @@ def __init__(self, model: Module, optimizer: Optimizer, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """ Creates an instance of the Naive strategy. @@ -58,12 +59,14 @@ def __init__(self, model: Module, optimizer: Optimizer, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ super().__init__( model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class PNNStrategy(BaseStrategy): @@ -77,7 +80,8 @@ def __init__(self, num_layers: int, in_features: int, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """ Progressive Neural Network strategy. :param num_layers: Number of layers for the PNN architecture. @@ -103,6 +107,8 @@ def __init__(self, num_layers: int, in_features: int, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ model = PNN( num_layers=num_layers, @@ -117,7 +123,7 @@ def __init__(self, num_layers: int, in_features: int, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class CWRStar(BaseStrategy): @@ -126,7 +132,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, cwr_layer_name: str, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """ :param model: The model. @@ -146,6 +153,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ cwsp = CWRStarPlugin(model, cwr_layer_name, freeze_remaining_model=True) if plugins is None: @@ -156,7 +165,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class Replay(BaseStrategy): @@ -170,7 +179,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """ Init. :param model: The model. @@ -189,6 +199,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ rp = ReplayPlugin(mem_size) @@ -202,7 +214,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class GSS_greedy(BaseStrategy): @@ -216,7 +228,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """Init. :param model: The model. @@ -236,6 +249,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ rp = GSS_greedyPlugin(mem_size=mem_size, mem_strength=mem_strength, input_size=input_size) @@ -247,7 +262,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class GDumb(BaseStrategy): @@ -261,7 +276,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """Init. :param model: The model. @@ -280,6 +296,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ gdumb = GDumbPlugin(mem_size) @@ -292,7 +310,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class LwF(BaseStrategy): @@ -307,7 +325,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """Init. :param model: The model. @@ -328,6 +347,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ lwf = LwFPlugin(alpha, temperature) @@ -340,7 +361,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class AGEM(BaseStrategy): @@ -355,7 +376,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """ Init. :param model: The model. @@ -376,6 +398,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ agem = AGEMPlugin(patterns_per_exp, sample_size) @@ -388,7 +412,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class GEM(BaseStrategy): @@ -403,7 +427,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """ Init. :param model: The model. @@ -424,6 +449,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ gem = GEMPlugin(patterns_per_exp, memory_strength) @@ -436,7 +463,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class EWC(BaseStrategy): @@ -453,7 +480,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """ Init. :param model: The model. @@ -484,6 +512,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ ewc = EWCPlugin(ewc_lambda, mode, decay_factor, keep_importance_data) if plugins is None: @@ -495,7 +525,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class SynapticIntelligence(BaseStrategy): @@ -519,7 +549,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, eps: float = 0.0000001, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = 1, device='cpu', plugins: Optional[Sequence['StrategyPlugin']] = None, - evaluator=default_logger, eval_every=-1): + evaluator=default_logger, eval_every=-1, **base_kwargs): """ Init. Creates an instance of the Synaptic Intelligence strategy. @@ -544,6 +574,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ if plugins is None: plugins = [] @@ -555,7 +587,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, super(SynapticIntelligence, self).__init__( model, optimizer, criterion, train_mb_size, train_epochs, eval_mb_size, device=device, plugins=plugins, evaluator=evaluator, - eval_every=eval_every + eval_every=eval_every, **base_kwargs ) @@ -573,7 +605,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, evaluator: EvaluationPlugin = default_logger, - eval_every=-1): + eval_every=-1, **base_kwargs): """ Init. :param model: The model. @@ -601,6 +633,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ copep = CoPEPlugin(mem_size, n_classes, p_size, alpha, T) if plugins is None: @@ -611,7 +645,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) class LFL(BaseStrategy): @@ -627,7 +661,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, train_mb_size: int = 1, train_epochs: int = 1, eval_mb_size: int = None, device=None, plugins: Optional[List[StrategyPlugin]] = None, - evaluator: EvaluationPlugin = default_logger, eval_every=-1): + evaluator: EvaluationPlugin = default_logger, eval_every=-1, + **base_kwargs): """ Init. :param model: The model. @@ -647,6 +682,8 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, only at the end of the learning experience. Values >0 mean that `eval` is called every `eval_every` epochs and at the end of the learning experience. + :param **base_kwargs: any additional + :class:`~avalanche.training.BaseStrategy` constructor arguments. """ lfl = LFLPlugin(lambda_e) @@ -659,7 +696,7 @@ def __init__(self, model: Module, optimizer: Optimizer, criterion, model, optimizer, criterion, train_mb_size=train_mb_size, train_epochs=train_epochs, eval_mb_size=eval_mb_size, device=device, plugins=plugins, - evaluator=evaluator, eval_every=eval_every) + evaluator=evaluator, eval_every=eval_every, **base_kwargs) __all__ = [ diff --git a/tests/training/test_strategies.py b/tests/training/test_strategies.py index e3882cf5f..cda029841 100644 --- a/tests/training/test_strategies.py +++ b/tests/training/test_strategies.py @@ -73,9 +73,19 @@ def test_periodic_eval(self): strategy = Naive(model, optimizer, criterion, train_epochs=2, eval_every=1, evaluator=EvaluationPlugin(acc)) strategy.train(benchmark.train_stream[0]) - # eval is called after every epoch + the end of the training loop curve = strategy.evaluator.get_all_metrics()[curve_key][1] - assert len(curve) == 4 + assert len(curve) == 3 + + ################### + # Case #4: Eval in iteration mode + ################### + acc = StreamAccuracy() + strategy = Naive(model, optimizer, criterion, train_epochs=2, + eval_every=100, evaluator=EvaluationPlugin(acc), + peval_mode='iteration') + strategy.train(benchmark.train_stream[0]) + curve = strategy.evaluator.get_all_metrics()[curve_key][1] + assert len(curve) == 5 def test_forward_hooks(self): model = SimpleMLP(input_size=6, hidden_size=10) From d3f58b29292a5cecef2028cc3faa5c81955daadc Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Thu, 2 Dec 2021 11:16:36 +0100 Subject: [PATCH 33/60] FIX issue #838 --- avalanche/training/strategies/base_strategy.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/avalanche/training/strategies/base_strategy.py b/avalanche/training/strategies/base_strategy.py index a279030fc..d9bb2dd42 100644 --- a/avalanche/training/strategies/base_strategy.py +++ b/avalanche/training/strategies/base_strategy.py @@ -335,8 +335,13 @@ def _load_train_state(self, _prev_model_training_modes, _prev_state): self.is_training = _prev_state[3] # restore each layer's training mode to original for name, layer in self.model.named_modules(): - prev_mode = _prev_model_training_modes[name] - layer.train(mode=prev_mode) + try: + prev_mode = _prev_model_training_modes[name] + layer.train(mode=prev_mode) + except KeyError: + # Unknown parameter, probably added during the eval + # model's adaptation. We set it to train mode. + layer.train() def _save_train_state(self): """Save the training state which may be modified by the eval loop. From d9a0e7b4a2b81a97c3871a6807292f5b712328c3 Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Thu, 2 Dec 2021 12:09:03 +0100 Subject: [PATCH 34/60] UPDATE target metrics --- .../training/strategies/base_strategy.py | 1 + tests/target_metrics/mt.pickle | Bin 27508 -> 24873 bytes tests/target_metrics/sit.pickle | Bin 31953 -> 27981 bytes tests/target_metrics/tpp.pickle | Bin 42635 -> 40181 bytes tests/test_metrics.py | 14 ++++++++++---- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/avalanche/training/strategies/base_strategy.py b/avalanche/training/strategies/base_strategy.py index d9bb2dd42..8dcc0dd9f 100644 --- a/avalanche/training/strategies/base_strategy.py +++ b/avalanche/training/strategies/base_strategy.py @@ -146,6 +146,7 @@ def __init__(self, model: Module, optimizer: Optimizer, # Configure periodic evaluation. assert peval_mode in {'epoch', 'iteration'} + self.eval_every = eval_every peval = PeriodicEval(eval_every, peval_mode) self.plugins.append(peval) diff --git a/tests/target_metrics/mt.pickle b/tests/target_metrics/mt.pickle index 6d47b4d01b34b28a64b1e8a2f2f0bf687fd4d74d..2970eb5eb690a7ea9690c792c6ae9f6bd2a0693d 100644 GIT binary patch literal 24873 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 E0C}X2RR910 literal 31953 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 bqPo&;V&AY~DwdT+%gUlXwo-R>Zm2UbUg>l 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 literal 42635 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 diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 0e2079a8e..cd0699eef 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -306,9 +306,13 @@ def metric_check(self, name): self.assertTrue(el >= init) init = el for el, elref in zip(v[0], vref[0]): - self.assertEqual(el, elref) + emsg = f"wrong timestep for {kref} (Expected={elref}, " \ + f"Actual={el})." + self.assertEqual(el, elref, msg=emsg) for el, elref in zip(v[1], vref[1]): - self.assertAlmostEqual(el, elref, delta=DELTA) + emsg = f"wrong value for {kref} (Expected={elref}, " \ + f"Actual={el})." + self.assertAlmostEqual(el, elref, delta=DELTA, msg=emsg) def test_accuracy(self): self.metric_check('Acc') @@ -432,9 +436,11 @@ def metric_check(self, name): self.assertTrue(el >= init) init = el for el, elref in zip(v[0], vref[0]): - self.assertEqual(el, elref) + emsg = f"wrong value for {kref} (Expected={elref}, Actual={el})." + self.assertEqual(el, elref, msg=emsg) for el, elref in zip(v[1], vref[1]): - self.assertAlmostEqual(el, elref, delta=DELTA) + emsg = f"wrong value for {kref} (Expected={elref}, Actual={el})." + self.assertAlmostEqual(el, elref, delta=DELTA, msg=emsg) def test_accuracy(self): self.metric_check('Acc') From 4d3d923afaecdb3660bc60b9d85cb9b0957701c8 Mon Sep 17 00:00:00 2001 From: Arjun Ashok Date: Sun, 5 Dec 2021 11:19:26 +0530 Subject: [PATCH 35/60] Fixed --- .../benchmarks/utils/avalanche_dataset.py | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/avalanche/benchmarks/utils/avalanche_dataset.py b/avalanche/benchmarks/utils/avalanche_dataset.py index fa27b4790..d99fd94db 100644 --- a/avalanche/benchmarks/utils/avalanche_dataset.py +++ b/avalanche/benchmarks/utils/avalanche_dataset.py @@ -2004,20 +2004,34 @@ def concat_datasets_sequentially( new_class_ids_per_dataset = [] for dataset_idx in range(len(train_dataset_list)): + + # Get the train and test sets of the dataset + train_set = train_dataset_list[dataset_idx] + test_set = test_dataset_list[dataset_idx] + + # Get the classes in the dataset + dataset_classes = set([el[1].item() for el in train_set]) + # The class IDs for this dataset will be in range # [n_classes_in_previous_datasets, # n_classes_in_previous_datasets + classes_in_this_dataset) - class_mapping = list( + new_classes = list( range(next_remapped_idx, next_remapped_idx + classes_per_dataset[dataset_idx])) - new_class_ids_per_dataset.append(class_mapping) - - train_set = train_dataset_list[dataset_idx] - test_set = test_dataset_list[dataset_idx] - + new_class_ids_per_dataset.append(new_classes) + # AvalancheSubset is used to apply the class IDs transformation. # Remember, the class_mapping parameter must be a list in which: # new_class_id = class_mapping[original_class_id] + # Hence, a list of size equal to the maximum class index is created + # Only elements corresponding to the present classes are remapped + class_mapping = [-1] * (max(dataset_classes) + 1) + j = 0 + for i in dataset_classes: + class_mapping[i] = new_classes[j] + j += 1 + + # Create remapped datasets and append them to the final list remapped_train_datasets.append( AvalancheSubset(train_set, class_mapping=class_mapping)) remapped_test_datasets.append( From 3efab4cdd10cfafdc09212191ca4c5f4801b7918 Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Tue, 14 Dec 2021 16:05:01 +0100 Subject: [PATCH 36/60] update metric targets --- tests/target_metrics/__init__.py | 0 tests/target_metrics/mt.pickle | Bin 24873 -> 24845 bytes tests/target_metrics/sit.pickle | Bin 27981 -> 27911 bytes tests/target_metrics/tpp.pickle | Bin 40181 -> 40181 bytes tests/test_metrics.py | 6 ++++-- 5 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 tests/target_metrics/__init__.py diff --git a/tests/target_metrics/__init__.py b/tests/target_metrics/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/target_metrics/mt.pickle b/tests/target_metrics/mt.pickle index 2970eb5eb690a7ea9690c792c6ae9f6bd2a0693d..08cfc7c72cef5ca9ce05a43f22bea28b5c48989f 100644 GIT binary patch literal 24845 zcmeHP3tSY{_g@gy5DXDBQWMJ*U$6oKDmsIr^V1QZm}ufIk3|sVwTsU*Qm}kKShLh$ zp&6w9e5EOx%3>mm+0V)cB4mJxC}=2QrfC0j@0~j?4`r61fB%0Dew^=~d*;r$XU;wM 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 Y27fMZM%v*A2l#8nZ_5TO(Z)Ic4^GT*cmMzZ literal 24873 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 E0C}X2RR910 diff --git a/tests/target_metrics/tpp.pickle b/tests/target_metrics/tpp.pickle index 807a36538e031f02fad89f28fc0e46088f043baf..7e94e3d8c1f70e443e8a6e5eeaef0ba3dd524582 100644 GIT binary patch literal 40181 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 literal 40181 zcmeHw30zHE`+vxs@w(S=adVL=&81BHB<#x;A!O_%hoU;C$&}%6&11+MGLNB%jFn{O zx)Mn^&4UnS$aMeDT5GR$dY$CN{lD+;eXs3&+F#GG*0YAao;B>Xwo-R>Zm2UbUg>l z@s(H-Nb|)PSWZJ0K^c=^qID8dC$<_5KgfJ^o7jryGSBDrJ)hUFBycZr)563}i+JwO zPCtafuOyI$7@g|z+yf9@n7Ek%lmw$ba$nysS0e4r&(H1mY-L;#&!x?(v#wHn2;|(j zxy`eG2JY6Up38jZqrBxkoj%x90m>Y zbr~lsEeTrIf5|q%Yl`E@X%0#)xkkE)G8ZOQHyDg^54Jz2I6fohf4c8tWp=2;^ga+bG5 zfhFM^ZMUtv%l{z+%mbJ=MFeXbi(p%<2o8FPAgY%LZjKf~@(dAV2aBNSIRU$?cNRhY zg#eK4E$waXzP+~F>*#&)hhi&(qY+za(=R)kKYA5zfgjH$dtYn}KWLHg^{^z6F$iDK z`c;=6gI`JD&pP;m*6$Ng#w3W%oEAyA^=i(Ut;TLVmzcK9I&GOvsX(>%Xv-&619&cV z)J+6S0yi+HD$m8y^opR2Nf6z=7D>3=5MSW;9qB_tx>*%n;9xJollEM{4)(-Ae*b6uT2WJ648%!KUY3h58vK zfwzH8P9ldTDObO6iSx<}3?TEhcQju%T@oDCj5AR1ykt{7&c)TO8918G*G_CI3Iqpn zHD_CYH}Eezg3eA}(S08{8u^7g^&H)J$@b%cot_K;%C4G&Zoga5lb3{=j9l2lArrK& zG)a$ZJsVojRvV*7_5A@TvO9*SavLGjwR1FDn5}nV*s*-nBJ0^6{O}tB)z@!a=YT+s zIx3e-nDXAviZ~kc%n$llxUElP4C(|w8n*{gYH#U&Vgv%!I<+_xjlsZHJ&2>(nV^6u zklA13XtYSgR&UhmLgfD+91Zp_DCDnkG+(wI{LAE?$aVk=Qbe$RjtF+P6T#uzA~@Gl z1o2%(@MxF_9#0m*`|AYkuGv5Yjbs2&0choDzG(Fqy$lGT*vjBs({%Z}#8#`vjn z`&^z&lH2E;>9CvU9#V=XK%zNkta`HYdW-C z{DS9Rr4O_(GL)7C{%UMt^z`X7@EyZPbl-lrS_y#5vwH1^{Z!U=>n#WCLBBu*$b9V` z&6iD=1jiXV*hV8q^Lo{a<-RZ^*&4m*r<@zldC9&Bn}U|+)&)w)#uiOlMu4NS{y0ru z2}U$qLz7!rt}y2%2WnLL?MTJO;Ajfcq>=60g3QO8_OnyNz`ywG^?Ui!24rOVNgV>K zA=9;UG+LOgcXvgQ8)~EVj6%;~k5gk%tKQj&Qp;;lI%ZX`y1GInqEx$N^6LTws>fWn zkRnj$YLsm<7G9TB1u7rN97&!HRRw_g^#*$|>vz7T^6MRq7KzyEJqFcI5v1%T>}R*vS2R%`2MjM&uKKr&5M`s~@4oeXlfa6yBL2Qx4wmP%VSP4JMipn?)D zP4Ex~6!3%A@e_cXw=$XkMfVJIUO|=_HKy2s)eacK%uVYFR~o&TUlLGZ*-_woPH#N~ zVkLoO%z?R_2PoW8{TV2``C3d`CarrW9q)P;GVAumfC>YfLNRddtmnKBEeRwxLek*1 z@Aml9Z)E+RB>~lz)xq6n3>Hh`Y8Hw_-?DqMRz)Rx``}fY5K?f&kh83Q3(F8A= z?QU&W>nW(e(pf)BK8pcldMjdePr1)HnV+A#*SOC-7+-uKNGl1gt2Tg(hk1ZX@mnE- zPoe8fN#JIZUya=o=uqFD3eyLu_ay>kcY8az*I0jFce$6ni=U7CWam+B;N?eFd}#@SEit0SrM_GPRHIMhfJ&W|m?{R2oq%3crQvv=?_tt6cR2yE61n zohky+*G=x@=Uq`K?^FT)=kDd_T}jY&(nVp&y~cT07IgjnAJF~HN6b$iF&8+KHSb4XHa^CY8Ne`( z$VYH_{Ue*seH_CzzUU3V(_GNppaDU!5LQ8`FfR!HmYWx}a7lH@G!L1pALJR7Q}up6 zV`M(EG2Pu={UGbx3<&ZA9k-RO-B$mX7lWCNjfJ_nwYj;~my4m8Y_Vb(ez+q1uCnY6 zb!(y8IAlAQQD2x5mVTTgL$f&U(diA72U~=*Y@a0`W#75oKCzfClvoUIoBp@5+RWU- z#>Uc2tE{H7#j-le>BZufQJ1(Qv9~i`RX!nR`A#mVYtE{!IY-3*$FZUyPsCC2iP*)_ zqAIDz|4(H#YFw;LEIOE(d^xL&$$ro3uWQHQodQ!+EowE&YEkxkR)1wyn_6pKt4)Y( zv8<+T@>1Pt1>;jMbF{4{F7p`o(H1$=iyU&0{UZ-@qjP=U{jL`w${k^@^`FszSeTiZ zYT*`f143krWi{=6B+hORze^m>$rc5Sr@k^P?4U)6xvm|L4znrWBSRQ7vTmp!XdU)jvm#zxzIwJ2LG zt2?)ARP{|}9P3BF$xJwZWLp?V?-fxKJ~W2w0L{rJt7x%PxR*6BqyhQ|^sAu(u`xG= zZnbuuJt|u)tLYGhj3f_nZatcpdvy9Fb9=PSCPSEWtlv%DDQUYe|0CLJ8w*o2%MRMP zMJoI6&T2GqqfaJkx(#vsx6Cv}(V3{WUwKdA)DmlGX|!Ty?n>Y ze7@Ou?lSIJtmnC_eP_WNmGIi7CWwrJWlX0So;%(8<+^XGSzzvSDuvn>F^>de{@B&QO^#)dt9s+1(I{I3hKCt-c5tV*8w-KzU zU1~YLy&=4yvpZ{b*EN2VVKwtX;eqEhYXHmt_|n~Wkq}PAt$qGefU=|DP+X|NRb=+B z_?63VG}&N4Zsox+At`COq+B~##{=-Y$(*SO7(|UY-y2gLgJuUI&|=|HLu(8+tNyxy zK--Etx-LVYjcjI>@dz}}9XtgG*!+Hzjj&7!0rs!EzZ+Q_Yg7Dn%Wt&&-@78&O8t)I zx7qz~UX3(l3vBk?jQ!9O4}G;BW8dQM*3)_Sazc5SNz@f83YCP)LKUH^P)(>V=m|B1 znnEq1wonINyKg4^Dl`{b2rUHzp_R~DXd|>0+6jh2d%;LB7EAM|z%octZ<_L3zdBS{Qfv`|mBrFz|2rGn@!YUz1SS_p()(Y!{^}+^WqYx}?5;hB4 zgb-n?uua%5>=1SeyM*21W&$#63(HL+AUCMq3~;ctk1n+gqYy^P!c>dNuqc?inxZ07 zB}7r0FpYxAsN>RLspI-Qs|0mT!x)8zqt^O7i^(v+Y*rhlpc?0Nuv}#nO~7QdiIk}`Elo-( zEGDc8n3fu7q+KhPiiBOPO`sHY0x5-vme^ddxYRP#9!6n+g{(Jg@$2jb;e>qBT}`gREDW*0(IQW%Q?CZh>Tk}XW8AQHsh&JU1ZtES7mjKzGWO& zA=kKE2rRpM6!r=GKN)A0Qp&VU)kR4u>iS#?rIe6MVQB`)gtcKYVQoMu0hXe!jaDf# znk+_PkunMcjP-e-BVMI;X;^?1FfDbNYD7_j3{_WaX;9Q!EI*+ECxylM>7gWVL(L!&@QD+O9aT-S>dta0Dmtpc*hkV z$DxqBy8V6AhX0pFiT}Ro#=p}ovLa5Po5pU^LcfQXN;bf&@$_ACCd@WvB+~8O$=A>Y z?@u!swwsNetHD^M0sqPl@PlyvqkvUr*j0rto?|<_86}724`XH0{e?^>3+>@wRL@d4*9j@qs22VXq`r&m4;{ps?6?qH7nJ@!a{x=gzRh@;?fQStwgrZBbEE z6Df!{$5$~Kw*QjJXx5(x_3_>lc+;O&NER~N;z<@2Ps8pS8W)pM1F~_aScPP)ho}II zmVi3l?aJbMpny$@UA%!s50!Z&lQ%Hf(Xp7T(o8Z7IgKV+y|amKFiqFN7wj5Bo0qK9 z0We`)CvxD-0477vq)DUOTbXbglZiza>`zb!I0|o3Di%&U-OQRnb2S~Oxo0e4&JamEQqNy1t=u^O0w`h_zOZg71*tbVwgLsZLJMlEhx2esH z#deV0riUxU(>%O24{fyafRin#87%f&G*@raBx?gyG+hj^r(0b*tl%^nMyAC!yQNUW z7>j`vdMHr}-i_R=SY1Wi*WoHgQSW-AtcNr+Rg|Iz;dqZWsDBZBy_^2;oXq{Na?{KW zv63yNHZqx1`T(ZVilJ^IXw^-p4FD9_8wc$GeE?aT!(`I5Xmis3=v?qe=R#;k zP%FE5I|PUi1%NG-ESz+@w@4i+Ixa>?0>4I|h4j~k&HMSB11;LMNZ;9}_kf_CK#8}v zh_{EWhb(^>VEMz`t2S;r8)p!lQp2=x=qRy;_ln76(g!e$=5yv6bghLQ z>(uXO(v6^5H0PO@&!U?w)rTdpaA?TvY%>;M7WD{OIC~%yvL@jRfXxlx;H-`SM*81{ zU$NGM`7G9`G75PkW?#CL4PCF#dsU{T zS;{P1n2aXSreRE_-3Hc$V+RDXhLOdUp@T0dCfYyGqjMsBD*c4d7SI z-Dkt0M`H}<0fpvIHhMj@^(*Kk(4PEP0b&;P_8CwQi!>W>=ChceWipyR>j542X*S?& zG|d`tHkwZEj4x=4X>0b79)3~Upo3(1Hwv>QCPt-3hO(m8_YrY zf~gYPWy)Z!D`ROf!J^r7%c2K=u%27i<|**YhW-`nxgn1=RSpyyR~DQvQ7}Np@SR^O6s4pQf3b|D>!XRSv@4t7{9Q? zCs=1Jg`RfFM$;+yN-cgC4V^$>A*bn?#KI4{d8{XZjyoIEzIewE9(#E9p_@mOERL{{ zn{JKIrT9DFj{QE(t@Ycr=;bK&>hE)TOMR&BJooG^lCH<3t?8c)*Fe|S^Zvt`@=k+lqeOHncgJ+J12*|p; zB?MFQ-5(Q>HL)^b6;#E8M;H-}Fe0oc>HAeWDE8-riU=hx^q>S&p5A>(C=q>4m6-DA zRVD$CHv~sx%DHFr2`CurAjOpXyY3Q@J7T^HQ*H;nj>h0-xh@1GHRDv6lF{n05`*+< zkMuDpc=?fl7yOQBOu5^s9^ApgYa_)>xPRxm6jM@<+J<0|(zt^PgZRm32*|#2Oo=Iv z(jF5^K@)v_Ov%m`GafEezyG4d;B4?z0&*YK2Lfm%rB+s9P;fC)AA=Y<9nh?t4 zge?SQoa?8LSy>%Yr5L>Kyjh9ClVLaD4(h1~2TL({QFuUwL8h~(5`&z2DN+m`_cT^v zknyA|+(GMQ<-BMN@=ksvls6A> zpC5ujR&8^A43dI(NHKWf-zfwG)q^f@2lbS8OQaaQ++wJY!Gq8QB>{U9L`w4$Kmg&) zd1nd8Kfj)^p5$OR2gmRv&QxKUN7L?vV31n(KB43o#wiJ<) zb4UgRFe888A{7P?uFI5!^6r%sgBRs`!5tOKnkIzu#Oi_yQ}X-p1QZm;>l4bVctXkT zB(`JEEq+vDR(7oz!pi(heDE`u-Uz|0$Ikgm3^Ip5C6u(}`zlOHiM~O=Ghqf0z>L&e zPYEUaV-+Q)Wbbqf!QknOk%aOhxq=E)vfsx_Fvu_HDaGLSBr&5NJ{gBt$T6FV3(@3E z|L-f&-@ftXZ&-w$KUmZzAn!_NvyjQ#sUB$hB#pLCoaMV{Dp_Sn6NciJ?}{f2{~`0 z)Dg?%B|S|VHf$Krz~I3EY|>-#GGA_1twke#Y6Jt)oQ?CR_xuCE2D5aD&nf_M-G$tng8-~E zG9BLuW)DzgHdO0z;ZN44zseg_zW)b^x6AZCau2RZv@mBaU2V$s@mSd^Yf`qjN4*XYRI9J_}dz(@@@Vw z4-e`wZV?Y2z`^tBKd3e+(Q}!8!>$4TDoFaSp~CNLP5~vNs(ZZ{1jcmEj?DnA^sVo5 zx0nXNj6}7!D{fge0ZM9G&CZkIY}oYMLvlm9E(Fkh)kFW4x&SH!x4YkV1jLnN+o9?Y zMF0-%YmUFjk#@PN)#i*jKnc%37r9;n;I+xXa>rq2J1u$X4dctM#awiSqLkJ+6(4L~ zS3{W~DcAcM>N5a1(py2munf3ED*n1+I1g8V55b4gfQaYb2Ns>gS@$;KP$7Pkp=fU{ zm|lSOi#ML9m%?z2=U#jF_QCn{+H-zito|y|_!i9g!bxyCzbwGvyu8)p6x@G#cs+b0E}g;(Q39-z@La+92e3-A0>Ha29ypwWq^1V=Ji>xK z=H)XxP5{=r$jr$8us+LN^oyK_ld24&=-ke6KM!EPJ$_C!U$i3e1>;WwNGu48TdwwlM9Ft3>wdt+BZN zb6tL-O&gezxHvk}X-0KiFXPEZe=SaFR2#9|e%H z^Mf5$IOnsj9L&07!NJ&hR)pES3JL&AU(7@wKvBEs%3WTlP^6NIt-D*IbV(`(`X9y= zUENHWG1$OM$|vLzR=EczFw5Zu6`=A`oE(XcrCk_K^2aN2t~eDR%N?nQkGlL&`%o90 zQSa}%!D2Lg5axaD6Qq*7>thAuR%mwKa=GLgELsvdvsSHIKpEY%J=QA9?3IsmqiDyX z$~fer>Fe}k^-OuL$fHr_;%zwq8jmr8`2wg>H|s86vKe0Ic(ZiFj4WTM0B>aVM+~g= z7(nlcT>M=a=AyIZ{i1Qmc|vU^2DuO7BQbb9Sa&E&GMt&LAQnDJ9ry~@%%{CS;yTYg zx$BK{gL~S?VGT@@Kzn$~a0p(=c+zeuIz1ZH_jcFCYkzqw&elLV#^tq&>5Y^8czfMK z5?9U6`ULczho-WjjY{-}X|nijNIMT0&vC7y;puSPV(ac?*UsN^w*+AE+ofGK?f}rUJPC&mA{X6)?VK)U{4Aa;SoXJ~zfWj?c30}ZjGHBK z0}G%E!x7g<-zuTZ;c?rhLIj{hm*0hE+IY$NI*0?Tb-mWZ1<&1n4o#k3=OxGUMd2fh zGKsKa7rKMi#t4zM&7M$jAUK?`VGT(*CzT@D)fICg(gKlkDnV5D*$T2KQlCCzq$dxE zGEpByty2RICPgyG*#Ykr* zicxH|6+;fcDgxi`qRd`Dk>Zmr3LmdYlQTd}#R)+amV<}D_MB~W1k}uU$y$F=J?6|3 z!a7Y>VIjjWivcReh*}YuqVSn*NFB;+&kv$Z_yr|Gykw>a628GpVh=_W$}vlH2iEQu zVmPyFh=!e7Edrk^qRa(zFQm&C0USqE#2_QX`V2P7!3GdvNNtf+$W z8>&RaI}t?B*hMI@4fVhdD3lXV8WOG8zM@RXInlzy)2XQ=K?H@oZ>C5&{#Fz|bzY1; zO4bK>5a8?wqVOz3QTV_uk+P?+XxMoVk##&=R6p)_1R-8>*d&u!c(L429H~0V%BNza z8`p{fUPy0Zfc)%C=N76^kV_U1Nq4duI0(BR$(sjBA&PD6`8!1hMTz^?iSc zAjX>jEC7Fa$%W>k@TIB{Fe-vejYTsqJQGE&m^!b|aEk-ftplJLuYU+i$qE^^*QD*fFQ6|Q!Au;Uu8xe#J5oHz`is=fZTCwp% ziSW7pdccG7ihH&T1q4;JjwpOCxDR1%>n}#SqoSCKofSoywZjmA=garBNby-F+H-Vt zA0R=<+YgCmgdY~e**`!8!P7<7xmQX=f>Nf<*YQ1KT)yi>;iK!sD7N8+J%}`Jrl_@b zl^E%Tr~;%0t!RCb5?xc&IzyICpq<}*gvgw%C|Wqz6PtmYvXMA$X1=*csblBw?zz;% zBEEghEIFTb{fxueg}fxH`{R@QGCM-kkYs;n(l7m?uC#yD%49?$FF9THn!(J*GF}qd ze~Oj$sHME**wH0Y$EaNZj_IieJn0RJm|>oRK2^`62@7FL|@@ht35Ub1rdhN>SO;91&ob-y&|IJnh~-8;^2 z*KZ9_<_2C3zfcI2y|2^S4!;10^2|EYr0kCm>+@#LW0Ja+Y2ETxz&H>-#<7;D&@sfipdhBdfqXI8E z)M9kcfV~Q!T)O61->3-)$3FWQ`SUfPtiKi`g!cu?>ZR6-R50VvV)Gq~C&MR54$uCz zntW1A$leaouO(GR0Oer$XY;Bwgf_xHJaKoeZBR+3P2TROfC&$(CzcYClfWhvo);mCQPT7}xH|ac3 zPAocq?8wjXX~@W{$3NUyGy&9`2mU*^0V47xqxRl`_VH+XpjyqFwvukZx;g6n*2q|(#Ol{^lb-`h;pHV2qoHt>6&kb}>B{K+fy(hX5qZ8dbdzDpJ_$ zUuRD1HXPJ9Zz!MDWGtw6x#V>(4r<+z*|q;F(DedVt1&;djE2$)U(w3vz`9MKCCd-$ z|NBRvl(Sk;t1(pmumykG4xO3{g>q(Wd{RrWC+yA54|!%#6T*yqMpPO30KmCe+jrwu z1CY;gSeyXQX_%44>AeGY0eI*7czG6j^v+M)AJuj$ggm-Xo}B>|AnfRt1(qA^p_Hc^ z#1HJe75wzxVE5V`@_A?b$_Yyv!1I-O zslVc7CwPQ!-8%EiFaRFv+_OPZI~M|_p6teg%7O4u>e@GppTPu54z^@&!zWN*2E zHDM#*f!$MER!*o1kIeoH@22oB(13(mq`1Tkxd4@9Tn(?w6T#*KyDF74wwnyBUayii z&4)U6VE+%VhW-f6*P%Jy2hLW6N9N$AMDx5F@R%RI?sPEX5@b}E*OBSv;&z})l2;=! zuPuNHn;wn2$^n=(VBFQ*MF7s!JXg08Brv>rVAAc=-H?0M$H#VkA1{CH=}q76khysm zs=h&8HO{ZoKGdP(IeC7i5CT5zm0~bYnIOSHkuX_BfKJFw3_RDRDKYR#(2vGI(YAdc z2A5ZfBpAq>oQFFY@7*+;P<*_%DhZ`lPkjs&>9vU&-qjXL2_V4`bltVr_zf zcg6Ec4CHNcLNM?sdP{(J>pp>)qIAnfgJ3A!KWf1pfX_L5!tzYsAA%{~2FH{bc$K>= z!C=Pz*U=bEc#MbHkm0T6Doh!D{-Zty>*Rkf!9d<=c{B#zSKHsjK(Qt;1Os`5xWwvp zT`YI+Q$fH1GXfg9Ibfh@apEum${Ym&%^JZSWG1&;ABcfx(4i0vyyALEF;Ju>5a7R{ zwF*1{(Op1;QGW6IhC z;cghX#7LAFD5?(&!N7B3D-{OeHo3$?ukE{lfCAL7br=JWlbxg(_%2?qAgtPJ^fB=2 zjYN=e?K}wv9v^&^1Z)`_f`KB*R}80}r36#FUS;TGFu^7Y?nu4DBNYao7xxe_f97fO zPAC`}2$;7GSt zXljb*BD$S0#$Zp!#!oTuXeY`{m@-3!DW307D>0D2ZLg1k*Bs9f3_O1^ggb=eF{*Dg z1{21148cJ0*A^2D#*e$EB4AGh+(9^S|JFSicqUAfVj#Dy4|iZq)U(LP!0YyFLh(-- zCsKC4l3dR304G*3;s(Vvu6L)tu+P{556NTUyFJnnBr;MmVoiMMPd0L3VqD- zcoQnYU;cmiGkOh)o@2S<~T)#flpBcN#OV$Tcwz?M(MNz1Fs=5(HMAiTdGgM(U~C_ zjJNidVxWBTAR7bs_J64WAcx#m950hEn13HDfBnIVe}ijY9JwbF7f=8NJ^??s#OoIm zC}nK>ffrzEgy%+6mh-_$p?Do+IF)hQ(ZUq391W(zj%_SbvriiH+yJW8zm{P%j;kM4 zw_g!gWx)bi?i6S^z3W@Lq@=(rh`-SQd8c1{0+XJN`gx zHyW4indHrQHPDtuD)IE32w&aiIgZBOb@^`~bS-`_L zJlB@mv);YSVVoNaDNyFeU%^XbQz*sH(R3)9|Kt>uXP7QIA`%uzGIL7sCQ*T8A7!N&9CHyHxdq{>I3#zA0f{d4aZ@kLrc8hzaI_I-hUhf45cj4kbCbo zg#3oJ+Fmq@-v%YY9;#rT<7r$yT_=&v-92ce-B0H1!?kx(oFfTv90kr{_^orE8$u}q zb@93a&-J4$q3vE+#%~ExP?;Xhj7TZlQDNI&VX(oeCp1JwQ{IR``-uK@>JO|KoXw1G^Q2NVE#*Qw&6HH9Vmu&Rm(*eWb4C58T0r7NUdPpHV zjgKA|#|}~B*r{XUSzKQcJ2`1fBX)KS5NG`udovB5pFfo)N4 zH`jI<;L{knH`%K!rKmKE7EOH!laR2Fv_OAS2m&t+{y=Lbu=w)g;vb%I%! zmvsbb_lR^FP_(}ypIY_^HCsUxSce{y6*3=pnU|}qr`$)2kIxx{f{BUx5tRrM(m3F^ zb=R*i=?in>H|J1%X8#i3IGLXx9F_Z-@7$57NLz&3A@mR0BH9|!kM42wqkAO)Ji|B0 z3HFs)ZK~jY)0zQfOFOkKpZ`)|=|*VvF>k5FBb+L>mCumlKP5k)FfoB;gW2Sx@a1;$ViUv#p1nh70{{VS_3?Bdh diff --git a/tests/test_metrics.py b/tests/test_metrics.py index cd0699eef..fc9fd22c6 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -436,10 +436,12 @@ def metric_check(self, name): self.assertTrue(el >= init) init = el for el, elref in zip(v[0], vref[0]): - emsg = f"wrong value for {kref} (Expected={elref}, Actual={el})." + emsg = f"wrong value for {kref} (Expected={elref}," \ + " Actual={el})." self.assertEqual(el, elref, msg=emsg) for el, elref in zip(v[1], vref[1]): - emsg = f"wrong value for {kref} (Expected={elref}, Actual={el})." + emsg = f"wrong value for {kref} (Expected={elref},"\ + " Actual={el})." self.assertAlmostEqual(el, elref, delta=DELTA, msg=emsg) def test_accuracy(self): From 896a2be7eb06b81d6674f185da42b657e300d033 Mon Sep 17 00:00:00 2001 From: Lorenzo Pellegrini Date: Tue, 14 Dec 2021 17:10:06 +0100 Subject: [PATCH 37/60] Added sphinx doc for the benchmarks module. Fixed import issues. --- avalanche/benchmarks/classic/__init__.py | 1 + avalanche/benchmarks/classic/stream51.py | 2 +- avalanche/benchmarks/datasets/__init__.py | 3 +- avalanche/benchmarks/utils/data_loader.py | 9 + docs/benchmarks.rst | 233 ++++++++++++++++++++++ 5 files changed, 246 insertions(+), 2 deletions(-) diff --git a/avalanche/benchmarks/classic/__init__.py b/avalanche/benchmarks/classic/__init__.py index 240040530..a097d25fa 100644 --- a/avalanche/benchmarks/classic/__init__.py +++ b/avalanche/benchmarks/classic/__init__.py @@ -3,6 +3,7 @@ from .ccub200 import * from .cfashion_mnist import * from .cimagenet import * +from .cinaturalist import * from .cmnist import * from .comniglot import * from .core50 import CORe50 diff --git a/avalanche/benchmarks/classic/stream51.py b/avalanche/benchmarks/classic/stream51.py index 1be59742f..72d8d79a0 100644 --- a/avalanche/benchmarks/classic/stream51.py +++ b/avalanche/benchmarks/classic/stream51.py @@ -247,7 +247,7 @@ def CLStream51( __all__ = [ - 'Stream51' + 'CLStream51' ] if __name__ == "__main__": diff --git a/avalanche/benchmarks/datasets/__init__.py b/avalanche/benchmarks/datasets/__init__.py index cbe8e9f08..f5c5f0148 100644 --- a/avalanche/benchmarks/datasets/__init__.py +++ b/avalanche/benchmarks/datasets/__init__.py @@ -2,10 +2,11 @@ from .downloadable_dataset import * from .core50 import * from .cub200 import * +from .endless_cl_sim import * from .mini_imagenet import * from .openloris import * +from .stream51 import * from .tiny_imagenet import * from .omniglot import * -from .stream51 import * from .torchvision_wrapper import * from .inaturalist import * diff --git a/avalanche/benchmarks/utils/data_loader.py b/avalanche/benchmarks/utils/data_loader.py index 104c8d406..3a421696c 100644 --- a/avalanche/benchmarks/utils/data_loader.py +++ b/avalanche/benchmarks/utils/data_loader.py @@ -41,6 +41,8 @@ def _default_collate_mbatches_fn(mbatches): class TaskBalancedDataLoader: + """ Task-balanced data loader for Avalanche's datasets.""" + def __init__(self, data: AvalancheDataset, oversample_small_tasks: bool = False, collate_mbatches=_default_collate_mbatches_fn, @@ -95,6 +97,8 @@ def __len__(self): class GroupBalancedDataLoader: + """ Data loader that balances data from multiple datasets.""" + def __init__(self, datasets: Sequence[AvalancheDataset], oversample_small_groups: bool = False, collate_mbatches=_default_collate_mbatches_fn, @@ -166,6 +170,9 @@ def __len__(self): class GroupBalancedInfiniteDataLoader: + """ Data loader that balances data from multiple datasets emitting an + infinite stream.""" + def __init__(self, datasets: Sequence[AvalancheDataset], collate_mbatches=_default_collate_mbatches_fn, **kwargs): @@ -214,6 +221,8 @@ def __len__(self): class ReplayDataLoader: + """ Custom data loader for rehearsal/replay strategies.""" + def __init__(self, data: AvalancheDataset, memory: AvalancheDataset = None, oversample_small_tasks: bool = False, collate_mbatches=_default_collate_mbatches_fn, diff --git a/docs/benchmarks.rst b/docs/benchmarks.rst index 0e28d100b..f51f3846b 100644 --- a/docs/benchmarks.rst +++ b/docs/benchmarks.rst @@ -2,3 +2,236 @@ Benchmarks module ============================ | This module provides popular continual learning benchmarks and generic facilities to build custom benchmarks. + +* Popular benchmarks (like SplitMNIST, PermutedMNIST, SplitCIFAR, ...) are contained in the ``classic`` sub-module. +* Dataset implementations are available in the ``datasets`` sub-module. +* One can create new benchmarks by using the utilities found in the ``generators`` sub-module. +* Avalanche uses custom dataset and dataloader implementations contained in the ``utils`` sub-module. More info can be found in this couple of How-Tos `here `_ and `here `_. + + +avalanche.benchmarks +---------------------------------------- + +.. contents:: + :depth: 2 + :local: + :backlinks: top + +.. currentmodule:: avalanche.benchmarks.classic + +Classic Benchmarks +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +| The **classic benchmarks** sub-module covers all mainstream benchmarks. Expect this list to grow over time! + + +CORe50-based benchmarks +............................ +Benchmarks based on the `CORe50 `_ dataset. + +.. autosummary:: + :toctree: generated + + CORe50 + + +CIFAR-based benchmarks +............................ +Benchmarks based on the `CIFAR-10 and CIFAR-100 `_ datasets. + +.. autosummary:: + :toctree: generated + + SplitCIFAR10 + SplitCIFAR100 + SplitCIFAR110 + + +CUB200-based benchmarks +............................ +Benchmarks based on the `Caltech-UCSD Birds 200 `_ dataset. + +.. autosummary:: + :toctree: generated + + SplitCUB200 + + +EndlessCLSim-based benchmarks +............................ +Benchmarks based on the `EndlessCLSim `_ derived datasets. + +.. autosummary:: + :toctree: generated + + EndlessCLSim + + +FashionMNIST-based benchmarks +............................ +Benchmarks based on the `Fashion MNIST `_ dataset. + +.. autosummary:: + :toctree: generated + + SplitFMNIST + + +ImageNet-based benchmarks +............................ +Benchmarks based on the `ImageNet ILSVRC-2012 `_ dataset. + +.. autosummary:: + :toctree: generated + + SplitImageNet + SplitTinyImageNet + + +iNaturalist-based benchmarks +............................ +Benchmarks based on the `iNaturalist-2018 `_ dataset. + +.. autosummary:: + :toctree: generated + + SplitInaturalist + + +MNIST-based benchmarks +............................ +Benchmarks based on the `MNIST `_ dataset. + +.. autosummary:: + :toctree: generated + + SplitMNIST + PermutedMNIST + RotatedMNIST + + +Omniglot-based benchmarks +............................ +Benchmarks based on the `Omniglot `_ dataset. + +.. autosummary:: + :toctree: generated + + SplitOmniglot + PermutedOmniglot + RotatedOmniglot + + +OpenLORIS-based benchmarks +............................ +Benchmarks based on the `OpenLORIS `_ dataset. + +.. autosummary:: + :toctree: generated + + OpenLORIS + + +Stream51-based benchmarks +............................ +Benchmarks based on the `Stream-51, `_ dataset. + +.. autosummary:: + :toctree: generated + + CLStream51 + + +.. currentmodule:: avalanche.benchmarks.datasets + +Datasets +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +| The **datasets** sub-module provides PyTorch dataset implementations for datasets missing from the torchvision/audio/* libraries. These datasets can also be used in a standalone way! + +.. autosummary:: + :toctree: generated + + CORe50Dataset + CUB200 + EndlessCLSimDataset + INATURALIST2018 + MiniImageNetDataset + Omniglot + OpenLORIS + Stream51 + TinyImagenet + +.. currentmodule:: avalanche.benchmarks.generators + +Benchmark Generators +^^^^^^^^^^^^^^^^^^^^^^^^^^ +| The **generators** sub-module provides a lot of functions that can be used to create a new benchmark. +| This set of functions tries to cover most common use cases (Class/Task-Incremental, Domain-Incremental, ...) but it also allows for the creation of entirely custom benchmarks (based on lists of tensors, on file lists, ...). + + +Generators for Class/Task/Domain-incremental benchmarks +............................ + +.. autosummary:: + :toctree: generated + + nc_benchmark + ni_benchmark + + +Starting from tensor lists, file lists, PyTorch datasets +............................ + +.. autosummary:: + :toctree: generated + + dataset_benchmark + filelist_benchmark + paths_benchmark + tensors_benchmark + + +Misc (make data-incremental, add a validation stream, ...) +............................ + +| Avalanche offers utilities to adapt a previously instantiated benchmark object. +| More utilities to come! + +.. autosummary:: + :toctree: generated + + data_incremental_benchmark + benchmark_with_validation_stream + +.. currentmodule:: avalanche.benchmarks.utils + +Utils (Data Loading and AvalancheDataset) +^^^^^^^^^^^^^^^^^^^^^^^^^^ +| The custom dataset and dataloader implementations contained in this sub-module are described in more detailed in the How-Tos `here `_ and `here `_. + + +.. currentmodule:: avalanche.benchmarks.utils.data_loader + +Data Loaders +............................ +.. autosummary:: + :toctree: generated + + TaskBalancedDataLoader + GroupBalancedDataLoader + ReplayDataLoader + GroupBalancedInfiniteDataLoader + + +.. currentmodule:: avalanche.benchmarks.utils.avalanche_dataset + +AvalancheDataset +............................ +.. autosummary:: + :toctree: generated + + AvalancheDataset + AvalancheSubset + AvalancheTensorDataset + AvalancheConcatDataset From 66878cb92c949e3e7c8c4631be136d8154df2e89 Mon Sep 17 00:00:00 2001 From: Andrea Cossu Date: Thu, 16 Dec 2021 12:00:25 +0100 Subject: [PATCH 38/60] Changed version --- avalanche/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avalanche/__init__.py b/avalanche/__init__.py index 1f26f41bb..36febab02 100644 --- a/avalanche/__init__.py +++ b/avalanche/__init__.py @@ -5,7 +5,7 @@ from avalanche import training -__version__ = "0.0.1" +__version__ = "0.1.0" _dataset_add = None From 30ba7a9163ffec94d342300115bad2374fa2d2da Mon Sep 17 00:00:00 2001 From: Andrea Cossu Date: Thu, 16 Dec 2021 14:51:11 +0000 Subject: [PATCH 39/60] GitBook: [#126] No subject --- .../gitbook/.gitbook/assets/avalanche (1).png | Bin 0 -> 112023 bytes .../gitbook/.gitbook/assets/avalanche (2).png | Bin 0 -> 112023 bytes docs/gitbook/.gitbook/assets/avalanche.png | Bin 0 -> 112023 bytes .../gitbook/.gitbook/assets/avalanche_api.png | Bin 0 -> 51341 bytes docs/gitbook/SUMMARY.md | 16 +- docs/gitbook/contacts-and-links/join-us.md | 11 +- docs/gitbook/contacts-and-links/the-team.md | 39 ++-- docs/gitbook/examples/benchmarks.md | 3 +- docs/gitbook/examples/evaluation.md | 3 +- docs/gitbook/examples/loggers.md | 3 +- docs/gitbook/examples/models.md | 3 +- docs/gitbook/examples/training.md | 3 +- .../01_introduction.md | 47 +++-- docs/gitbook/getting-started/alpha-version.md | 178 ++++++------------ .../gitbook/getting-started/how-to-install.md | 43 ++--- .../learn-avalanche-in-5-minutes.md | 4 + docs/gitbook/getting-started/why-avalanche.md | 19 +- docs/gitbook/how-to-contribute/guidelines.md | 9 +- .../how-tos/avalanchedataset/README.md | 12 +- .../advanced-transformations.md | 66 +++---- .../creating-avalanchedatasets.md | 63 +++---- .../preamble-pytorch-datasets.md | 38 ++-- .../questions-and-issues/add-your-issue.md | 5 +- .../questions-and-issues/ask-your-question.md | 14 +- .../questions-and-issues/give-feedback.md | 18 +- .../questions-and-issues/request-a-feature.md | 5 +- 26 files changed, 272 insertions(+), 330 deletions(-) create mode 100644 docs/gitbook/.gitbook/assets/avalanche (1).png create mode 100644 docs/gitbook/.gitbook/assets/avalanche (2).png create mode 100644 docs/gitbook/.gitbook/assets/avalanche.png create mode 100644 docs/gitbook/.gitbook/assets/avalanche_api.png diff --git a/docs/gitbook/.gitbook/assets/avalanche (1).png b/docs/gitbook/.gitbook/assets/avalanche (1).png new file mode 100644 index 0000000000000000000000000000000000000000..141b3481b699611de01db23742f88dc082f6d237 GIT binary patch literal 112023 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

BI54O~zRoBjaxEt(6hB^gH6Z0Z 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 literal 0 HcmV?d00001 diff --git a/docs/gitbook/.gitbook/assets/avalanche (2).png b/docs/gitbook/.gitbook/assets/avalanche (2).png new file mode 100644 index 0000000000000000000000000000000000000000..141b3481b699611de01db23742f88dc082f6d237 GIT binary patch literal 112023 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

BI54O~zRoBjaxEt(6hB^gH6Z0Z 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 literal 0 HcmV?d00001 diff --git a/docs/gitbook/.gitbook/assets/avalanche.png b/docs/gitbook/.gitbook/assets/avalanche.png new file mode 100644 index 0000000000000000000000000000000000000000..141b3481b699611de01db23742f88dc082f6d237 GIT binary patch literal 112023 zcmeEuPY@mv0FP|ojq@m%ppji2Ba#UE=hJk*LUZ^wqFrRARtp4Zy|$M$3I%8@J~ z?j1h!#&qB94Ex)=%L5&0M`7PfN*!3p##L9c+sPqYR-0Go>~}Hf#twT}`{lP+eKFkr zB$6FH$YEqcM0oU3ft&JpZ*9(k`V$z?9KP5|)pVbl$|Zm>HXf%KQCsXNp*of8mzj%h z*PEQ8A)y;Xq1bEKSbbamB0YA906-WaJLP(F6Sc&krd$Fi{!)#3sOO4TST@bq*Vrph z&^Dh_qU%bL-VLEKoGT9U$}Gt5mf7Re+YR%lxhgT+h7vWz3vXJNr8&7D??mw!9fLhd z`3eS8$oFarCG!f>gLMfgAuN$-UdmoAUPB$-K20^?d_^1q_Q#&y?5#OFk2`)Qs ziYbUp_=Pgf1-d^3+*QEzb}UDaFC8SaucC}aCu0tU~Fn3%=3G|lU zV>~VB*+`2jzXOz7Mc+Lj9ot}m_uczmq;@_;-AM)sC^?fF*LPAdL)b>$(DTJUA?#=+ zI5ZUN?Ze>LVtq#TC^Y^6KRr-}oJKS2q|VPE{&0Mu&O&C<709ihV&C_*h15kir~6c9 z&MFOtHC7~ba|Mt!GB>pjCw8}M^bYG#L>;J+%8+&wMhyh}B3>IT*)2Gqb*A{yq86y` z`?u~zbaG2s&_$*9;{vo!Pq#K$f>sv;ld-WkmLplpx;s=-{c3UKe(<2g6)+(;UJ&B2 z*k1UmR_lTb_6I5Yj*X5}J%P(>tSu3_qN(rh;recr0zACRmtj_WCTW6>aMvCSu~s1{OX({u z<0x%D8?XPx@8ig)qtTl%(reF;iBLAknp8-mU|tUUN5UHyQ;|+v8>pp1@%*pWA1P8D z$gotaj^%uVx-t$MJOCe6Q*d;sEy*J?jmE+kxnwRhbq2+tf^hr}UI}1#I0eQsdN99Cl-1H;964 z?U<}=E0Us*1JTfbEL&`&Yca;{{EVVAyRGwF7V74}KD>J!y*`7H^R51Y(;BxSwDx#V zY|hc5P3<>Vy&eOdY66x#evx$EjXbUxh-JMy4AMG)%`r@Hj%0<$yX1V(wwiETGrDTUPY&i!%?v{ULGoO0+pR3Sm4Cdh z-eBm&erU?Q&49nM{2B|~E6}pV+I#z4$4;L{Xj{GAiN9y9@~n3wGdc3eL>xxv6ZmO$ zZe_|=R(Z`P%E@+46)6R@>T1x+7A1s{F={6uu7##_T&t{Y6tjaJ?$H21-yjs#i%R_* z+RYK>E%Dyz3HC}Co}J{`N@(ZODh&mi>(CDb3t`Okd=sQ;_iYP{UX{tM`kTWyw~0R@*a zq&oSv^Wrf#yA4hl&?jssrt6YOLbiX1mpLTiO`U72b+#WsO^>~9g2 z_3nuBkplZ^CGoF_+3jB4%T+Ak%L~M;HlG|~ObY5stsLaZW{b-4b?+Nz;novR_|s@{ z1+tTx$Uo@5+CFq-lQAEQx>RZ*KKtB;C949~dp2s_uFtYoxOP$9(^`*kR@usxhicye zDKWi+dh{eO%P8flSd;i@g(t0!hoY68+_>=VIQ+@3JMA7iDMS$B%h>g1`UCOY)rI)e zN?KYVu6&~^>3KR|sNo1b9;d!P8V`|YgFcL$YUfbVK)$>4P@LnxhYUObt8Zi-*Mb|@b z@+3J>VIMEAq5I9HvtS~ZJ}qT@Y@LPXWr#%C3iemT1|6WKIC-`DuBnz#Oe7`Zg7ZW4#*3|AW+vL;CHHl# zrxj1-u}`-&x@zxSJ<2Igq!~JptQqa@)pet8mtPjLSplkxOr$S z`?l|Pj~kf$Ek1|TTJNn8*lx3IC5r13J>MvJi8E^vdazh^)Tz+MFT$ND3z`l#@j8T$ z9q00AiBX~5yfSE`*Bn|zDy-3tJRL%BsYZwa045{3o&OBlsp_!y_KhNNA+X`2w)m3A zR2)zH9&g!Vy2dnXvkev4S-ksPB)1IFckqu#;#01}`nsSLfB|Vlk}aA>&bOp8x$QKCa*H)^s$UVR zu zEL-D``+B`%3e8-pP%5+#ou0RDAv$}!CroS0+#e{(iZ`G_N#t=KH8wJ7633=6+k>Fb)!dUB}=Bt1?T)=odJo<4+abkr;O!63p8Hu(lG zvtI=-^!A%>G;cts=%=UM7^Pq#@L9F_sQP~C{+)F>(&~cr25xg^r*m#}&^Xf5C_M+s zX444ip;v_Gj`4;gwwPGJ1EdnN+lA#Fy3;sV&Y6uG&j;Sa+z-N`uWl4vKgWIByVbye zVO3m?^qpEIcg$9z>NaeqyTScyGQY2g8r3YZ=YbLRwsYi)VH;mo-}C5zZF## zmb>=c+~O^fnJ+q7zqr;r4iUMBofzqS*kG-y^auBeORoQI0H>mX{nyYFUe zOjg_Pzh5bly(9_TxPHtpgh8EzTP0x(|1QsAtS9kb<3_E-qkh|;kK_C^@7Vf$91EMN zk_-Qj`H3Z+QEcezXVDW@Yse+fhHi@1D+@#W$TFs2xr_MZ=U-1yQocu@L zUUPIY)A#Gea;2ozv+OG-r~UPCw zgq`AA1!?tP*t-vdP2$@Bn;6I<7^6lfBZMDKT{?{&Et$nt|995Zb$3u5Q+$p^eLz$) z8izlLoKa5$x7*2PVc1W}+>3AOOre%)Ay1;jjeL)^4=rE&{W@)Y)YynzhLKYIED(!{ z0OB{xEhdshpPhdc>!Zxr9T_?znqM0dJ;fI~NToJ&?kr%;kddJ4RoqNCcH%{S9p&nxC`(hoWWSa7u$RvG3;Q@@UQoR8y;d)lX*RD2pLSpTfUb_3{C&l{vtu5mU_aD@ z@a#8jn};t&xcfT+8U`Fk)Fm)+$ff6Kq+bcTiDq$)oia`z6v4;i5?yHVS+%Tzb^MT# zZmc3JY~Sw1;SaQLVI8&QI(9C%T>9PcT@pUV?v~G(gR;0iZ@(mZt;goQ`p#ekzm$u? z)r}lJoC9G9iE`CF#B@=z;Ft{Z+A8R#;*?eF+s>Qsa99=e%e&BWh)ZCf+D08)$kW*I zFWd?22qfmSI~nv?-gyNaFTQj83*{+`Y-;{OdEm-~T3N$zN+Ty;lX>mHD^)W@r4G)2 zqH88b`IF;zK!(=#TW#Rusz#7q(|I?=L)&#)O8-m}NFK}67;%F7ZDsRAwby%1KpLmu zcHYI|$mM~Z1=Q=5RY&gw8nswA61jr7fphA=g6L}Q)4+VH2(i6g8?3^hqQQyZ$E6?1 zngK3^RilyvUAy0=HhNdo?&j{jbxW0c3e<9FcXD$l)$a^xQ!9Dq?7c+k&zS^Q%Qfo5 z-dDoSV7Z5cayAN3HU$?#I$D1@Kx5~hAnEZw$d2il&Yz7_aPXm{#_`gwcmRh;JQ;tIfdpv2jG|vwwH+U#<8f@ zo#MX*?3{TA9YW~H%Vvy$EAp*!Nz5m5sblsp=J4SYyvGNhZVIccEKz4^{A+@RL*7`P z=;D!sHI$SW_;%#}#Mn|NYx}-BLL_?}Fubd?WgSRUXknNjA6p>U-b4T(dDRWK`4J>h zpSu8E7A%D^WG6f8c*=={;-Sj8X9v;tFv79ZzCP<$ec13&p3;$wO;yG2mLZ+fIlM}~ zP!O7S#)=MPxviXNCO7E|f21%EN*+Zr5rwA(qNYQ6-s(BOLiroyv3a*rYfN)CAwIFA zVfZYz!zg{YTz|zCvr|>WHd({PW0>>d;$&wNiqkj6boLBx{Kfd%Hz)=aXA%8LqZbItc>-^Y4LAhXg}wju^Op_iOE0D4*!ES zqXr(^TI^l+zd*-CthC+UKhGPt&*Q?EPK^<#=7K#)9|CwyZ~_Z=+G6>zoD!{SAZm{f zkLq};g-XXDJYFzbPBf~!ApXbTjtMsNlTCw^6a7LNi%r(F9%K8UC6TE5V&~b(0+%7A z$_z}3?Q{xGxT8BG-m_Q!&Oce~6c3cNfu`naPDjt*e7R|B15dVsLF)ndKvA zjl!3n6v;N*5X3k39es9BxaBvSw>#78WI`c!-{@TigF$X?h0U>o4^fBla7QFVBNuTc z3zf1BFD75s2d*eD<$EE2xnedqy%5fX-{(bLT7Y2lUBPfGDE_%GY>tb)`EHZ{R0{JC zTmnCaKTGlV^|Jvo69Etm061pI;`3(Ol-KFhtvVA-@zcOZZSy4{hV7|13pTfaThrYJ zhYjRntD`h(w|h7d%&AWJ3@@s#?H9353NEgZBLLg>~EY(KrW#zH4#u6@6jz z-Y+M%>)`!D)2>Gx7uZi&G6{^~NyM7ZAU_`Ghbyt`nBj|_$Cu;{HO{pz>nk&|&4wo6 zbZ{5AmX-lWAMp&fzE@lP;c+xE-)eGyhnONOEM1j7;&w@S$41-Yy_*F8Aoi@5mP6NztbRp3bqny@{R-9&((QP2`g3 zBK zdSF_zL4|Y!__ytn#U>7yh`(a_eYg%NH+A9XK#9WI%>B-shxY7@ zkZvczu7P_&*LqWPA6fQ_DA7|5;gKI0ZRvlki)u9(x)9#fDV`Bj`lGE=b08&hY`V=* z%!7}=RKEzMO-zsV?#O5?+H^tIG|qI-wT&I@ zPAa}_dlDCyZlpYA@s%7#+e5cqLq~-=o6lBjlYgF}tL)9I8eqx{EyjwtrBB! zB8pb$Qmh~c*vbgIySprht+weU1V%>QAvhwz7vK|v`0-+?qEn{mp0`k8@_eSrk#BLkXs*GGVOb(xQF+E71}!tV+{C`@JDr&nXZ(J4%YMV6kZ#CU(m|TUH{*Q&Y)J=m6CeE;S!G`T>MB8=%qPg8Lkmjo%iA}1?^iB zTzr}`1GTl>;%iGcI#T^Ckv@SnF-71QG3~F;W*U9|+z9`p)BmRZ|MNxvZAoh3 z;-{_m`Ry2TNa5?KB{c?0^)rL(Tsl8(zoztWI{h!&|G!`KKil*#)A`Rf{g>nYkLCWy zoBls%!GFBzKZo9bV$=T`o3Mxhs+1yO+m}OnZEKP;iZq7)G$rb_q7jK_k`Fg%KYjkB zGOA?bgbkR6hSP(x+i@<*o)3qIw5)AL2V;X3zcwh1XEdRv2n^Zw3h~$m*?W8ccxxcs zG9ML@D^)y}quz40t|r^P`OR0&_)J8!tFus0SIlRhgtW1&a;Npf%x4ll6Pl$s@I3i+ z8fN)KtuJ5Ojy9V?s7Y|-qP5}xO3`py*jlM+aO8k#QNHo=2ZiRq{70td6p}9)!HSPx zmcTCfqL90_$y>#V(aDs(e6>dVkWw`*EK47O>=_T!HRm} z^Y5QKpxh^A754^)`3e>f0_->g%QnxV#_2jcsUjxBz?v1%`>@6;CV%9IP&N-bt}k@;k~owY%5$W|NwS0f!Fz){!43jEnRKDC3x zH~4_P0qVz)T&Z+Jn>(qaOS0MVnPPlN_Dpl=Xg@q>Vsz8I?0 z>p0#|7mJeJ(Nx&KA;?{{dPGD3n@8~~2T_bS!UNX)4fuBMmkulSq1rLn2Hzpi!V6p%~S8 z@G~Gi|F;l}ED?aBE#Bz~u-Y1|{qhyf=GoWN;})On(8*x*GW;k^0}3`e!G&zhckKtI za0nRhqtW?Q6BNm=cS(GAdn^4JRR`$MQ>fmO9;|rYxwY;b`4JpslQ^ZYr6Aw(nHXRw z?CbBNf4|XD@v=XWofsoo!ItO+eAzHkf`@ibze2e@9uhnRKVT%E$xw;Z5kIyquCFZX zt@llB{}R6x6l3n8?_G4({yj3WmigBbKr8YKM1Yyx9s|?AkZ@)o3D=-yUCUqpb1biCR%18t*9IC|P2DPPOc$z|@WOJP&4peo>=tFy8M?RL2@F@}Q&ijS zeOo^q!~LrsUKH;Tajs!ZLxAG>dx*(L@e|j@uS-xS;&gX3j7RVv`PI( zA6DSP83@`0-zEqO9N}kidfEviD5E7GXg>zGFE5}fu=(xS{?_zvy}E+#Gkc*O?`%SEm@NO4Cl|b3?7} z)r-_fJeF(lqg7)~&to+|Y7OlD5Liq zOe-O&!&HH~nm${JNO8c&H%nl>?qPR~b-2_lv1Cpf%U+Yn=qr)%P9LG3j7RD!!##?N zx+UbmPBjZpk$B|yej!G%vgN*Jt%2$EV|5c)Z-x|lb}(Bx*IbE1zz?AY)7jDc6v<~o zR68gj@hQ@oYU44=VS{$O77(S(C~KlXzM-P&HRe4Dx|%$c`j@}R@?eijV78e`8{Cd zwqkC6N)}UjUe$Tu)XIrkBM3gUg6&O%#N2VHM18uS;)|=kOflg4N-rfUCW$YGV15+# z^=WmxM1LmZL0ZJ3kgT69JeB?Uc4>Jjubq?Uj1F#Nbb}wkhAI{_E%Wv^`F57y#$P72??0#u?Zmj7FP7@l(X1Sf;VV3==2*AaceH*TaSBvI(eMNzY0_sltYPFWuPpGl2`k~aJ zkVXXmisyCc6=f&vSBu1HXci`+<8JEGtz*91o=M4k4nxh>u-VytV`2V!k^$fA!KB-X z@aO3Xjc;0zdR@>N`2rrXcSV+f&#Of~+rDsGM*0eO^i7G0IGK;NKFV%Hr_w*?id3;b z+{CZ8$R8}X+H$*CFND_?k4A_{yl2s5ixG_8@1Sjwje7Q4k5F6`v$AoQl!u;mg~sqZ zU9dhGKrrsHGCNDTnh8VZ}mAnFhQit&M32Q=*VK=*p+ zc?HBv=CZMS>NqL5KYh|Urffyys`9wHsn{x0D3nX5mdsy*ekm>_C{`^TWB#l`jja$~gy@mrq&q!j+aF>4@2%cV>U zz&lF2ba3{Kuqo$grU2+Iq+QaD`@LMyIYwH*Fy(yp_iODa^2xq}V;cHFqv>t|$qU~1 zD-hpMdf(yRsBj5}$AN6xiRM{9sGZ_I`wUyGM}B&2P9D-HW3L^i3HY`;gOcfa!AA;d6Xk%%e`5K?!#OE%hL9Ht zrKo2JwT9EINq-+JB=@G!_~j|>Lb@l2iufFq4Kd>UXo<&@o6U~C1A5RkFSpYb_7%Or z$O?H(YXJ&GUG7C6rgF)oZG7{^MF2Lt1+!V4uHeZ~c+U{LBy`rcZtXBzs-LRr16Wqh`W)G(~9?M9y;i8K~ zBS_@OG+SwiOBz>hja})O78CUB>y6^;BN2i{J{V5G`XR(j*ZR)!a#HI8HjXk|36$MC zE9A7a#8EF&VM2a*dPHG;Hhp8JoDqFO3?<@|i!@B@^7P|diu>>+l|?TxHhB;-`YZK& z%uON=V9)VVvL88$RFL$`>Qc$aUx;3h_vqCVa~uSKO8w>#d0Iwv|M|I`wuxp_7N--K z9}*h=r{U7i`0yx~)QZaAYy24L$4ZIUwP;P8)EhQ%S%PX=&*26NPll zbl*>AowBsFOxIStfvT@$ga%cu?P0`<=AZI)ZAI>If>mYJM&ts!yC=7pyJ|mjo*vVY zQZ6-W;0=_NP555$Y+keCG2!D%ei{|NRclvQX4U1_)BF^ND!V)q-`ukCw7fW1>m5JM z9~SkRk`uv3(2eXdGTAtvy6Z+#g{m*tTPT={RI`wn!S5fl?+BlSUztRyuBz zgK~nLW*`x(y1K_7U=O{wIF&#Z*-KC7AJU5168_&NiZtjTnlnNL-_4oKHRpBf3W)o6 z;PKjiz265aWKw52i~0EQfyHG80Umg~N*1KRtZF$gBr6T2GUlq)riL)wYrl2{Tg{&E zv%ZGh-c#$BX-d`s8{9Ek0|VgQhGCX{OO(y*5LZf3f|)b|jqZ||D)D`P_+*(y+1yV|FXkQ|KYu<dk7Xc%u~$} zWYAKYJfITC;zeU9gIh}wQg54-lu#V${dONdZI86HA5%ll8vDo;G2bi@11|oEM z70#7P*15&v1)%-fnH$LG_d5LX<=R)QvFmMiZWz_-#Q@dTs>r@4oc2um zN!^Pz75=#L*fvo*TWQ8}ki-rIK_3yjAAGvJ;9E*&A*yBi3I)k@AfRv(vBa{{sYzXK zKa0Sy{UI`P2N1w4vX!@8DI%2D^8H#)DpegXFmy?0ek~b7YAvFL4?%d_uQB>`!a=}NcrT(QO7pNa9RJcFv9-z?>TpUfqEY&RK9=RI*7aU=fJ#Kn zsQX3T{y0~!wgLRCIy7%=3szj1)ij8m$_2p0`FkNGb0sQBRS5qs!s&bXyEd_N|<|Z zkfP^etu~nT2fL7^Hd~7ei8QvoWT1BW)R<0PDBso<3lWi&8xO1 zi791mcXkZef6*F;0}pT?QRuS zIyFkjNMoOZG%Y6gZrfaU7`&S6e-GrN^o&_h(1i@hd|GJT8O;#y$f1n8QE(J=I!{c) z9YE%*k(vqB6i~QY?|Z%?evc>Chm6&2h+&52D-)6Y;bgeSgyPKjTa3Xql?dS$%WMKD z_#@DXV7byzM?NRg$i%v$RXsMSI~7AaGJpF_V&+Ht@-yRMtm>cyQ>8*IDDOvyfa?d2 zBG9ZiLOj(nUn9uumtcph+z-uVIY^lI(}VnArT|nOz%kX%P5)Wc{o+OY3M9-F)(cki z+H^f09U&3z(8H53A2y^%a;AR;Peeb~TTfAT(!1ww4925e$;Jx1(-j|!`AE0&+jsA* z{JOtB9r}^`7U4(liLL%bzSfYO>~D)z8v#2U5!o1A8gfj*2o>43E%@odx!$y5^SNus z*X9R}F4w^H4!dEM+vBU7S15`B`(oTh`3j|RWp|#p+n63PZz9F`cA1x~63_8wiYfP| z6zy@#A+Cqr9rE$?+7~n!$w}AVPUcm1Z*;*<%DWLcYq*p4r>n*6Jj}hgo_I4MNLbzb z&MXrRS}4>-ZSMEId3J9co?Z)1QEflrvyshw5AbPwMQyWR=fTwGeNBYehndVwDNmlk z@4+n0Wj>S6*eGjaxZ|Qf0rk!N!66}Ou~J)Qo^pBb^zi<9k&0zc#Qen^U-{r`<1w`PY?@{ z3({$f(kUMnQEDOA@5?=4=!P^h8>S$asK>%P`M1$))Yr0N`M^t(@t3^&IsVdlUmhi=l(E_%wsWQh{vYKR~h~G8P@;9 zz}HtF;*RZ&qKZeu2$$dfP348dmnkuCLI)UnS|df!xFTP-@UuUpv(enD%~Bc(FdXuKS8%Nu> zlZB7kw<=!NkQ-guA4?<6Iyz&U--q%fyu&&vMYKJdGYpVO zHP}?u!q}(80^nQ?U7Dzu9kF+FUFT`neQ>(i?4hLnJn`qQny!408S-((yCc@k~{ za?Idf`>7}K$6hhAt+UU*}FfX509dF?H)WF2gX-SK;)F(;lx}EF`4H&f=gr(tCSR5uCyKr3ySYIY(vw zZW7t*Po853>hcb7SukY|LKO%0X>rtUCtYyWczVEiWK<#$qnzB+dHqzMpJk&LtSG2L zFR>GBu7mZ8Y0a;W;ippp?&}r)W~m6mFU*fHz*hhPCxpDc|w?#r}5SC$U|aS$@kBO_288b^FTW zM4_o`zN^WkcSe4ItPwQsJ0yw(J^7-E*A@C5m0W1J?B=!29X2WsO~nKEbcUV#{xDkI z;T$lm!wG4rSXRu!CM3J~4E!x~S?E%86S6S~IX~R+4_5RBg}{kBrkGSwIH;SQ@myLE zV%ei^z8}9&rgS^oMUxXIVHsz!g?j-wd#bFtnk5oEry`)(n!X!SOYzV!^Zf z2VfnwzFLS=%=d{VXeifz^=Z!H-CutSv-pR4CiG^@5<+B4-y`3bXGXEhCE zD~+xcJNdq)$9vbFo^$GTUB^87`NI8qcr5~h*h%C?_|};vu<6*5nh}>D%($dcAX)zz zQ##l>gTI$7MS927AI{B=N8Z(5!4oQ#e)^5x9C55=&t*HWyN5R{jkbg{{+coZ1saUg zzj79Ri+Vv_vLB1B_)Z-{d!sojDq{?si`Y89rV%hznMiW%W+`fzc4f7Fn`}{yI+0Zl z^6En|g)uJ^R}^maQZ3=8WC6Bc0-QlK3F0Re) ztib(ZO?haXu+6i%kVnHYeTK{71g^-P=K-4jg=3fL76Lxv_|49D*M4b)#MYUb2HNznjFblg4 zRAh4(x!*6`BwXd>gCl%S_Dn%mj%8%JRc2;coYJGTq zD38W*;Nh*?m-mmijvIfx>%Ix0%7P9m^0){9M>a8Db2PxGPnCGv%2W|wvg1N>asJ(Ll(y4WfA~#5?FiAE)qA+=4+gV2 z&N$&R@wi^3NSCYpA`3%TPz=Z}WHYt`I#f@@*;!a*p21)Z!@L9lwqFx-2T9F-Lx$I| zgHV{L)QGku$-+gmXl}{Dq))T^%%B!Fo86_!|7x}29<#H z!ib^E^grmhaUX`venDr&I@a~09Sqlpt}_bX=@bQ2Rol!GIqg10&Rngu`Gy+jhnVaQ ziuG`Zbkd`R+Xz0gGQZtJ6{1)!%Aem35f(LV9Y#xbpiX|c*q=lZ<9&K#D^J5mUEP`w zseA*sz8yUa=o#$D^W^9-@<%*1L(EGtY)=5l|9G`&^|0Dp&!vnW%Gq2=)@w45#a2B9 zRJ=N*2HJjZdE+f~NtnxPHTC-OowN|F}q z>)=H`CSG`vudsjra6tnMY_KfHp|)_coJGZ#y?rZzcz!SVuw~`6D~iZ6yIUbS*X$x# zrZuTgM&CHwvN%Q}vP1?iLOpFzEoaLUCg%t2MDAb)!!saSss_~Lm);8zWfNf$>ZYl@ z=BnX0iOc%?x;p=qx0=ByTozm#vRAv6ewBu8-O*ObGP4$V#I&S;NUUsY%x-Be4kon6 z3towCvCN2DWB4LJKkVUO!4_zi1q9AxATm>#VbsJ8tj=7;=EG=KWU~O-N|k1GCBa@% zqZ|J2NaA|MWt(V4zE2C*m*OKfN|>JL$rG21F+HCf&V=C^>4RtZ-;R@b zV3F_p>B*B+P!?v-lm30R`JzFoe9Qu2a+0XtuCv}oV<=pC%zNx(#x!bO(u*cRTDKyn z(03Hbj1$}@{WhfSX2b6>lDES{ZSUmMe)K822LIs3f-jE4M6{q+{M)+sWcLD1%Os)S}T+ESehr8D=ImSQL9Xw>~HC*I?h1u%O==R^Qyem98M-PDj(R) zOM!V9$kQU%^ynX)K4-K{dKgcnxqtp z#k&&z?bVlEzEVB)j~m$@ykt4j6LE51wn@`UdVlupzRzezS>SrM>4mjUI=#R4K}e= z!rK}!*zQN13+!%p=eb{J3t+G`SjWN%Ts?Vs84#LE%cyO;$Y219;INPn>M_6uIuKe4F_h-9vi? za>Fj&3xp>ScOV?VmhA6RvP-b;#t&)@58VEZ&=?ulLsOKP)FWY)Rmd2*SRfiLqrwPK z=_p@)OTiyD#F)-w5@2U>{C77=-)~pTfS0N)U*5S+WjdKc)#p{YE5LQpmAO@B(Q?Gl z-|)ONx9w4HC)c_(fGDs=8yr>97A8Io=hg`!GC8D~IZRM>FN-7!6h2`~TUBEYn(PMj zpzZA;Q*)@gt2;kMHr4wMy~>)npf=%KVHqi!m_vcs)!KJlo?oOSXQj* z@S)@Mf}Mmia01n_i)PQ)AgaZIeKBKL2{_m0boDg!y4vv=%X+mQxle@x|JsHWR6)@? z(V}OV9s~X0%Er0pP6W8Dg`z?yt8PI+y2bjh#LdrRdPrN#t7MrTkEDAQt{z!1)7RuU zB|5r$Nz=+p;i(WK(Juoqf3Ga}*a2F6yk>?vH`#(h1+l+$tcBxwVwhMn9QiN2D67i?!?D|2+*T<{I z)*UZcpY*T&^~w$Kk)pES6ov>*+dQf73LL`g$}G=m?4ZP{9J7zy#6LsKq3bTs-SFlg znA|V;kEx+?%?9_X+5Ym8M3E`Nj@!~kF|d+Pt^Dh0;3?PVf%uXIs1E0|Cq7`ys&Ttr z$)wvA-brFHL$;z453hPT1=CP zc!~5HBcM2n@LG2=1&0SYnv93=nLJSl@UNP{0gQzE=UoEC?2|I7)*&1jg@~5uv1=K@ z78+kF>O3M3qG4Jh@|f>?5ioz`FEh4yg6GYb8gR|kA6)l4zh3N*lQmYX#*zMhI0?iE zco|Ub2FsR96{If$qy!)aV@NNUUc;8~34NxuGCmwpS>?BU=le1oH?evB{Z=GO0p65^ zhw#rn)7wW4QY44CCcRQdlMFPwEXUnD+1y}OtEb7QL3P0 zZC);mCUL;2&GVYbU&_O@C;MEfVo@8gy*(w8-X_%84bOEuv+HGhM~)gpv0U9k;Rc!jS1;HJsY7uah5(n7tUV|_y6gjv1Y`o!1v2Ic7Sp(o>5 z8Jw`wy->04P#vFsF8%CnJb*|=$mgw@F&F#oy@zBD4l`}@SM$M?H|sbQ^ z%U(DKp`Y+972$_!b3`cQb+T7n{;m^Ya%H&n8}#IRESN+&Xr#xZRUx^rTTP>TA)h;s z7C87Q>UJTHT7m0oJeW=(!zlVobf~k?XPcZJ*zS3c<^NLFWTb{yedmPVeqD~DuYC3w z$PW!cE`v_3;8*>A<;=;!2BVKkv^QcvXqQNE%K+QW)3Rq2DHl4+Gmw7!9UD+xBe%of z|5Kw$o0_je_jhb47qfnkVF;vqd_}6-JHO(-rm15654l*Q)~@D(V<@~EYG6?V47Y~U z9d?5}??zmB;{c%gqq~lM6RL%>j=hxqSd~Cb+mTu;2oErjjQ3wfO(y8%Bt^33!1h!j zp$erCU6@LGBjU`#!?z?@KFT#ml`G0e*V9cVjr`EcY|Y>D*b5q{o@cuVUTwa=f42z* zsra26S%*;VN$tn!nP*-#FN4BT!RCV{)C}(0`!F%Tv03Krk zlJKM&+akK;3f6^k1H$fI(kky+Z|D2_CoHbJyk@LKzos4$>zp1w@%#kNf60S9+_Qfe zeZrJ*+>Q|V!vR{U2p3R2#zIdBfC>R9l8F@wlL-bI^!3Jb@7ZbI;SHW`4X}g4A@29D z;M6zPz7HE!Qqa(l4tuWRF$nDOh+bnp;&1W0_6MW6L2aa{!vjb2^${zmi1>;%pmf8G z#xz7}{f#?5wbkLk&eVBGXJPALf!`{I6=p ze^SmD>=gwm;;Hf3F8X<+T&?X#my^tyqbd-wd`${oN~+hNrfAG3KY-^A-Yb9ito*@N z8n1r6FD54I$TQqeuFm?Qwj#0;`Xf>=L^eKpgb%3U6g*@Q=#gg{17hIYpHbAAXpy1< zLLBsmIWLN(AKGr{P?1b0!!@!4h6`#5GgA!P+jh*@HqvxY`kH1+XhyainW;nlGnB_cznoLJybK}YM zaOa!|!{8xc*jxW5sGt-;5*;9;V05wRE@{X0zTN97%DtAs$ZPHGX-ze=vL3Ref>R6qh zm(rV)a(+TsT)jDn?u!<~S&JhWbbo1;wumo-nmBX&jO$;Wct2as_b$&$t<>RVS);zB zoaBe^%54vbJkW2;WBUL@j8FbDUQ5t>u@RFsQlO3c3msP`f2pHD@i>*ADgT+hm|Wcb9`4K#mzuT{-KcbSF)HJtVR$8N`Cjjn@~kSgo#vU+83OD3D1xuh8Jj{U zrD+;$+o!{cnaLP+mxHWekXOr*X*X$R57DYUN-&4T>_uQ#;{1sp}c-Z&n;1Z}q) zIRkO6%hQu5S$BHUuSb=jWm~b!R6lJOrZBqFSn7XX(Gyqex>{jSicuz9q%sSwpPt3v zR-}La6pwv*__cO+qpHnaRF3|^63S5vPVoi zM9sX!wghxaM!dNFX~J!sv-4xMo;e;@qiGaBr>lib+OIW5>?XVU*rXj)oq~;GTrWy0fgi z3cTx2Z>I9tFv0qLbL?q^B$)4?xo08E$UmnrogVY?%o8v5>VujQ9CuA{Dat=TEp(HN zB6&ox)BSAYXT9fDMD80QXSJN~#7s}oBxRJOMEhyH5YH0e$D=_y$q%WYjAeDO1}<8D zRB1~;+!|+w>%O=5C^jwE1Sd!IhUnqgf1kY$3O=a%%r>cLQ?k_Qpx7=o@6?%ew6-s% zFa4!~E9TNq(ZuoO7+Z;an=!=Zj6}%13GTn#rpZ3?BS+7p+MZ!r**ZBAbbw^YhNX+k z_E=L)UtoRhZ+4Lh3Muj`eR_MlOxl^#c<4##s>0jC*U0mqjfy&zH^<}v^d)>gsv@V> zi_aLb7%(Ci&8%;o8~v=zStg51=Qd1CZqVcz>{*~mYt={IShR?2WBfyUh&5Gk%DSB~ zlV{eajEAOZ2LL$0iD}66UqYvt|uV7@p;$9PAWplkaCC{V0@*4g+{3Pxi zv`)sy1H4?!%&i7T=B2D;Sre<`BHy?vky^9ZI=B8S(}xyUqLRhq(WQMWp~n$~>^L`p zv`?stb7oEi%4$8$qk*6h6@mhYlBUMMDv zsu zW$oDISqYAbl*Izfb@=IWYj*901Y2Rk&i$kHA+3C(>rL|AFoxW>Gl)Jm_EgwCF6A zaQD4rw9kb5POx^V>+mhSwtL1sBWL>x3SLQdR$71}8FyZ8&xxg!D5jjqx!5Gbez7Du z{Mw27$4HF{I0(OgP44GgZ}jW@?VxU+LY2~4K8-5qEA9xVy||5gFY!dpfUtBac6?zr z+SQ`^Z6x18UcCGDR`{odgwXjo$Dh-VJL*y76SBJKt~4A^KSMxYC!62#+ztgfXvCUG zNIVE||`&(5BTJwCqlz_yFhZFnsY)@?oQb z=T{zo>Nm;$3qxFuF%vF_UWX-n46$mIqHZu>w4*m$^sb5#f_xj{w9D#z3i@@+(ROQN z0@$Xl^j9e{Ik+Sv#H03l2}#=d9@cj`)ECTg=T`dC-UYf8I zekTqEaj~~qPY}KVy%ZlK;MsfFFf2NlnY!7hOWzHNFv*QgmD?4sIjNNLUgl9l&NtsX z-n)YUFn0|8z%q@T=HbHAzk7wgnOLVD>Xex-;zaFHMqaq@Mp||un|t3hKk>T+42}wg z7~4Hs>DT4ojQ_L>X|SsGv7Yhxw2Ykn98dr0nS?wME5H1rS5qM@w2vj}_~w{zTI;W_ zpiH4SDTwjkT|rNH(t>3lJIwvImN)zHmX^ow12@TXk4#fo`WBl`?d?==H2DLbzxslL zu5XT~!^TQ49Rs_;rFSJ}QzUo0_O`pS z|9D2^oYVS>@@W)V>~vEKQ_1w^}O@3V~#+RpgmB(@p zTR-yrJlY%*cIQKOwq;8g> z|LastcdOvG`JyXjVd=HahOK z@|~iu-$7U?-wRRMrkS0!QT2`?Vz<^mj{1Yck4QN5;=3d@Y}C1Qiqu@6x-yC>*y2!x z%e$EQEY=Z%S3K?x4Szt>XnSgII36+lq}EHnJoSqmAt*5>#`R_{f{cOF^`903RZgYm*Yj8PbH56s;mXznT)XGd{v$p8HBC{<6N-gKL7H=N7h4uHJm#3O|2`kfg!g5L z)rj-qlz=0G9#H-4IuJXuhbYrzUUW;W%#}7A_j%kHb6x8tyvqSXWwZnYT4-v`h1ZGf ziHb#_wx95>iCse%AMcBz z!m$QlS{(8j#t?D*i~Pjmh!thsJ9`N}M_aSXr(BGBfMjP*!x4Ce{b6+eaQqp+>q^MZ zg`*Y|AH%=Z0c+}Fn5)g)0 zl8ppuy>(hR<=3DC9JaONaa8&iUeAE@$7C^6RGckAs(dl)t`8VG^(tO5qFS+(f`R^; zH^GX;?9_+0xg}a9RuWIAb!#;Vm%7s@w^sAnuDhxV57=JfzhVHl3Bbp0elSz($w(== zbMJ_F-0^3djpA8lUmPNx-lfcrAQm51pdToCDSh|2!l3x0!uMr=`_A{+^^S+90#5^V zYK+)<^R-Cr{EC8=NC&kWoYho*-9H|Jv(t_kfu3~aVWxYHL*9(qI1j<8AGl`W>}u(~ z)(d}#lX0bCebw*XiQA=qC6|gBSG0~_q`}*GJs%pu9WPX%V&pN48*PD-Bd>Po+#jiX zRQsmJb=yU~K%HAxv8=k$>Ndfvu*&=XcavD}8_#p+FP#d2duze{2&BOYZ65^^Q8cu1 z(u8l1?#@VuLvBlHm`^00D^FwHdSCy7uPzn~Uo7pR$D7=`%ln`ka#k|QGg_&o3B1-? z!J%3OZlFa6H$M2`>O5?csjDutY~GVgV&y`%s;)#AfO}AD7y_T%wx|}(EVWvfS?Wr{ zU?GkUw7j+-)gPZs%jC&CI;H3ar=GHd7KTbK5@1vpxRo2hB8Jc9A%^5-)1OqkDg-1D$4}S|4+v6LQId7|Ff4`Dd5^?o>#Sd$wiQ z|5Ls4elx8nc%@EmVk;7Z|1}sD2h#hJSHh^bFV$+MI{o$AostyLM+Vxu3D#e>JU`iO zv!8Hbo&2&{aV|1#FTZI%uSPKWF4zph6#*XRVx}O+A{c-oFZzgMkPgZc;=aI64Pq)! zx^qF{e}+eURYzkUVdizcCOSpo9TUDHV%cXvRQy*f((vF1OMCAK)`_U6g`K-{2H=X1 z_=gwB3mw7k2aA`JDZ@QL+vxdi)s`SWn{1CBLbjl?S9hr63RrTP4}QOclM6cQZVVdK zJ(2ivd>{!9jQC1ryrJFTwZ8{t*bKjY!Brsp7vCQhCt-{Q17VLda`njo+|N*E^p}6* zqM8KNU``VM2obBi1JZ2;SN z*ZQ~(N-hUX61~+z>pj(P;Y<3^-jwk19xd&tYV&D9l9@sl?kp8M$08nOK(4$Wk5 z#886;nevwtGt4O=jp4Jcj2#p+Bpj|Eu|KDFvL1t_>ws%Dmu8zWH7MAw1h>F z!FO#<=d#g{_?SYFUPIfWFPZ2z=8|~oJp(K4Y8tg{m+BXU$5obZp9!f9dR@9n84ukB zEyM&C7fRx{kyoRjr0R2{MiYSE_(hDR-|qtMfK{ra3f7xJ?lFR;cMx>V^PmzUrH8n< zbwd*wz#j)g#zhGnM8V^NhAN)YO5)bwS7znpZ64HBD+dr+s-f6A1N{3EY%RX;Q(7ChdEcxHe{@Z>%C z^^f!|XGnGU2rLtFq5kKi>d5GGXJDM3V1OTK*lFT8=@_}dZGVO39kwmKAQ1On8XBUKFXpcLgl}E- z_4NsREN^dZ7LDB$rB)W36Mv1l*$UH% zvxQIpZYoc(gY_*Jy~i!fP&m83fp}|J05d4|f}Fo*?#cn$9d&SPq$Tc{z5WEqS25O&4@hxL63z9`g|D&YS- z!E(<0y?^xr{C_^#|4i`T?#KThRb=(FreXil2g&HC2a93D>h?L~;nU$7c}+fx}Y8m8D<#?1^8Y2PJ=#64+_il6*vD z)^GXJQ!<}9KiYtxPfW&S(ImJ++!-t#2QsWNygwg#WSH1N|LuEXeSaBiRI|UsTXjt> zl4})#L+gjLciv1A%MDdnHs|%Q7H-K;l}qC9Af?}YNOkM(Ube;JC-ciT32<|Z&~`uOpbCkiMXWH zBDoPg-I`U@Om9j$>%fCj!36^w(noqCMsG|>x`fw^1Z*cL{-`^}-v1Bhe zThhJhHw>S;m)Zz3aJqS^`{W>rtf~HZ{XveN+ZS`khMM0J%@ttGim4 zKSUd3hPl1v>C|PsU*xw#I=+8hAG&LhDWe#hS?H}SBBz8L{2t!e>qM z8CHWwgt<>sN~5->Pi^1t(cJ-qL3bG|5c$CVMBM@+KyjeSbXi+-uOEc&#;lJ!xitTJ0E1G^K_G@jAK2>eRFTA`2_DjrPW9HY72c`x zWuHWdy*z#St18Eg(xVFVH69I=s>FPGQ?1N00rgYQ91C6J<^#&a+{@Ooy2U!~uC~>)mHhpSIG3A!m zmuB?eus|aWUeRRPqsA9gjqxw%MH4j4YxjQzvG^D}i>sCpW{E(*Fb-egLxB=^#!cFGTHrC9fp5v&zBA4iJ(@V#N?~}`FTgw3 z6!)U$_X=6Ik?)mF`jx7njv%fGo!aNpTd1&Paih*bO0#Y6qvyg|C)Z_JWqtPg*4eL%eO+2yqLr|a)#@{`4+hvzMl`>&5<2w8G1u?h{&LO&@C(FKN zcJ@~V8)3)fjExhNB-B00fhf=)R3%)N*{tJ-;l_0~G90z1Yd4mmA2jV=p08*!O+LT* zGF01-5&zS=WSMIY4va6QWhS-9yi*m3LuY>YHjQ@o#o#!cT}=Nn@^be8`{-7^@B0+K zw9#satdb&3TXinlv&T$U|x`rY+P! zHT;2;2FdidpI6_bI5%{NJ4|s=m`kM{avHa^S=y`>BN>@+lS9xl>fU54hNykTf(lE_ zZMr9QrA^7T}UpxJ1B+N-e6YKoR-{x8Lp7#)$tYc;g3arzCs zbNYt5cx#vZW6~MfY@EK$hAN+`!X~o_dOGpH^qbb0_)+HFu@G@H7_X?ZFtftaj2I=oUHk%#sW6O1Lt1)&ot{{s?c3M& z1&NkSCMYYzz$vd|ctpHu`c?I}?9aO7ujD-CU;TK0Z8*D%twLPg0Ob!6Z-L%RXDAZ2 zS4z7UlDAEOJKV_eu@y`$7;L?-dp^@TW^ND7!#n)u?cZ<%xoMIzVf@#N|Fp0a+d zBkDxmb*k5N`s-aX$0JD)oK0(Q1$LI+r200ysto%NCR!Ufv+sf(kgKAPU+dyiY3AkkXS zb$#ETHg!J~)lbntB+cIWOY*-53AX(+$SrniY-%Hcz>Wg86A|SGYLaw^+MHay&m#2$ z)ptyWKB9LGpz5>X-U|cSwFjD1G|rIDktc?yUFetW7+x8+zeV=WQLca4|G2Pj%X!LU z{o#Ux0t^+#|GH&lx9M%VXqpz~Q}x8C>t;qakwi<=Ej&*)&wGo9UHdS?BDQPN!lxu ztNJpdZE{2MP8>nmiK=xYZ#^gqD(v+Z(>Pmtkyme+iPFuprQ{~Y$ry%{oN7ADtIZ~U zFYHEf{Ac{H$BtX!C{U@a29F$Dz4M@0>5C>y#d=Xo-k#6l4tct~qQ(vSGGqeIm+mp5gAEer_UVJSxAyMs4NO>Y)&Zt@yE{w*tY4ngPe8J4T z6h?f*)?V*URyyt)@1baPFP$?j=^YKNs;Cq|ffSu_0aSDJe?VYwy_lS_Uuj&GK4W%$dUl2xMS2=77=MhohH|z~)rvCHgI+$9@=dzEq!u8f@JD6u#f&to z73|JwS?CfQq>}ILCj-3V2i;uw_B(WY9a6^x?Ed2#bT=NZrMrYhTYYxRLtN;j#o100 zC?HWhsUK)+oRb|dGmV|si*DcSr(l>C60~$xo9`!E`f%gkxcheR$1R*UUz^3MMYWNL z-3#LCT6QT`tZ?0b^jtBwlz+d;Uv2`mu`o9KcC5hjgXO|7u|wY(t69EhGUQ@XjfNe! zQDI{e87fQlu}R3ZI=dRpbNx_a!o8=0_PvGg&Vw=5em}=zFwtMR^;tCWvsx+?R3X?< zH)3-pN&oiqjaBUMc*U4ndBL2?8%^z7(fdK^=8L0`E(}QR|H`sN2cZsvd)sLOn^n$Gz|25Za8k?tDYKj;(q#pBS}P?Fx@zQ;M~mxjaG7$W%V2dI^UPRlnl z=gJj##(p+3+dg&zAy4XXrf!yGCU zUU67EZXt^g2yHL)syO_oh!OZ4_1OH;ZxJyCcTv|>#0x8?>MY!7f2w8ug)MS5pV@2H zsEdgHoxhs;HY)cPW7ay;5OuTiORFT17pgt6#6<^<_lqDxzUuY@ZpqHLrDMGAF<*S= ztHUskCOmG!6UGh(x!ef@*Hpx}t!s}{Ynw*fG@7nBYUI3B1lvpVI4*3XeMC;Gue^tC z7K3B&x1=Fr3P}RjWso1g?BPRx-0$xUHaDhDd@ZcHQt0)IINi(6N4tL!j9yAe%Q+3p z6=5T`KOZ~ss`+kznZdLC`c{X~!{N*5-L4`&j`M|Q#nJBT-Zb6@f>kEa&t#~TIm^dK zyB7wyT4%204ozmq@7Ep;6TRwL`KEZpqe6DllZXa~eE{j9jVww)zryylv*DiFRm!i9 zwkCQ`6x3$u`;`0FHa0ntN!aq+EC%`M&J#7W@xM?Y#sD>Wc?5GI|Kqsx*MB_5s-b99 zbBI5~-4ptIxIwrIzulC5uQf`b0|u{%NW+Y7#;5ETllkCA?cC80!*hp9dtBzD^1kg~ zW~UiDg9Qc7#|;?7bXU}KPzYJ|a?dA?=B}8O$Ax`2LZWT8ZnXy@0fI_gDYM?{URp)lh@Rhu7;i zv%F^wKdU6`4oL?k1zq|FKO4|NP#NK4SBhzu6}IJD`$A3WAF+Whd^ zze$8cqdu#mXs~D!FFt-frLm>5)=}=;FKEQUBe|WCS8AFyg?M2P*11oenv268p{;7w zs7DV6iWK)7a(r;RWcZw$&VuSZfJ%{V@8*5ne0aDmw?7Yma-j{{&uI(%W`?Co)E^G7 z&s)ZBZ{`MjKzcPO#>zL|((hOcuvz%LBZ?j*Ru0p~T*>2$K8QRaMB z_Bm*|I42DEy;EK$&H_e7z(z|N$927&n_aifpfpDE^o47DU!!2jjd=#AHF}^gEZ3LS z-FAwt`OD-un6-CkeZKSZWzD~zJ<)1lo;-eOi+omO0z3ZYJKyme?kC9=!7ZZDASCSF zus!)%PUb~?g?t2}W{d=faOGiWOEGk4eNJPTPfbme$vg5pouz82Z9kK(({Ghqtd-yQ zv*H?;LfTTDX$*5;P5oZk-2b&%%%|f!Zlp*tmoQUUpXIVaCvV3?Pfagvval!+Ev!Cv z_$yXiKXpi5Q$gCXG;zBdv5+d1ZM4auj5D)GPeDlU+b1gPmF`)!DZrX`lIe5vGr~g5 zKKGW-gqsrL@Qb>2LJ}QD_Z~hOSzmOBQjHj2CeulD+G(d`qwO&uq7+7z#MZOrD~jnq|AhMJ*l2$0t*VJKE8)GsHZ5 zwarZAM0r~Y&R|hLDK!@ccroGr@!hvQjFEnE{Rlgo<3O6M2Wy~~CxIp!3A~Wg zB7M|QU!kl#6sq9)GR8LLpvv}auF>MQk{v!h9!Pyu%nD_y{PJF@PWg0IHukd6&!?<2 z&zTj8za&vM>vpbUKZBVL^+1PKql05M7$rNg5)-4$?4H~OMcX4X(Rk?(M7%6UFA^Sv zi?^Ubp5oKDdB?=XHO3+i>3plA3YdG|UPe39`yYh$-bsF#(LoR8rGeAG6Je0ED3-@Z z46!dY1QD^53BOI$=&mks`;NQmeC+*7c*$yw#1S&<%im+^{4Jy#uR8W?7yL>d(zAqgWpYbnvTeHDt-6-ir>h7M|W#wD7J3Bs7WW>QW*MwP2Qj35e>3G!d7`m-EXTdxeL-5>jWd!jnGhtN8I zqUSghXyo_+^+>@(H-{D!-&Wz_xHkgyX7goSl$Cd!uSOu_W#v!Ymfk8D05e5s%A31l z7tq-@#oe!W`@#{_+?QhS%sb;&oHoB;YDz~hpi(v%pXIDsX6CMt&@??*sjE+F^>&!~ z$E5~K_FPVS)+vHbEvfuDGv*zs?di^Mcn_qVCsKc}*rw0p_6hfrGiqZ;iK{1;65INC z)zMY%QZY@J(>JP8c9XkgXcn;C3a0*~Y5a1wQS+4#=N4uq@g9=+Vx9sdfMzlj5JPC80wKH?L$$`cr|?bN;|Udt;Cn)4#U~* z=!KeEXswScpB8XFS}jc2_I@OPcFStJm9cO08yb;}61P4l({VKdP>yrQ#?{>U ztE^N_#p^pSA5#r@v`eN=n8jg204+!f`9U^C14=1QsDgq*mSulh{D%)L^78U!X5ILt zoB_iVUpcebpbc^X{bUAT1O+6iguxT@d(JlYA&S+S>@78!IGq~Z%WXBd!j088?>tGP1R#K>=l^&!O+?*)Lne>n!}gd z-Qr11 z$Vu`%xNuO}kdx?WHp}nj*BDCeyliQ4bH93hAL0fUQxYsFNH8n*rKiE5rN#%y-hVsS=f(qlM2IhbbsO0}m&0%9>qv2iMqcRI4@60|k zj6gYHCa>v~Z6(D0!RDTiCSy?>p#j|`OAvm54}1VRnmMv#5;7nhZP>xG?Zs5`0;C=I zE5;Kp4Af|_voG}Y)Pe)Tp{EVTG!0C1t!kFtg^j|3os>}KD6gcosIV4=HjHx9%C z>2tx%52-UafY15bC|bZd6TkyMvLfwZYx2P7`s>bnBDC7C@SM(eNypeZsOBX>X8b|+ zP`r~Ks;30(t-YPr1vZ8Z3J+U5`Jj5hQ`@p%Ve;69^l+jIHVWOETqmRvh4IIN=?`8! zmV!HQgSi;B!p5lP^ngn^ytI3b1yP}IX_z1So=ipzTIGH0s{2~18liw;7{xQ1anSFE zkwB_|omLQv^-<61BA>Ebhmk|7z<*1y8w+Fb*zMZwo1D?A&Z72EP^V#>_G_9?_X7iJ z!69w$a-=J4O#uj4r~kY z7Zyy~%$`1t%gV}<8WgSq6hSWx*sbQ1!N34%sE0}^jQ1r~1|HC4bOfmsa|82zDC?t# z=0^3UFTP=+dSjSbTJ~@>Uz$L{Hy9DE&otIIqRvx5;10ZC>tzvPb5wKM5aAsDDqbiW zYA0|f2U}|;Y^?b~^@d|IOQT7{TQmUylQ1k&ZmA>=!rFY*|; z9Z_osA%I^k&Ji;0bc8T%DPVRq131pX4(laBQi9}YZdu_bxR7%K5*~{vpW|^$nNJ(L zPcvoK(|JiBqd=&^0X-He1qJn-H4wz3?vf5S@C`7VkdWS^RB-BV*g8UBWMpGtG#DI! z0aX#&=JmYTuEy>ux9m@PrYbTFN)HOcB0sRVY@W@>>Tm}^ z)U;>N18ebr^#Y_AP%i`r900-iiy1VfLL97bo0Iu+me_^F+Q4cOTPjBlSa3y8Gw`n1 zs;6k&r(F?>Jk+*8MR$OTG@o>!gPsJSB0oNQTF|-xRAk%QR}t&5{-aKcl^Mga)-BCn>W$NKZ=xIw(~JvIBx5Uk(Rel7`aH zG{VzK3!PE{qHGglvH6Ac4Aj(qCcZVvmre5o#vdwfXIEHZH>r6C6Eiwl^NTh$I)%#{ z(*;^MSd$KL=2Q*bPk{l?VZT!rk`(Y5p(&DJB!S8jZu}5M7VF8m$Ur+}>xTF(NGGL# z!;cS-`n#6abJCNTo_)4@L&zkK0%9J>cYk7@7*O_MF&F7$szI=4G|EF1;HAmwr#Wvj z&5XjLVq)OGc>~wMU9C~1JXmN5J*RNtgC9I;D-!#E=w>*3tq{=16Ceuy9lmq%mPs&3 zgN}cQV((^O%Sb~@7CLGI%;)f@EVpqhw9U)H;evSkAPbh)Gz0 zFVdr8MbfC}G(l*6S*YjX2#`T%|6U{MSPTaq>f@F;=+Jf2i*&`x+3Y$G{i!%}ng=lc zAYeAM-^Jl@2MVwfJuPlXXuviQ!j>bD4V6l7B4}DL2g`O-dl3UthsS?3^vt|3MLTW zX#$X#7x>y0R&nHRW5!LC%{SKD0cvO6ej@oHU$U2JOFFKcy-U0YR(Rvv4ef{~V@&ru&zm>DJl*?r+_V&g0;L*AMrox#xdl#e?tq2-mBEl{^wB23 zh@R7=f14OLD!nfAUfKM65$~#8tu7>S3F8I3xnvN40)0%~;;iro+1V2S6lZaT$Ie$Q zbnE#6Bmu-Gstf~St>Wkc7Ct_WBvu_7-}A%L79Msm!Xq$%i{#tac#uA%r~H+n1VLcS z(dtS$1=(YMgQNh~WU_LuEi$0{DhGj>g0I6#wh9 zrdfT5uebMi{cp&0;b1Y)`S=fjxCdyppXuY1Ok8LK(sw@`$<_@08&Z_KX)#CWGpLH5 zW%Xy`a|5MX%gf1?aNf}a8>Ip|k=83B;JDB!2_T63Q1}DtIXWPSN`9zoDV3gX=LC@| zmhJPB#k&w){M`Kf_&8eGd&S|8^dv*E;@oFEW$RowKb!Z_0Ydo{*|?OB#|Di33YbW% zJ`%|(26)nHn|M_Ki85o@EWZA2@R1l>)Ad>^0?T*L79u%((#p%qmX1aZ5vH*sBMVjz z=pfhy0;?s+7srA-P=d{#(&C{B4VVU2yHyk*a*fBnJ^%8Z7Fv~gE;zyOhv(+y#gVO}CWUQh3%Syt1Is8TLLJiD%TFt&z2N3$+n=^d~KmZZwD{ z62%9V9ryq+Ud(_O{W_51X$na&3iOz|1Z1Rl{EnY&r|VqB9*&QXcWf>$b^x}0XD}P{ z64(_s66a~Fs|A717U10|-0V~tq`<6t@7yyIgdtFaoNRc|s-CmRPQe{;0|y7ykeOj` z<1`eTOc%>%t|gG$>(EL7d>JLO5c_o8q3*Bt?AdezYaCee1s*W^&;Om#lgk(#Z;kH{ zNaA)|ru!19o23f6?qj@*UM39jx>w0Be-5C#Q{jQf$y@))IiqZ8bpk)H_%unUfmdq7)TX5okv@g3J;=5DG@0 zNQ{@FGD2#n=ZVmFJn-cx-|^0jNXFtUBcx&CR2~q8F1-??K%dLK&i%z$F$DvIw5{=S zStlo_phuq_W22(5-tk(;UtXUyBN9|abrzN6l^H=i64Xf0qcTFq#xJJPjIg!G0M8;6 z&=BaY<_-;dqwfQ^>*Db^p~A8mxw&0%uHp2cAV^+ep)21l!sdySXID%tEC*MG*M(3$ z*NmUv-?Gp@dQ?3uJZ!T}tmhCo<38+`F zeQhRnE&hq=;SOwp`t@$41*&gf=N&_@z*Q;I7~;M9qXyoEQyz=agC8Z)M^_Z*&H>gP zs;)9aL*)S;XTqRwD=DrDtlDd2*iaLOxQ5kUo?zJ`h~NCCMnP4@JDm&0U7B{EefwzE z=b78i)Ht2H>zjVycO33hj$t{;Z@wG=2?d*Fq+qCwNT3p~LCpZ;M~2w)O;b%5(7qVw z`dS~YO7e~b2{Qf!(fvN2v?rrkj;B|Ae<-~7jR4!2M?gS8E|NDaKmd9V6Ja+|5eL5M zMdJK;+a`wR3oyzF_?&?B2eo(@loiyL#-uM~!8>Ly>H;*BShs}sl}f?Et9Xk7#Cf%k z5BftolIUy-heB|Ozp~Ny;#e~@hy%u}D88-i-GYiP=jYdCW?}Jtp*=Di82`YZ6u}I( zu}0$m9)O^LXi;-C@_c!xe)o8JGE)}U)bPjk)rEH`YPeuR%klQ4IdCd9ZS4os_3nt` zrt|NoACR<%xDD8{`D^5&K?#D#$|W*EtGDq~7MaH7ypYPc8@QV0t-{`TsFs(P#})9v z(Q135GCna8N(CE}F#`V2?Q`nD#LUcntvN~VvOd^e<>7#Z3abf5frJQcNF~A@@PREb zYP8j9!`TZ>?IIb`s@TRH?;!QT3!(JCEKTv+$WznReQ{c|Q@6Q9d2JM$Y zI)45q1~fG2sfb;2~oX1sr`jnd3(5F=R`g9R6 z%9~MhuOR@dvjUBiKfebF4lo6n4`B7rXrT_)%mx?HJSE%3sCWQ8z&V3kfZN+`(dQO9 z_lsy5crFw2Oc@bC{4j)XE)SgA3AN-jG~P}*v|%eBn;cEPLjydMB@$k9l$9tHWB}S*9`BHy$PB_LCPkU)SZ{tRT&9E*6BRYZy}7DD&~&;vn!2 zU5d`#3S7C(AOd#a7u2HnJw36$OWaqZt+CR505!edqjx`76)&v$(btz$2zpUkmqAR= z(keC>K@XAz&$xnJbNs8M1xTf&5n2TW{lJ)RnBaN>=5((OCV3T=Ds~iM z03(X%fi`UvW#`}}Nb1LK&CWH-!J5VIAeE=2*D?wnDXJTO&DqL_vxJ*KPZmv#3%P)57RbMvj$^6xU2O?TOlWt-h9hDIi7*f$usDl)| zc%xvfLWO1PTw_9rq+_4KShY*UJ7OqoLU;faczV-&8?_vRRj+2igRP;cNCm(Fa}bD- zIjDTvgc1TDcnwCqN0^`vdPl*ik>Wx>uv@d+?%=OGfKU;E> z`2^n#eUCI~rxn?2zm0v$*t5jpaG(Mv3W#0doRH4g8TfA+PcclmJ?kY!v)a|ZvzDUuYJN}Vvg^k#{>4bl@Xx(Q>;=H)M7yS z{MK#XJdpke7DgHjm7do%Q3;kfU@i7^Ps!{Xj8Bym6yia3+ydwLQ!4<_x|(Pd{u9Io zFvf}qsNMfvT!8ceBssifGr~YQ(me&qlQ0Bj0q!7Rdz6C`{XcXf{I@Wu!~16;pHWZGH;bdSzBZpO zU@k%(U9s;AYp?WvV5AUq;q2uI1&G2P_)~PO*~Rg;%+%iAp3`@xoGt=YVQOHa&dTgiknQ*p zz&`s5vEw9K@_^b>bV;2TAJ_0Ainm8d$EpwkC`h#>D*#GSy&;Xu03;=`o6GVaX(E&S zd_L_FglhWwc1!8+-Z4FzatPJ~T*sF6`a7W@DQY88;GsW|;b=_;K)mV*v?SbCbE6c! z$%w%1i9tMmD1Z$CKwPwtaG8rgHHC=xF-h3=7ybGXIE$gj{Ozacd%Sut-e!C8U8Z^O z#DVgl4Y+=QxD?1$_(;qjzz8@Pk54}?=t0NQKdfdO_JG=H1wXufDSYnxTbRrhz>onZq7<)iB_293Hz zY`S#W+1X7ZtQw)%LD_=RmnqJDaWysktBfikK;N54&f^bYwPgeW&KtuP1E`8N2LUYFE$0UvA7sYe6SSs}p=#zH*C+%^K;btD9Xu zO(B$Y&Fe?Q4%_Bkic3zmf+eUq_%n#9#Gv1Z9^Afaqr0o;{4f?@yG=3wc&Kp_=;Ss+%$WwIKZSFfMEF zYdg)(q6D$Ze6_3f(c*M+=O0|$Zd=-<^{B1DMneXC7u& zV0VV)4kv7=rK8@v&K>QncUh+auJHy`{arPVi%YqgG4T}@;JTR>W?)ySVo3N%1R7!g z3w-P(D*u~3L;>eUCA;}ONzfnQP3z1?qevL<(>K z?84;!b5==lEl?rUX;vLI!2dygP-XB1u4wA-uo!{jOQKKYL~pTE;f;C$H`#HCEk+Ds z0h`trZR6K(H!5DZIvq73bP4&5T2L1G%#R}Wf6upN+l&^m-TAOVc;I_tK2&!~&5(25 zOi4gs`nu)McBd!-df_iJUkOa#_a$=tDYf2vv=vw3&2{Mo?E4zQNQxZ3>B1w7NdBU;uYEBBymNCJd}09ZC_N<6C>9L^N0j- zVKH%$Zhs291Rg`m0FrN&^b$&}^B_?=iad(77_(i_PbIA``b| zUkXD3?PR45Bf`+HmRcKlTk5a;V77bPNK>$q1A=2mDk#7?-vMaT9YFEo{`}y_SK;c~ zSX**&-*Av~d&8jh8)cObdiCsTot6~@Hougc9c>IZ!H4ej6cjh<7S*oYGfn^Aaidxm z#$bRPR=kA?ne`OtU*Cf4fOD_;G+k+KwU8dUL6;0Rw5nT?kL8f!2GPOuG!PbrRZ|t# z340qSX(HY}?<(zQ4blLOBXG8qFmBJ)<@F0tN&x}HNXLK0oIneuL9#Ao!qX<$^Mh<} z?{KN0xxJoSLqpCg0i`M382mDvBrPq??RV*M2I@C#lNZh3!wDGgp&uM>PmW65#Q=T+ zCQm_fQ;ZtH+EjBmV9$1PpJioA)kN_=4I4K#68b_F0**DrsiYwDMRmNUmF9qvEzMa5 zl=W`UMp+MgLj7*jb8a3U$sdU?k)u(~-9|F#AHOfb0=tkWW2~yx+5mbfWPSGYb z2?{7j`f;yMHIYu(n7NTq7CZ#7PvTS%=wp3X>y8&auA3v>)M4Z_AacrBfba7_ z#EPEFm6ey%zF}fO8jrZRJ2F`t81Dy*FM;u-((=eyY4qwoDj0w3YQed6Umv+^{88c$ z3fOyf?K}Yv?q(@&FoD1L^*P$-Y#G7rvi2klkCq&1$8ZN^6$%p9LQsG+Qn>1e7BZkF zV$eq)voI3MfQ2AuqzZxt{Usbg{|dV^joD>ogI!x7D8G&5XyOmjF+nn>;P3!onur}j z1~Jfz4YKUgySlG5t=VMxcV(imwm9d0{y~AaFrejs_H|UxIa>H|KoTw0@9Ml8)a6kc zf1wAE+r*Js>Mw>C`~TqUt;3@3y0B3g0g;f9?rsJJq`P70j**b=?iOi~ZV3rVX>bVX zkXAyvOFE?U`;E`@p6`9H>zwls7k|La-fOSD*1hf(8@M5@-W&k;Wax>~_-~YwgarC$ zL4Uaic8G!k#)XRLeZ6xn-#=GOjEV@55|U(eBcN|IfYy%OBK$Anqr?7Q2I0~+_%$^} z%*tN%?Lt4qw^%vbYzi14-)L{3@+-LTuliHD0Uz{!^g|BzF$(}49knU`_jBl19H)wi z%h`v9hBnhpKO_J|0P(_6`(r@U!_A(s!4q7dR|eS85BQD*1brkr%HbQ*^Zwr;E>+AH zI4)iLQnB*so)-5#hN-D3fQ4ns;&n^~SOu>}6uRfYNdS=O1ptZMJkH(+=>X^DRd@Qo zkcc}t6;R5WIrU&|k2v8A-H_|=3ACHQ&9Nmq zf=ky&1ltOp9YR17Qp#)kdzUrK*^XfA&*7mdW{u14WNBadTI{3f`p(;Up}&?4Ng4p4 zc}_oxKmh4{w*MD^?p%xI{jGlh09vrW4*ZJL=H-boC}q+a`JIIJ^z;C#Q)do5WRbIt zh45DkQDXsv_{q6|5UCZ;$Yo9aH!Tc@!rX@L*1v2Z$lmjf_Tw#{j-@3fFE75rO^w_B z%+Eh}h;TIpgo6VJhuh)oT~OLz;oSU>aDsv&xGS2uZ5xW-wZhYH0&XRpExwnBeVszB z45J4Cl?(rf|3An{fXk%I3yI=wXMdgITSVhndg7Tk9mMP&kX7l;Fz86SU;p8|q6kpO zR}d#MhiW`oqITLtDVk;_O3%fW<1f#5@NnYG1yqXyI@u5B3{FUc&Zx0Lh+DDPdDcIW zSJgvk8dE|4q4?>l-IuJxoQMZsuY9x*X!+v(rF@xZ=7biS`ThF9Q#5&Ev}p!)rD6i; zynC^H!U4@Bf)^?mU5pzx{!heIlL*BIOSW}kPf*0;_V6he^%4WyPSkx z*}RClGA^0}4kSGnee5cVJlSW23amavLU1D?37IAOdXGLFv^}uNoJ8-Hsj!IB2O$1~ zi~Jl8IFx}E{SV;y4`1~%Nlfb}!#dzf2CGknY0R%_HkFK!t61j2Mc&ascTqH5;dxRz zOp~j-tFaqZ=xTDv<(?diRPD`W+MmswtjhS`zpcWhcVe8-8b?#ehb;C96@A(V##bYr z(ldy369?lW!!?=o8I{-!D&;h`6Fh!RBLELq(a9`hanWAr(h1q>lSJ)VPbD4~H z0Htb}>Q&Nb{#knInx!f!6BbEyBaa< z2nk~2sf`hX^GE8;FS~4|zBJmwYU8}r6YIkCR>%n*XjS#J=%*J%G;(})+7r`>IL1Wz zltw>#eViY!rZ;T3QNL#%F&OV@_i=BXznX3Ad9MMNw=xxArvFJ;5CWU^oMKh~NduLM zDwI5USxv4_)b%bX7AQ{4cwCoMqEW31@TA~;gFA6EOGI3mtzfL9+y)i&|J*8KnL|g< z@O{3{VsNJ5?nr)j(r-TkVqaHnJM~0e4W^vGu3j`<7>b+5|NVpNG0&uwG@C9k>*tf(XKIwjzv<%Y5O_uIeCtG!TR$lG_V*0lj@OE&{1MN<wq_3JRwa&mHr53MOys9SG!3Dhox?al$3oS0OYB$TH3*Rh5 z=o==8eAhb4S%FW~S8Yu9N@(<#N{$$S-?EM;fb37i66A3}IB<^ABoF*5+-WCKB9w&T ziQwN5TL!6YNcwa-dQ#IS8Lw0qUU@|`r_egQ&$@|hO9yT!?&-1A-5}zH+l~sjr#~M) zK}GxuFn&(H$db{NtDM{pl{>v19!jOCc%Ut8z<3N8u&QfWeDJUEfoFEz&B25B5;Q*) z{!Tfy#2{cixl19vB zLDaQbZ#{7b1gk>DOr7ep+JUU|gM?g})auu-?=k-}+w3?cY?Xo1 zAJPAl!4^-SsXbN)Zt&YP)mtcq)2Pfv3&uN2B>`fkl>+32n1PBjWNd} zL(I8^h2QYl&-K_qd=+AJP-m2>;=Q{ssGd=+0jYV~50iQ*#51F1Pk)L5BOZ|VEQrH> z==$9O>;7t<{6bg)IPp1&a-BY2KLL50@@UNdR!#@@z*Ye5etKc0>pnXp0RdUqjxpb& zrD9$H1euTh?`MeQsCkaRReM)l;e{0$mLl6W(0IR*s@IcET)Ri?*K&tLRsTwt+X%C(no}g>*;h^1S=XbzAym3 zTp8&HuUnbLN)y0$j1H!-6VFt#k%~)F(Se#EpCN~dKA&j$_f!W&*1r0mN^r_CqTazN zmmC9=b;7{-s4n-gKaEfCl>{hG;)R1(;22a48!6MkefDDzzsJ5~c*01o=@0Io20btW9uNydgwrG82*phP;O32OVKBm86pWtCG^9c;)!%SP5%d5w8}4OIrFP;L2qT@* z0Uu@g=H5KiX(ujzC~&O9!yEA#B?u9B<~Y|-U8{b;>dQr=rZ3b*oJt*j_R^dfg7#S0 z@^++Qh|O^7xcY+_oz9!ux$)-a)n_1O#-O&CMI^Y(O7_?2X#z2j3P2~@aE0MWuDK;Y0~c)!3ysNbZ9 zC8rPkH2)>Ti0_r5IzxaH<poxuK}Qc`MTG?++=QV=nMp*TrZI@QC(5HVCY6b^VZRJVsEW+$#xHLN@)&O47d!UW zo--qv0KyN(MfIGbFK2&3uOx`-6$1P_?{2GO>VE@5ntWwEj{6-<=EYY=0?#wiUZ6^S z1nT5^62dHAHhq)x@v!pW`D;5-K&utaW(`G{$ldfvWXcHs-K)r)%tG%WNgV>cY)|Hy zQ>dP!rYll;rpRe!3@E=m(gFz1=cRtoM7b96=g-1k!s9k18LG*D#KQrIhaN7TvJ1zi z{}vAu&>cr3nAQOTeFnC}jcI=zCP>bLi;wRo>ho8hBH=-up1rk34d4{QUx+O`c?Bz? zVXcnSRV%+5>hSm*q%+_G4P#SLkgO_|wmW6z_A3Eq7VvADrh`mo&VxA!Jp;qs)^{gL zFraHr&%&15KbOg|piM)h?SrKl8!^w!5)}Na+bY$jq{KP|K(}c<~+Tq!uE#)v;ImbZH^@cObN_?scB%^?R^D&SGOfm zWoTLLLG9{<1_sxw6XNm9cfzZgg`#l42YH6nyqh?cnwpAiiS2}sc!sHy!uqxoaIqLg zT$rz_v-Jq`-vMkX-Vq-29D!O=C&}%RZ|)$mhQrI|Mdy3-(_byQgSA?F8)q;22kW>T zlNxudJ2MNvg#boDpn%ABiTn`VtRq>#5=k2G@9p>-Z)zK$pe9*hA2R{p=ScA1k)#+7 z(-O6M{UQ$meUbU;w83GT-eU%Ck zTI|FlA)nkr@6y)udiUIAE1A^%zK`AIA%k&X{qjyU2>ep)R7>DIM^a=6 zYBS+9jvc0jHdmx$J{IV772V{vh$3}My43bqaLH(lLIdR?V8eed!Ux;` zOa)bWQEdRb+Za09IE$`I%3o8@xVx1$r<`C z1fX!ixY$WgO%KR0Tt^6*>Xmk%9ercGSR?}{X zy}ibsKl1R_Ka6k|)^l(xHR|jH(mo!T^vKkE&C-;W#aoQFO})x*a2FoK#5IhwzPUP9}^%;I8{Lsj8gMX`o*UcfE@s;Q`pYPZIt`j}g2K zkAtGyY_c|&gE5z1MZAN%=cMMw%Z8_Y8i8p~ZYi;ypA&JzOU;}7LTW!a5#xbLe6u>X zTHW&>&i1&a1d7~wjY}Q_RzYxj2c(%CNCMrw|Ndw!Th^g|0 zkMYmk^3(D|#*=DeZjEBIfX0`iz+&p99L4WTZh&$7OzY9`vbvLt0FZ z%is@)zI~nlkxWM28UKDgkZ>0XH>2QKtlN{`+^Q5iP_0UJ?3G&_yBEYvsSAIUPAh#U zL?#0$o(ZzEE+63HSmj=>UfpWb5E+!Qm$Qe^%lM+ssC0l=Pg^Lp2dxQJ@ZHN`?IE5I zw@bw?eaWO4x({=6D#@ss@LImSThQvbcrO(mBaW)X=1227Ge_&C029o87Z}E_5A5X6 z$NVp+*O@mQ6Z*|_#U3eYPe!d8Fg;e*8*YnzEhsbmHqMnvE_(8b0aFT;pmn+dAP05A zMkC=)``uKkwhmSxJkc%qeh6M$al5*`(RiEtGVyA9*)df#_pycGh5FhSvd$uyGVp{v z{7mZeeyV{ute(t!=s6CUToXgW85Eoy+71jAGUCEbFqg7%uPwA~TT1S804-6g-};7v-#kwm-e8kgd)n(PPSph%1R4xYT35gz za99+!r5HC94LKrpU9ts_lcP%wo=PeFq2sqrrzaypPx!>Q5uc-4BHcE&QC>VWLmNI6 zYN>K&E2O#VGQlx#-Zxyl*;U6dm`?kpN%Wp!{Q?E3!K|V>kxlRTN-FvaXaQvfGZ@~u z2}$`hk}?fEv&Ojy;|Dq=f^fOk-@X8vAv+P`GvgV^)dl1v?Ct|y;ph-powPcKAh5bB zOS*qo2kHv{$3>(%Tixw?GAZ*HGUC}J9M!Gt^>SVei$cZ@-41%1hf<@A}?6hQpKduZ6PXEYjRAO0*BBx=+v-MEq2wBjyNRU%*ll{Rr=$x`yg z^`qlFrC+?2tM~bVTam-+E%uBS$ZHPF#&+GqPja-9vVS?YcorF z$owE|Brzj_L6(9?YE;<%-A4hF`g(cgxvF-X%UjW9v%5P{l_l1D-^*jtTpgeRd|mx| zKZ{=G28c<5(b8gp;+;WHNix{HPQ~fwN*t;Yg~(q`QrqavALN|5I*XRLWjhv%jR6}W(CAC zd1{Y$Fkbxr9WKh$z*^d(S6uKe%v9z(dfWnawqWBtR`#5v>w`}8#^d6j62Er0l#^SO z;QDHq6am=K|A!OEnB?&l6PK{Qa50A8MoX1j#~e#D7fr`eCjrFOM*oX7e| z;8Q4Q@%@xtUVM?sDO7F@S%fqzgY&W|XWD50~ ztZXSA#Dt`*Uk!z&;owXja&NC?sk}td^!R8PA1HE?fA;&A-oyPdh9s3O3}C)mx=E^< z?c_V`|7$ycRdciig`uFneUYmTHXf#>&}{6|p+vCE>oe>{kN zCyyE0rj zt4~h{${#)B&IS*0HT7e$E@m&|a}vinulLw5N#D*0^S+_xg$8`;>5=+s6}X;y;BvUh zJMb^fWYBxhzEJJw>i!It|DxT$kB(6#Sr2)w`)xp+MLj!L`p%mja`8 zNgo0ZBlaY4tBiVL(&mWe132XAh5b}x)Xs;exjfJB_#cOp%m7Rr15}8Jr}S9uJ5HHb z6X@9!v(6!|x=)Wls? z-xm0MyIqpa<-Oq%Dcla_T}nZJIu;Gq0+3K4EC?cw(dllD7S=wzlKrukTS>X_@%bpWS}& z+JTN)!reEQhm@~FPye*@WGX$}(|PY7lA3KLagcUxl202ke0{@3zX1hSD?^y@ zH(1PWZ_k0(51;swKO5E)fB@j9PwtCsOE72av={jV9V4ohWZsFmuw-*=vPPKlF9u%R znsDH3g%`3nfX+AhsR&iJFF;L*%@C@19|jVcLK_am;p(F6>al=vyP6nC<$ZlmBRC**Uu$@ z$#j73>t5CWj&UlLCcae3FM`XW)VfoCs1C^uct<|h4{)$5ZzcTX+|7G|h{i7)#Bb6{ zj|I>uao^gPn)Y&V<>hc9RZ;*`eL4McS+e6CiKLw&Cuc$VgWxe%GG1Cu5?CJ_T(9TB6;4>(e}A0*A?JdVS=X0s5NsDU1sCjuDpze|W5 zC8=&Bu*H@mP-Uh`#9?msJ9kgpj=thnG@gyc5MVozB4)oIO0f-0tpk5kD=W9%`1jVH z8+>T`Ba*glm`z)p%cmU>xa%b!5x1@B+&+27i)s#!qwZZ}<wx?Q(ChcGNU&c9(KZMh6EX&h zOc4w6Kixhnndq6?TiYdhz2Z48(vWqVMDqmID?%)-zA`Z(dG%9_{FEYqSS-ff8|{H0 zZJKfaH_2bmKEw110Z|tBhdfL(sQo4&G`C)#Et_A(i}zLGfN5RJQR?92M1l!LJpTKkpqD@8ut2+7C*O*$YYU9) zVo03ADC856k%Xl3Oci7YY$iq}iNjNE?3F?%r?eYp_*n!D^G)xL?hluhKT7q}U18QX zepcVLVKsRy#G0PoBi2_Os?;y%e0(+)PriNbb2r>$%`vwGc3`FLTTWo`j^=70b=Md94)REMk^DbVOhse-or7^dh?kk)I^P2gi#bn~mFPT}}8P z0OetP~yjaGv= zv+p1f1?>-!qL6yWi6v9LuQ?7%<_VKiS`a%4g8~!t0gu_HQ$1#m1*VZm$W)4S;LT0dfnS;_gNF?q3acNg=y^oGg?im6T(0sqpjCXX zRRv}=K?g4$=w3P77gob07~Y(g4f|g}^&eGEr9DZP z1P+OJU8nK5*$<45Syn26H?HlCH4_5)zpm_NYvpU}e9=DMzjW*HFJbtS&XfStO(3Ow z97g41rq=GGm{aN_8y~siB}5{5!L(Q-R>-DO$gKk8F|84051g-01DDfmi^r1hzJJ4QJ+Fn+(f&U!Szl zl^;A%mfW46cO5;^gWK!qBc%6`=-~BH2M_>4*WiU$DI~8IcxE7CcO;ym+ z`#@LdmIQukNTsgBZ0HJXgca2 zJbGwJ7Dpw%sWsnuq5ei~rLN9iC(`X;rtfl@C8teR$% zV!6OlPLZ{oEJd~1`lLIbufdK<;U=b42@hCOCNSpYa?}3Hb4>cE=R~(+<@U)hn=Crd z1HxxIoUtOMdZVQ1BYNs_oM%7j1%yHxYEKh*@2VzkBPX>2`T96|WDT#o{@zDa5ucOU z=5n!z+ksOCdFG#|aW7E5uPn7Xta^4G>xZlz&`7ff!kkz?EH+(p-i9D^dM**3o?i}O z;BWzCzefe zid@Tnfz-L}PFWdK;sU#og4)uJh0&O{hdQS(PFxrz+L2s3(w6Y>F8g_z@Pfk0qGdphIQThcf z&%NPw?Rqg*VZBqirVf*!yV8MFuiRT3#vPvHdTnG?bIb0v;-Y5t&B@6y&JwAi!g^ph z*N@o%hB$%Q>fhF->Rq0T4<2+Z`nZX8h0{OKv*sd|2qTB^BJG>PlrtOj@xc(*J-RBG zvSC1A#>PH#!qQ@$nQ@g*O1puqb}?URd`~`Ma95(swMQ?3-u<7-dt8ljjzHe#IKyX2 zN?u6Z_~7mki6c3jf0$ZYX?|27%op#-FMSU18jD7y z1BM;Oh+^Jym8FE%oG>STTzb=dk^9?JU8_x}EID0Ur-hP{-l317u#ts_#|=0WJbL57 zs#|d$t;x4^U=isx-1|MgK^b>xlL8p2UQ`s1ON7c$nRbRpM(D{8Ptwk)#-`d(h^=lJc)8a4DbBDBoVc>p(e^3-N1>~0X^ z@yN2v!^qbs2bGBGp}(rqk(U23saeijm_-QQTelX^(=6v$Z;`Xte23@osLt6;K3|4l zH!bF%1{>y3%QwI8P@_@MCwiP02p2PIV71B1(L%=^QT0d0jg zllf&7x{K6KOoqde7hicp?)pcS)akO#?<+y8T7TUxtoZ|I$H-^8hNc>fr-hutg9#(9>*|2S=7i zkH3-Rq>m4HZSpdt_8PA)>*+>t2M^W?U3(Aw$wyLbFtRFs@hiBv?ed5_|I1Vm`DB4MsFzrx#&Q1TP7T_u3#4|esK^-(# zZPI{A>#gjkMqq1T)Z$J|IZ&T|QNPe=`RI@T5r+kydWZ@VfXN=bns2bLr|bXf2O_N( zk^)k`lD&-_Q+@xegZw}1fT_V2U?xe5-`vV7-Zb&pc?AaEiDY>7Ir^@K+W(Mx@|bze z;eGbdkAdY7H7`Y%!*c*Uv{Qy&WOIaD3eb5QXAZdJ~rU&weC0jHty%0&=uJn1%J#U+B3L6S2n{)U1tPtU273>NYyECoL?V zhg)qPfYYMT9vxp|P-f7FlV?oX+${%YGe)-!*fzv!c}$iKxhnJN?4q>@&`{t@(Os92 zcpT)==^eZ%d=ri&ujKj*4d_mr4;MhxRa_bMe7Jj@6~y|@6b;5`&oY#!I-?L6_Z2In zqM@Rq;t>jwPL=3(*q2T7rDZ(?5_hO?)^a-SWlBby`JZA>K9sAe0f~Z zr;Q)Qz=$k|c#nU4o}A}JW&UQjDt-MtW^NfR-U4ATv7o;gRss&WWX%tY1!N&-u`KyBi$E7oI-wFkz+j|StQ z0bPNb4N#m`QuTpd<%B1N2+VoypiT6auHGA?cfXHbp5ti+3z9A&wJU^w4chY)4{*A4s{7!|U9N^^W^O_IiifynY#rxv|26JfVnTLC_E4&b>}hN! zbW9uxe2y2@%pU_n0w1s@!+#iVU0lp$FFJX;hqshJ_2GT~`qHh@L{tz*uEmzLEuOG2 zC;?5bZahAN;Hl1I+3|OspK`2g0IfgoVfaADkYR9tcHhjDysWc8{GEhZ3@u$Nxa$Fo zdi*IFQWs^{-S-TtFT#hR+on9kFEDJZqwgYciho?!O3PiKhi_aH+j>ouz?G18{>UP2Hp}H&o1kRcvO<7m8NSwYVl&wh;P->^*P+sV9^%LZ1%cAk+fy4vAOe>I; zaYV`T=|oiME`1VHwwViI&*u$U{nRw3i|-NsOnC_D;?jR;OLVE;`K_<+CD;gW-$x?` z%pFNLbbXnSn|&WQ9woI6@kf+%%YJgckO?iW5j z|I>S_G=x3QMyHauX4t!WRx`nOdWloLCh@gudCPw$on@*#PkjBxYPyMhP=^JJknsGn zd=He?Y^dWt82|A3D)4&2{A_;`vbQzmroznn+x5Ili%iG~n{dofeOJ3dMr+bmlkh;G zL2{(|_3G|c+0VAO*-^f)(UCU$RptY_av#sAHo1MtWcSaY0=o0ouNAeQ3>q!J*+O> ziD=lc`j|QO4A!@aA@R%&Usxtm^@KWA^<;@aX}^AETQo$$Wi*tN9v{I> zTfDLo9LTQS<_H>p-5o%2-DwyXe|Hy=}s= zzNdJCO8Xs>WIvX)n6X%n@F#TcU7tlFC+4unz92{w^n<~)gUMmCxAA&I1Hn;aM|$;7 z)XKaEWm{_4$Cs``jg|9Zl7SS1=C#mX7u{NuS1g*9#wnIRXXLYyp$c{C$9KPE%;-c` zjN9!-#$r=q0F&5QfDn?3VK^s73%uVWPDLn(Xo^qeWL@F%f$Kv@k+A|Q^KrYgi?-sPd^ry_qc)rVo*)Of24s(ZlVz&8a4^{$2gYQN}0uD?lF;^tG zky}Z;Escj=XEj`2k2Mp0d6z*;gAZ(^Tz)cq0W+-{{5V5+b_;As!N?%$ltHKMKJscs z73#@5AFn_RVlFxrf=}R=rF-sEi=WHebst21jw37;IZe!Tw-Czi$49) zXuXVLKA5ulII?zp6dd?U)DAI6#CtHXPmFWO?2BQGfSEco*a+4Y%rNx9oE_$;r-+^# zc0+ysM@Tcuy_O%H0Q|j{n+2V)%tmQs{19go zQwnKZP;_~?LvYDcX5%5aT5SG~cPtFLX9CP$z*}>$$}?ytL~jLfsL*!2xcEk`Rh{pV zhnAeVh0iUU;*Ho^7bnSj9bMo z3AgTe;+L#3aazUez40Kl<8)b@md4#(8tEE8jujd;Lqdm#bKo!A`?y1tn6Z=Ya9=UM z;@M0BTWWR|RX$`I=C<2V2zczJ@0t4_RhWfT8nu1+Wb|BeIGX4sjK}-XET^+S^T+es z$DQcUpXRq0xh)*g;!5it3@+?OlY~p)ckhkXQ~Udo)pn7UM04}eTmee9q?FelZX#|r zOs)Q{Q(>=o7zif%6m=->Dfn!tP{NJ@{4)nrX%gbm3xnaIKNQS64Ok%o4y8mbsDs`< zdeTxhs9u-qHGzouxg2%^-j(Z-hSbs!KMI*9H#YUbUW8f3FkdFr{=gnQXXq_wKrD_cAWBLMDJIu<_`2Gr!O++D<=G?@q)NjXFVU@Jvmq~6_8A@3U? zg3>O?MSr%c1fbCgzZm&gN9uFKhwtkttTu>!8bEHSlu5#zSB6l|_i()RLZ}r#6t}4L zXJs8S_$8y0Jn7E?JSwLQLa0b&R9||qsnfyuYi_G?1a$2&+131}DsEuh{&s(d3YoyC z^O-v8d3i!F?sIp^jw0#+)|=f^6-uY;{w2{@J{?vLwX#PPM7*YCt*!Ne*7QQPV|`y9 zorN>&ttz1JepHDUJT+1M&8vUJ{@UW;7m4J`xc=ZoYhjQznwjNiDuY90o%U0wOEk`r zbkqX+WM}4gQD*P(s)1g8@E5w)FG`RV^~ngbn?L*yEg9eKQ!8}w8Zx!;r$mTd$&kkr zC!DROtvBD@JTFC18RPAL9IjOd0Ru}?V&*DA9}vL3t?P->3a@XSH&gLjO^la}@gdN%<}d3E`O*hF1-S!W%~Mg=ChAkuy%M0z6SnGIyDfVyp8q#Zizuh8ER zhSAW{wiAhUay*Rpy(p?|t$TINCV0k_0s}F^Wnxe!P`Andz~Fw&!j9RD^5H3E!3oZzN+S={l#bXE4o^(+q0$X%|B_a z0vFdB+YQ_sq7Oz?dIp5(fBKdv)@hL}$IwnI` zZ!adeaw$maZ3;aOtUhX=o0*v^2ZZcLk9t~wC3Aw;APGSNOQX*20S%7DC$Au@NDR zn|?hJ|Fyn=C2=t1X+ZIiT+}iZ3vQ0VbC|0j?T?JrbCgCW0&u`Q@on? zUnf|O6FZ8&9nzg<;a#-@H_w_C&?&2|m5o&*p-2VEg>tt^+c!_7hw$)5c!*t~$RCdQ z<9c8pSYx3F)}QADcVqCkE05 z!CFUjTHpHB39%ceFP~ZpuvEsV^=VKgp;^rB<olLuY!Gf1ek!wYM!?kb! zH6GQVewqtvdM@NrJ?0Yq)&xx7%=w1zw^%qg?o92BiLYy-|1eoV{x4D=99%s-JRFQ# zw-;>ejp^lD$sOZ}^JwwBdskZU`82n#ON6VdxInBEMDki$1=7DT;Ck}svc;RyH3Xuq zY4z*9-qk_z^3S~z z6+zh0Q(G)LT$t~c1_syxJ4ro_25gH|mFo_&)>hEUHM#WFQq7l;ZKxDd&o z+SD@MYC{~P^ss$7>_AtG2QzzzdZcXj1L)C2?`|jQYZBfA{JV%`;dBI14d@nP9{XKNLmH?Gy_ z{Rwi-J|u4Kd+K03i6`lGRHOl;7^m5ZiTUAn^;qU?Xv78`;Qm|TKGXFmtmOU+f;9@& zh|~^tf8~r-sT^bBrldr3d(r0tZ!)pqh@k|S?=`iN{C%Ie)epMwY~?iY*5ZK@OjfZK zbx)@pDQQ+NHqnRtrVLDz z2o-dsbZnUG?dy3vm%hVywoUzTGu?e_`aPZ6$=%nr6}YE|{tmptfX7fO`L1?=CS|x3 zbC#S2c+eHS)EiCww4uU?wp%Q9Bia1#21ZnQFfYG^4*COK^^Q?^JhF|*{qr7RDGGOW zcAPq;Y`H%&i>)MH1lOP?j{>urRKNA%z+>ax4KcvdPLeB5-M+x5b?X!|L8LSiHB!vr zMJ8`<9CL4{Z<*#4U?CaWV($+&!rE|=InR-~$Oj`7XG-&Xol%{UpW?T+3Z*nAB_-DQ z-+TCd^?5Pl>@2N;Tm+aYRB*#0*5DxlCizSK49vm9r{=_k^a`8c@249-U$A{_Q}vem zXl^iS$P+DTZZc7nb@;|OOZayQ%)T$Ql?M4tZKXFPFagh(Pz13^tySIEKa7f%76%LL z|1^X91-LSYN&j2B72Kj3Qs*fb1|7gD6%SL>XJdfYS8)bIPF}kcL?eN->BsApAoI_@%$3`Jb;Fv8A>bI{M&=V5sE0`))nUblEU&;8!H+PiE|_9xP{ zT@XUTFyplH+S#Z7D4PAJ$ja%u)@r7ndTh%~Y4OD64TAC;2Ku_8!x;=VY*ZAUxChr5 z9e&F;w`&->MNC%bwgxiXUN=yw7M0;QIV#2h@22M|HgPTxqPRT@A9HMlvv*7|s$7rh z=Ehr4m;B$*3!RdAh1FoY{@A*NQe9L<(QU_K@S$dF#?;fJdvS{Vu#)2UBHv_YrLKd+ zAguo5FYY8{Jmc^>Jp}~E043>0{vU}!=KMW`Z4qcH730%v93JUvql{)rd)4yj+36F{ot(s>Z>b!|cviaQd zaphP(LhC(nmW~#bcep~*;EsNQ`H$23Am*2w2CuKkeeYvsoI;Ho?$3UX7%rorT@y%9 ziyqwmFr5DU%Moz7_{#b*t8gyr`0Ce~9K@&ZsqZFhwE8(?$H+u|S1}S8GY&bH3fWEp zE(ZMP(SmR>4E$d)YzW*CKDmn$j4rHKX4C=9O2A5@c>NdKuWvi+L;d8W2G7y3R=WjO<6 zX3qUU=I6H(`=dkX#T;)RIc`suzdSnK-qBw{b}ONMHb-b@r*n`X=}E-Yy{-bpcMQrNI&7Tz^D7T30zHJ>T?fCOf5k+pN? z0ah=wxs$|QXm&-#Kk-yt`q{6D^2&7sS0tx_fRii{e}S206BCnkp4bnCf)Asw{X&6x zR`fHJT5`rP`U6_Plzd^DrA1~d52Ej&zilPqccBZzq8wdL(=?c^HOC<%8y1}yKF>)> zxI@BHQF^DCIeLUtvEYRSg`S6VGmb%}CmM~MrGg0s82|J`Qa5ZR@_8EC3>4No;#Bf+5c5 zhA${SanF2mWOa^BdL6FQk<(d(ty?RGjD!?qUZ&bu)a__cK=7T@85l0pO&!E<((yB5 zIUzISUGlJIt5$x3TH834dJtZ94rmK0t!wNHr}^OV7@ND&NkFfm>5(3-OpMs}Xtp#y zz3?g`MNSSz6LhfCyeR&Yllz}>-{+t-ABZ_2d0kx*s!nN1yS^u;vd7<%h-_8W)NN;!!5A5J7pP>xm(QN3dq zE&U0UQ*5A(4=2C@7U65j@Tz!58Bf-+(>j59ukQa zbv*NfiSIvOq#gGLy}Y{eGTHvjU!wOdf0p3%@)OtZC090uRHnBVO&D*G$~ki~au5iY z2c0fiO@ALl@9ABdpcGkbl29~OcZXR9q_h$J2I;F^d(>gd!e5aU6^cD=K9TF;k9}cU zs}Ze_lt_b?`mw)56Hsg5#191&K+mIihc`=jS8heUeMZ2g>c1HgGVf9XJz3{w~(z4f?{g0_29E7pfrL-Z64u_wTP>#cFMkw)ty9wYv{0DHKvgn z>7pWYP3+zr2> z7MFDut9j}p(ggXznV-hKNDhrmRq*|c6L==#`zgSQkh-p(54`ljkpAdWdTl&seq2wE zZ8Ej;Wj<8Q%#uf3Z9F5z3YJPtU6#87JBS+1CZ3FDT{>R0#pQ5~e~%$=0}z&LiIb?H z>8c>U>Y!(>><*%5B`U7mxy|Tp(*@>HmFSaV=-1M2?B^(&A}|C)_6}YdWM(W1Hkk}j z_zBzb*i4nYr{o=z-i-QePO@9q=1S<8KUDllPFcCP@to^tlO(!-nDqhei1O3&=cAQE zPVT%Xhl+p6^CZ7oh*L&JMldp0pXpv0zTe4{u-N_x=ZRGy$DK%YAuvM`oW*WqA18nd ztMAP=L(T=P5Q9K4*=CIqFSgiljH6^ikR%DMj>w5TUw4i$YL`#4uIn~H`Ls+UITKZ9bMrX=;y;m2To zx4&d07a8~7vkjL?t(4YS;SDhgJ8>)br&)TK+1_H^|Do%x!>Wq9y-{gt=>`Eoa?@Q3 z0wUeD=`QJR5RnFnO(QMcu<7pZ?vn1VZ}FaU?sx8ep8G$sVy-dA_|>!`>A^VYggi@~ zl(E@MKqu9R>Lk}pJJPup6+EY6+H&V>1HFl8?dY|dUT-f~nFyFVyqsOh*49T~aEnRu zAU*f6(m`xGH$V2c=$#!H5I9YALJ?SjA#~a$tD8R>wsDk2z0?#yED%e=mqq&jCI(g%rA87V}(NZ^umKeF9@rJtShTn7b`W! z1LiKoc!_6pk?&8nq+7kD!5k7n|X- zk#2Styq~vu2t~Xp1%1CKQ(Yf=|!x340FE@HxxVOYk+4I<+ zYvsMX5%$R_pwb;uo8GS(E4N3jK3RgpV^CEsLar)mh=lzdRPMOw@mT4|Sl1uQU-R&X zgx}{Dxm8=0!mfWM`kAjB$+1)7q6j5XBv4XTG^0$?d|5z3WQ!jW8wmNUbTNeha03jr z9er|~oJpV3+WE-y{WqPW%Sj)e`pmZ9L^Epf(*>~K!nnS|=x9Pjjeh#Q=>{Ma#GpX_ zSb)~^V_TaT8!heY`x?kjebwKfg2YPnzC@?7R>G!g!U)UxJ3hN|`nj;`3c1a@e{2BF z-PhN(=Q!Jb3>NgcX9wvG7vL%7tC?_Gfsuq7TEZl*iaLj7TPKSu?2+f1Z}cATAFxsd z9TBAZ3zpPoF;_mHjPn0+>dz9~EVo!JyA|Z1Mc9-l9kqPR_up;I@rToMm~eCWe||1u zxyS7E%QSNI7n=5|vjulg+$r1NW;|&<9Xwl5gr~CN1fdWNPs_hu!Y`gfW3jbmN)k{l z(tARRl32BsC>U14E9LOIa#@0Uz&PebN^~6X);x3B+=22&G$YyD@$4S^RuhHX<9g~3 zxUB=~rDq)f6_w`Z=dTAdTUB`UlSW)1_p+G_89q9}!{hfE$w4EfI3W6i4JN#|ySDZB z@nsr#gFKJFLgQB_kU>P%f6kY1k+0SyLH>^1`AKTYF@Pj0aa zjFf2omCWdN-kU%GQjezdtxAO#!lL?PuJ477Eutt#y^vdB@z5%5F@Rpuwb^Z(TOu5F z_r0R_!CWa3{s?g-Lp%2NxpPxnq0w@Gzs9hf8R^&3 zA3L2o6Z`hHr;@mHr{v33vhxMJCQJu|YDwfy$-;VYiIw|Lnn1ha26ugbShxERD3X}Z znM|Zz;XqeP*!+O5Dm|czie7N|58D z#@x#ysVY~6Vq^NiM_^(Hl2>03JX6)BakqL!qndE!24atH+am6w_#NB4{ zsMK0~d<03V)s`g&Di+v5^CU0xv68F?UqooC(b9NPGp@ZB#wKaXQeC2DiH?er<2Jw9t)#3S{^41YJ zx)C9VuIh*<60#Z{2IN*uv1QJKKg|_xm|6Rr_Yb*DdOmoj;l*gyd0{8Fra=N(Jw+ZL z2@o+dIysm$8I=nO7g}$)Us8tc*UDlaZMo%}`BmL_D6IC|a_6q+1bMzMOSV#SxYy?H zd(6M+)sy@CMx`nTh$fgcSf26L*vZ3yh~6-1efVWo!0ydoW7|IKdbvfDp3CpO@g}_> zIVmaV%z&}ncpBsRo}#t5ZIQBu^O!9G{r;+4^Bc1!qS<6m&HBl1Sk4x1Dz7(!q&!$D zl7jsY68;cjX_Xdq1IkPC7d3s~K2OM$MK_WuQMJ@+MW`cuu)Ok8M$a&Qx1}vTsJkX?L+DOBdUlc z$eM}7VJMmfD#ZOU?DN-2HTTE>j<$LOXI?d?2mdTe&F6sJiJpvm6kaKz<$8J3il9+B-hW zCmENO^FYZw7-on@I$?_*#hJ;F0^rM|lj8#lyGdeh0X>txB#Jp4B=3zht$zj3+1<@~ zjwCQ^%P7S*?^ZPOCb0~dQm>tNY-8T^tZWR|_Y}ebbe?cyxJr%oTArnMSGX3Sy3l=hQi1wAn$L@+5Q~ic#7L|BE)_A^j z@SKT0nmIPx>LB@NBu_!>J20{3FO!jobWReRm6Kw8E%x$S_amjDH13}%rjnt(a%uTz zeYKXg84XNQlyegZvKbqI7}=k{a%{7!J}E<_=%==ZLOMwudYar4 zka!CNdV(11{wUXtPVJv>XmSPzn#t{W73J;|JS4Ro7$?hSu%fj-CSK%~$>L0ZJ4H>f z6@`#D$4RTya9NGl-TrfQK`#DsH1qZAuimbhr}*9%=aN7&WW5F#B!*U1u}!r}c#vu) z4I}(?jihannHB>+Ak`Uj#Y8%k2bRwtfEQGi%V#yUuuFqz6Pg$V5pys})n&&IdsG zM~$a|1k$Xf9xUhF`BgU-0fca78+88c8yfl|A%Xa!eI|MPlC10b{K#iInr^c-;DD+8 zIyw^lOS8NRmo^Bp*xCE@OkvPIWI%i(;C0510rzuMjXX%LR#K0lfh(y8L-SBT-L<_?ltCx7H0Tfc0;^{F0t+vpqS=?O2bMI7Py;x} zj~&SEGy>Qyj+3NQ#kz`{J_eHKBLCQauR0c>5~lO{rcuzF{eh6dn0Yib>@;7?I_ng# z=CFC!thn~K9h&LMZroT`8f^&hjD8Ms$PHiEHS`+{pVDXw7yL1mV|F^Aj*&|u^P{VK zna=matCVczSopAtM}L^k3K4@H0nGk)+I8)1gt#f;8VSdxwC&hHKtSEcmZ+u9YIRRqU`V#!aRd}@kD1_rvafGTEjr1X5BrK-&*qf&hf zRVRH?WWv5&~A5!OL%7H4~;gezck#c8)>oU)U%o&CtS2?meVf-JQ6Y)l<@=r zM&+bW>xrgmH*sPb^Q1iGoUz!-4SLptQB|l-jsb&jwV^{e0L;?1Rm zh4ff`qn3iPqI(YyX)ct1hoGTlM0P*YUkc_szloyLmT|1mldbI%WbnDThVz>8Bpr>M zLTX^kh)iqh;$R?hQywNXzR~n~+=d4?leb4nzLZ;F1PpEoOEG}ex3cqZt4F_V$JnUa z(bozYAZE85;F;3aT21~iOTtGRxa$b#qJ&N%P-(tUxV{Wu8Of4_8<(LP4g1jt4ulTL zPCvE#{VQifRfH#qJVB_hy>`V`@{oiOnd~eNkNb%9Xvj{5)>mPNq^v?yTw|gE{oKq~6)>8-|=MrU& z<>ly>LSSnkMxTaU5$v-&U8`9+*HRutB`jPxqMXzY)TfJ?N8zO!NaoeFa~Q-Ba_i!OLv5n`+in^$ zwu!>;KS}Jw+BNT^%)bRbU3?1emR7aMHk1vS)RxeL+=K5@lp3bJ(EP}gs4@>%iRVv8 z-L9AJE*h|Nm+l8_-pBka-VZ;MZku`^?$L%PcP+YLKJ+dJ%73DdE56TE*Hpg`9Z5S<$LtM37;X`eGNEHr>i=mBG=w=L*~ z?|8lj-6;5Ky;Bn=W2a;#Fs1Rjdg`#4iB#Ybaz4_}rR}pV?usk>w$@3N-OQ-ke-O^o z)u78S6-~w^-23q|==72^QkY~P@{W5S7IFxZMEtV2)pCR^_)(-n+@Ykm_WYR(bvkMhn#$M>b5;ft%=|2M}0MThx)6F%1Gkh{2axFMc@w$m>&*Tf{9a~;58ZZYZU-~V0K-&Z{_6O|kHcDq;k3xI;gWC_0DRN_;f-n;C5 zVg1*iJhqZhGdcb+iNVihgjeNxozn485FlM~Wbh<2pfkaTx^0)4raUyJ{l-)=?^(3jY z)m0Y#=B)K9!JTQh-e2H2^zchUo&EDkfzqfpcD@JD*rimv7$mmk-WpH=ZzpfWaC~X) zT(yh8Z4&eJRIJlFu2iT@B4_@##!@+^rACvRK_KP=3znsnF&ux zn+TkjZ${-bfxZ0{@zZ#z!jno;R%1v#%P*o(GHPBYio(^Z$k7NnN({9f7z+}q3xk}T z`1A7u3*|&p8aP#SVoRT`J=DC@@KzLYag>}3K0SLi*}a==(2V8p+b#1|OsQFY3h<}i z*$T~OOa8^yv)4|8qgazT__4Wl=LVCZ66E}u&KuT1&W`?4UOg`hq(_DTuC!&GOZy#W zDzAXgHBs&2Lh)CIp?h9g(w)H3Pa3hS_1+X$j_gH!FRCF+wqe8Q7!VaDYa1-q0xiqq z?JE(bY*u0Z-LBvHk_GX$pjA5*Pe{KtoJgCVi=CbQgvDS4TTm;%A0<&M7qX1mYt5=J z)$J~WfkXt*e4;RvP!-;;H?)C1z!5xL3zCRv^JWPZ5fMCye`?Lg1AIqnt>=}VrqmE2 zJ)bzcMW;-ph&|1+TGo_S1qiw3jan9|p~gN{RG+n74||g%L^kh-J&Ehs0~2F=uRGgX zmYvA&%p|H!8i^m82n8bBwqN3L$dS~AEJ3t{D)0jepZ=D#j=91@*7oiXZy&4A#2ii3 z5aIDZO|1vl^%cn&x5kDD;S&C@RQPnj8rOj z>wR2wY;SLuATn+H?f(XmSv7@O70)+~I8d_eJM~!py8+x{6c;pZTB(QT+TkK6amtt9 zTFw<*0ul)0Fo=p?!#H1@*Ob^Mf%5@xt9&ob<)}h4hv$l=5#=5(*$!{5OSEcut*7Cc zOeWBp8g4k)v5(uk#UrufSHV2_qYYgQBSznPa!=DGWQid6#yFK%`=}t^F~EP7P7-dda9$FoI$BVP9%l-4rknu>47leAtX712>yA|_yB5@+Vk zAFAEX%k~8$;iv!X<1M)=td}w0g7lJH@zlzXIrQNr}#K6AlE9@gcA6P>XAwQpRB(XuM>Kzq zwmkkEO-oPz`HIs&xbhJkx4e9I9nz?lFcNo<(OV1mbfd;3kWs4POqF9>(fRt#eL9pxho9XTg)c39G z`D605=6Zw;t}VT$M=G0Iq5E{kb@F`-5roIV;n)9XZKc^0-QlN+tZs{!W>RL!iN07R z^Obvs;e~0QV0Y(aEamVwm=D-0-vzNjpnG)!0#q=&UU>bcQq7;p4UqKo14)ppp5>&r z@=ONswPT7Z&0nx9de_`pi>7A1!)ao3aF~ir-HZ zpVqu(NOYtKZ2%GmLp5V1H}X$-&LIFCQg}TlltPW<6Dhz`c6MjI-N)-Mt(TJkTH3oaf7*UKK#XH$1VMSPq2cA1145h6$H4<7Ggo1)@PqYY5HOY z(2wYQzt5`C`N;_Mwava58RCm0f%U64T3lT-H5|NK9JxfWpn1U#YgJ{`jkixe*iXM# z)eE4g$^^V>2xIrA)yY}$wH2B~A-}?eqpG(E-4Ino$LsEibq*!5&vSz6GHR zEl{|>h@idPE7N=h#3IJFZ8AJA#;G;uctz)lsy;@9pSI z(??8))!K72TXB_Ua;uKOlQV;na{G=`lM3(toHEef(HJT@RFW~zYtXJ#2()emBrq*j zy6N-bj_R%aE*%4#D)?p%An$4M? zpSwLWWJ>0WX)OjxPrmL&BHEohHPURZ3xvXY0II<1e9eA)x2t@xT0hy#`rVv@^?lKP z3h;IELl2j2>3ZbSkT5ajYqVI+huz@G{RAuPl{>sYdN9xl! zBJ0&~!_>8p2~)YsttH@$pr*z#%4h05w{>;6LA-i-i)xAySQb^c5Z&TN6b#d8W_0qL z+9>fc@;Gb{2dQm%IR2%zu-Sbwn99jqe)~W)dwyQ&N7-EDnE_wFTJe9H(h8=|y^EPM z>hoeYXoH0;VQ}q!a$(edB_}!pgrIDx2T4f6;dHTC+R9pk4*}-xacYF6cxA*XRmd3# z-egn_qdxkt#&!1Ayr(I&_Z3DMgX$g?&egkPFvKy^^kiEnqemI~<==Pwz9+6_v`dPQ z59xbwJv%5ZdBl)LsP?M!CKv)k5fK6$R_D)8+jY_@U9Xfdj>;Cxk!eT40hU|`qEs<- zT!!zhdv|Or0FLi_ES_mn%|C6Yf9LifGIWF=4fjO=YMyN-5}wS;G0RW5)?|N0Whz6O z2S&O$${uZ1D*ZewSeVj|-@e`I`xI&0(%1hB55EU%#kRlN4I3|n<|5C*j@cSOpEgd_ z=g_9bGoG37#aj2fxaf&GXr7Pm99R>R3{c}|$xLX{V9rg-VDFLIR3-2@FV+n;cF% zRKit^e+KZhX@S!7fV~rf;@2PPUQ`5MYCDsgcf)vXLMHHir}5;viV^zKhdOmYdM%beT+>W$<-8Dr_K8JIn2ClQKbBBKamn(>ERBtAXi z?gx%|5Jh!XNo!949?VpKDd6>7N7Oq3K<#oTAG%;y(c|oWYVE;>?btJ^o7#F&TRK1= z_mDjLvfHlA@1M!K88{PzKJ@0KVYt&dHC8ydI~RG`|eJH7GO65F-PMw2x7puVA!0oc~SgB}(=8R5C1- ztk^`M#IK5~nbOW3e!NjizvFd+%V~n}n^gQ&N&EOZoK)~VQqHAv#_CSa~I_gVdL%h^W*hpU>^DmMhRPIJA1x8!PF6S#hpvb>8ahl z!KB)D{?PO!{l~5Q#(kVVJEbacQ0>H}?Uy}=LX8J=3GqscJ)ZQgeD}KVwbfDYpLIYT z;>MlbU^H;=$Gi@Ie?8kPk`OfKduo39WQyC=Bu{!4^*>qwhkW+~fv651ZFtBKPXeU? zk9GA3Z^h8Sx>3hF3W{KuX8yosf`#qu^tUOE0cKVV$(arR7GG3{P-Vz2&@rCxNHRiA z2IP{l(otzio)k!UquX-*1Nba~0VYd)CaG;ZXmEGD#4I1Y&^){2&6HXX4|9jsdf_qJ zqQ~k59@+jETI=~>`&TdD-~6mXRV)D(Qg&PlC`#`#0-QU)K*aF$fi~bDwm{tH=|dVs zCL#rW5rQgBIPglSaG1K3d|R9JCJGtpy__^6^Sn-aF+B=q2$6_(wfI?$jEYN!UTLcN zx877^oC;#N#KX6D(?#m>v^CzQ6_W_swbtw2^PYGl+KnzY6-%Ezs2E2$5F9kQkp$z3 z(F2Q8?yW|rWEq%7jKY1`uROfpv}EDDSYTbz&_=XU zfN4JF46gO*hZSVhU-ing1Q5hMJ`dJuw}iPaxJ+sFYw4Beo_0n*7vGNDP4=DvAjsNz z5XS)8_`((O+5UQQsN30Zld11&Vmk&c^9)eqmRBl%uGM#=ZAC{=_wXxsU<}s*=cmbp zlPuXrIY7{gJxeKkjMs{?Hka8tWhzWu7)JRtn#vnK*51s=X=1ZJ!2ECOh@-@oRzl>T z)~oI#xX{Hs7SMfsc671RgeqVa>s27vSWU5goxHqs8cGzwTYY)K(C^j zuHwB5FF971w_Z#CaD^U5K@4mm%G)Z)5CRjPqZS;PU%M;FFN7gVK=-l)PtP$xT0HKD z9@Ju>!FVczGD!SZV$aTAIwB$8P%KkpeFWTJF@fA#+QP$0mO;JclIIv^TxdRim9d@hFj z24NaFNlE2@&=!s=9hd>QdxdB+qSdQG5nk6b+L6@OktcnH8pXhU^9Q-N!fvfZC{ z@pE(r@~WT4ib=AfHDERi&HGywC{4SyRWUhMb~B6Ujdk((NYk_h=!&T~a1&xH##mEB0Ws)Ijj` zb7-q~+jqI*7sYU$%ENY@*uNRaVsl3hyQG1(Ts_6Of;&EcX=#MtJCq2dssP9wNvsJs zBfd8ArlQ()`C-$@cyQ@`d_7f8vw>!C7M}s7*nVVaRY3$F=y~&)j8K4!Gin!rK+*GZ z+jF5Sv0Rdhes}{=hwadRk{w)ofEn>opjS(O@R2c_C=qrhw2Y=bclZncCVljt1_FmB z{@Q6pShPs1hz9NY$K?gf;(+0eHg;B|sN`T90nAU!F(ws9^D>7pUqrk^boGF$Y(&6n zz4oywxiLRKcwWa?Jox7iP}|Ol@9vF$sSY>R9o$6{C(D29s)787LZB;JzyXG#ecG>I z2ZMiH^4x5PS}y$zM{YNn9++03`};mJjUPoUDl9)Q*Kof$XnGQmvX7GI(KGP4R%0 z(ey%dRjMfwvxxt>Gmq~raBLrcz1>=7_U?Gq3$CE&GYW;z7z&BHY(K(ySFM>eLsf?W zN$ugfctD{VMvuJqZnoC6gQ;r_9<8D3R&1>D*~K-&BX3iIER!8QM2gZ8{Rc3gd;s;R ze}phCI%}rWAXJ(-IN9h-PF;<=z3PR_0Rav(6O>s_5}+Ob@=`;s&s?NAZ5L2Dc6V{T z*tVcf>sim|*Ss=Prb7E;J$_&4(WB?3Aj^v5l?S}9?DA8B z;bIk6ype9VQQIkBQ{h9L{nI<{XDet!@`+Ymbv0Ue>buBSJQK#d(Xf9>!OinPkMLWp z4?yz`Rr5A^xuID|jjrJ-kkcGQaue%*`^h9++_daHXEtoje7uDT7?1ubyc&;eWB!-% z$exj#zo>Eq1tb<6AvSj?1@$`m{kgXrueXaat_Zl+U4yH8Ja|wFeA4zA<^Q!GO|Z9D z;LlYK?VIEO=LR*t>$YOGWOii#KwH?i$b`!r3~DQeHxSG_{# z4Fad|e_<`Am(Z8=@uQ=i_KwIp$`l@#SlNVHOIPfM{*P%hjbeM>$7G5Oau_+(TjwH1 zI~@=?yeHTLmbopcJXuAB8iS+<3upN{y4G#=T zt%0}VF73!WBDoX>fdpxv6(Lb<4yWcaiQGMV0C7)AmnO1MJPPM8T{R%xW1|3k&k4w( zHEjtdYLZnNj4kjWGOH6EK;YEb!vtUCS6agMm(Sf=1nqE9)@ld9sH~-I)aJba-9RF{ z6}o8f^%ha#4)lPXjUGWvlBI2i;@W8WT3ai?D~E$%T5R_@o-4u**AtX>aaiEbq5qVoGB43M-(( zYSDVtWsNd?^eBQ~rm-ZNX66a2+3*Ay6lh}u;M0L?CfQ|PGvh;G;*EvF-^D8Ijdaft zc1@!Q6!y8VWbb@!MgOX1Q}H*=;_C3%epweqxWv}6_Vkdo%#>r%?J;OOo!6g-7%mGG z?BCxsK7ShZQD*)7BH8pV8W?ZQi(&^&qNO<2H})|?x~AzGfoz%I5s1Jo767-{8(!1` zSlbH9lHPN97{_{^=xV4oDSNG zWXq5GBogu?&==4HRebe#J@cyphg1E6^XAex1GhW}b_U=b`L(R1Y|`98p=H}y@@o?g zT9Yz>fdl})CuXQbpQB;bl=SZ+HwTi`!>ui7Sj@Go07saT*Hs!3cn5PRN(7T!ft(2ks)^@ui?g=m-Fb9S-Y`_;&Pz za@~_wMKRc(WDXYig-u5vBsZDs;>~FVUY(X?h!J2~grQ8}w;cak>Tv^85lLCT{6zHc z^}sC|3V1wp+*xRZW~1GmV8hj6SFozh?Oy z{QE9l1*ledh~YsOj-Q>$2wHtEi0?6uBOq)n>uVrGW>;9O1t0m_EJh;hZQz`^&MgPMg+^;mxfDj0YM}E^i z`~RYBiyQp?AAwD5EDN}R1@eN?$Tt=hpDdZY0`_Cx(*o~+9SOY84=UxQ)h%;@E7pJ# z(}Y4k*dhPnGyx~y!cW*Ky)Oek2MhIICBPr3z?-&?YRlcO&P)0VlcL&|O}J*EWgrE( z&q#C}!g0r5e`cAOPdsWX|I-9VzD8j^fz<1NuoQSLuOlco;S^j%fc01o9YhV1+&JD2 z1_%BRuu{ta{6~51O6ypZIRWUS%UPX9g+5K~OD%fKv(FrmF@y2s{>$n~uF-X@$X8y) zqX_#0c8KhLS(NgwTzcm`&hELB%~Uldam%s-1&-FkFPl{ryL#Ij;cyZ`@(cB0v8SG^ z3X>x>(k0(_$YnS{N)f!!gZGH<@y3SX`NGjMaOIv+@9$2EQTn#o`%E++&Fg?nfMhgy zknCiXL@ey}t1H_x>d)6_z?Cia70AB9fEvqA+MhAbvSh!E2|p&@USA1}7fYdT6PgJz z0QI6;G8!!g)*aUl*N)HW@Ez+K9sxbr@?*EF#@0)%iQ64T=r{ zJ!D+QReaX%K`vl!p_A8@~5vi@CwDxDIP&R-hU1l-clh;e$l>x(jjj$^YLOR3bBfLD$P6?8GH zQ~JH5+K}`iifZ#W(cVgxz)yKXK#6}x8QX97IEFk#EE2Y;*I2S|? z0$UXmH4;M7?ILQ|*Y{rd(}V$#pCk9A?_^vXn7_)VOE=%Z>U&S+e_w?LKW z|3WCM2M?frT#u~?un^le6E!vb9p?1z&nD}l;-|}nC3HH4XgxASA*~qC{swT8hFxn4 z(1xad+RSk23v#^Hs@4{Vvj0Rsgk7)2Ql_Z25w9u9e8ye-O_r2i=Rh8c%UH}g`f2v~ z#Fg6mlJk;chN80v5OWqGw~`X?K@0Q;SK4x8*y)4m+f3?1x8Jq6V2SLrSm#HkcKq_8 z9>0Hbef6NGt6B}4T6#rXEhn{^xOgy;rz4B0X~|Jm02vV6=$C|y{1Ma$Ulcwi$A|XT z`Fz%zPeElJ{;M>c+BegMYP-E%%1X-RK6kEe0y&zXBV;g8#*Pd=h~>1s;JvQz z@An#)E5ZlL7ib*n@=ocW~0QKl#uTgIMT{As*B7zL04{1s0xtA zmv{L9wjZscV*PJVuCB1BfA;|aHB_|x9~(#4pMoZ*+B6 zTO*Tf`y9V%ysxre0cKc8(Miw5x<29xwQ4MjZy?A84oxz^%y&g5iV+PfL8maciU1=F zv=q@J_CEnynK{AJ*DKsQi6n}MdV${~6FU5rKg$Cf^IOA~3TVVUoFaam<_fMc;`Ig^w z$`>4yso0F0>|Dx`|J;b!*{jy~)=iU@Ig^16hpF&W$Rp_V4Diq^j@#I<1E`n82TEoN z?1?Aw8jG+VzzW^5xzW<%b9#O$YQ4l*)7v#q{Q6q|>%PkVSE2-Ko_RQPF<3(tOx8Am z_`0c{P1a_VTOT{*e8`_;f;QRR!-7)lqa}}lY%OM~BbATi^M+9=E0hLik;lSl(z( zN3ZV&<4utW>3j(0$FUS?Vfi2okeCQ^QHB!S#Q;tIehem_Rhn|s6wr%@QVxILT1urFewdmenKJmvnTd0_uzoXaZ zLugu}p;Ps%WMp+4>VJ(IMBjJdn{?{~&Y;j;1l1=N*s&C&e`sM7G zPqDTh72urD7gh|U9H_Nvt+CVY$zD5AzF^aef3oj2X_Py<9oL^3h+~9vsoOp}s8a-@ z@?>;a5oEQwrrkcw0pMc}!os?~@@CN+2$MAXnKKo+klT;fPK{a!PMt+J2bFFAgkV0Qsd2@qp z)9MS$1ohwukROPozhUpYNtPLt<9CJojtpFxo7#Lwy7~o&4trz&SMAd-!t|=2ot>z)Bj)01MmQphO%pb(q+3X z7*__yAW5+83hDgl)}xbxVKW9~zdZ6nYb5~92~zviEkoP(ScTYWyCjAFa^=;n*{aA= zzw1JCpzf1r)`WJvgTJ{fn~L8c3!+y{(O6WLx$-ii^XshT6Q&u~+(>4x7`%?7fYQy_ zV(X-c9mXev69Y@dsE=>g@3rK;MIH(E$#R0I5}GCHOuZV>8&Fuy?*?0E-{HLM^nD)W zrx`CXK{()-sM6K}u$7Hqp!2*<<_{AGRGiorTKT!JrH#*bTF~imW}I#h04!)ydOjNl zNPAaN;;}6~FLF;=Xh`m*z=A6o;eR{?0>C0$U3JZ2IJ}@B7mVg!g_k8uYZjg29h{rn zZe2AaCk4Ge7s3!CXg;*|#Tm)WR%n+KS}s412J8xedW$65H8_#`)(4JxfrO_q^}ty zjc33ED$3`!lJis{Z7cE=q2_j=FrK#nQ>p#S`E+)Fip~TlpAnFN3OoD-MMDZ^yePD& zG|)tsyHE`m0oDsEg-OyQVddTTR8P^#!q@*nG4n%` z4&CM7;y3W7@$fp3m9uwBnTMdI;s1XHLgmKegg~#QK-;Zo52UM{?u@gi6`bLb4- zAK5#b+V#gt{Spp^ygd0GhP#2{zHGqqVCq3V5&vvy>j3$xxfkvF2ujsl-eLI~9g$r@ zc#-k{z&HnghAC32sO`re(v^-5CR)cLG+c})WQ_B=KZ78Y0vrev#VR>8B4QpBj<<*j z;_GV(9^He9a!GqPXUI;GK%$n06ru&USx9o_r$iouM)e1>$3MLah4vf zIoWXNV;s|OWiDA|YJr@nyyo@dhhP(}(x*Eokmj6s0@Glnsr&s;xmnKyj%HxeS~3S_ zGIe>gDgfx1r;?;Z4^=v=a`66`(p%XY|Q^%CJ#;BVgrFerYrsP?KG3` z_qzGgy^~@{(d(JC>2>$^g?M=kV*&eO&I|^e3;7Cy0@NBc{iv{{*~1G(s*K73g}#8(G&q7qrGZ}18`Xld z^UqpQZ+?3v+BA$sEy(s|gHEH6ZAs|Dq8c#cOCH8@e2w({GlOvve5Y8hf>WZ7S?*IY zz8gd6h*bJL9e~rKW0D=6{j(}E0O1+d%^;YWks3znwtc#ebg4OFm(assx|_<_;q-8@ z*!mL#68Do20%nE<5=76XIHa<>nxby8>4af^yi!?a+^l_x=-XhBl@o2+a8A`&G5y)^ zuOIB<-yDB$d$cjbf+UWho|ZEWsyRz5v5`AAXD?JN|L>@VLpPe*bpVfD{1w>j@p)}R zms-+iu@Y$8fW*PM2Y^zW3$?731RBMZ;%dSVz2ML1gJEAiy$L3YaaGK@F^|(*`~*o9 zhG}AH;U+#T@zo@D%3TX&S#HvsIPfuW61H*h*3|r}Vsy_-;}_sF-FjQPoU+D+bZs2T6yo>zfG!<;87&d^jJNsUGHlTH%hBg z6a~gm=dq0|ULXSEnrcc9)~8=8Tq)M<aE!+S~Ol4IAv^-=%NeO{W&AOLt7SB?+1OvTeMJ`}JzV^{Ykq#Ell)sJ?ohDx`4{Eom9N;4?-Z1- zwI`=mAGi9foFz87bu}r~!Kwy2V)Lp?{Mz4Ka{Xp;Af|uLBc(ZTB?tOA?UUrJ=2AZb zeX{PPW9EoP5T9eejlIC%kmgM3UK-DTO{9x=f6#{7FH!$T3xIz40jyZi-$#>j!mc(& zBOrJ9EFuxWB4GB2BH8@Cyj2_D=5g)^^4R2q?*|>S{LKUo-gml+8dZ<@{r!yJCQ_qQ z=bl6+7N?2pmc`(_l@!5EJ)czG2a3dx z)EfPc<#`@|It?9kDA0Pm&Oz>dUO}C3+ESlzUUz!LgX}9S(W^?Yd@qKVb^13jykpdg zQ3eg4P50OBXDN*?q&fMTY&R93!rbd?RSccNE038jNmfx?r8l|AHNNkNqc|GT1TIEIMA~&K<=c}3i&KHYa6cYSMqpTXqR^}zsh}2 zAJVxyK5cxz30z9;f#r~~RQcBz&H{65?6|cU#}evzve96~2a`W9N*$83ohPSENzu-B zf3&M*L&*K*n^Vch;dS!1%^e)tG-8NfV!Vb6wss(LOJ&a>qEL6TwmHb?|{Kf?5 zz$ZLo^^MA_J~*m-%-u(&q>AVCPehaRYbd-iP_OH?sH`<@NbBOw?PuvNwwCK3PS)V3 zCt1C!e5I(_iPdSUUf-8lcFr-AOXL1L5h;4Ce`nt(y;@PDp_dPD%~nLPAxR`ku&|{B zz(ZYl7X#&tU{^qxkB180-uC=GIFFKp)*1fk@f*2U|HQL1zC{!44EtFcto3nkg@_*QS>$j-BsBK&jlrRD5lFk7M=}@FgNohp78M;A5q?@5Z zQd+u(kcJV4?(Rmq;d{p9^Stl%`~&azmvddpIeV?W_FDJ4*V_Bc=^kV1amZFy=hZMT z;N%{(KhO@OJ0u-N z6+N`lZqT~2_z4wAi-(B=1IZRuLNjjZWTh#~K$lMSmM>Tzb{7(_0zy5}0IXWy2ASev z?3tPb1))2L+Ls$#e!0+JM{+)BwhHY2&Xh>`%!y-)j1r>Gl0Z0Jg15dn`d;WMVfJ># zc~6vQtor+#9=LumuiIWaHNff=QkrSk1JaBnCDAHXfYGx>`g;YGd0l8ckSemH6k@Uf zU0hs_jiCXka^)n^X-0>VlU0<(PKWBTYU=ZILoOvIETrz5gm5xU+8E$-KS7uUM~33Y z_L@jpt&nB|7bM9!HUX{zaD_0PRk{8n!^1s3#%7iNEz-q18sc+oBElf?225lskSqx` z79}yXw`z?n@S7bpLANi}TKgeX+xo%5C-8X@<9|^Cm16psSnVO@f4$ zv~7;?Y@a#@D*LA|T0+ z=Xtw~x5{@EciLYfmw(}M*J4&UYMgG@^~`#8v<oMYgCM&CWg{8bCHCk@vOX5vmjc z6&G3h%ea}!Tz;_95vc7$Kk4njN6ez+B;N0J;Qrd>=dG&!^zY1C(LyV!sQMnoaOFRq zlvb<`)@o7Io1;obKVL80ik|YeOjC`k96-s$_O*OkTyNv6B(I@Qb+QY_NHG4GZNKKv ztwjKVM6g(?+1I=+&vanuYQ&%Bn4Qyn>L8L*U+<@rmzBfE(ZihOc$sULEJXLYCzPpX zw?8~F0>KqxZTC;I-G;)AckfHR$Z zp#pVr-IemA0=;MJ+wYh27T3P;CpMjWt5lWhQj(WYGTObCX1^*%c{)|6FU}C6F68<{ z9DJy^s{OJ@WYR#ARVj(3vF5f>)1=2NxK!;|7eZ4VnW=Zv(F`0$>G~j9QKFTf7 zCp$99ZS{iZ4b;e+U9Rb?E7)RY{++mzo61*_4lqEZ>~avMq^8T;1Y>ICRgBO|8CB4Z z{eRcV(mTS!ckv2(Y}IR;=r)`&>7FHs0;PGW&Z@i=4L?vXnH-$zNs%yeQgyh80%hp2 z{_(1+$EDr)avzneb7PTAs)iAmZ02684;85PiCig`w;cc@nT&W>)vc$NRRbDKiS8{5 zuhc9pXCs$fk+2|jWO}?SDPA-nPVXxHLteEfMkSje0emC&Ibsd$$ZFx#n2flR&kByX z3cg;n5F*jv%Iv|xu|IBwVF5% z+g!qxGpPeo#_HMQ%=h;&jQfhS>ZJ{%x*(aDLHEi(r0xTI~d~ zk!oN`NuY^i@j-^Xz6r4qjcaR z`j!YD=GYs4(sPs~-c5yxp_GM#1Q1JT@$-U8hLULpS_=T$ve;i}&MP9R0G}+^U5#Ex z9joc??8dHtbfFkhW?L8@KYUrVKEByM&#}S_ zrZB@H8V#)iAR@}Rc`_s|1LsnM3SyHf=F_XNw|~+@a&UDz?dE}eN36(n)VI0-3N;GF z%2m{nK;lmb#3YDoS-oZK^7;+v+T34jott96YMn$=b^#t)G*5x5Z)`78Uk7c?8J zhuwa$D;;zNpa$*BRy$m8edOAkwcyOXK>poMNQP&MYEBk&WYl1MMrA{S_&|?1`3mx> zwD(02U;57Z&MS1-y9tUagGrL&Jg`=eAJe+$*(z;@^A#5r2Z!!JSX3e-`;s>OVw4#b zmzBT4fdVUXHs%Kowx+CjXdheOEi6cO$VXZ@6~I^Cr1hf7$ays{Ewa%Q4J(iCh?sja4RNr!VQy_ypfQNMP}kzO;EA z-A+SLDJ=&F-6URodf{1uosN0rRKe3&1wna3=IU2e^<$-u>cJM6ey~VEl_>#$6+5;C zM^lCt1MVJ~O~bo0em^NR0Pi%Yzo2GQik^W!VFjx$#0$C~Fr1!WH5ppDSWcLoWY%?- zC83FBjz)#cJpk3HNi!Nc{mCdejE*)@NG+L=)J%=2); z7F@LXWnn4g>L}f#{;N}FFR6uUstA{zsldTIyM#%Ws*`!uCDZy{3|J5tBkQ}=>tGrN z4S+x$E=`8V>renC78~~n<>zlm1cVsy*jdd_cO1PfLbi$e>LTc4C zOw!4>Pf$^~mN-IPL+>TZ(I;EzTbS@V?u; zFkU>riN0+C9JzNT*ucEvqwX!5@vo0;?2AiZCU#v-&L~`$X|(0==DgY!ih`y#=UUr~ z&h<`DGu}L%>P8W_{H7FJb2zW?I-LKp>qdrWc-`#X)VW7d5nQ6CptG+y5L)>!gSDrk zt98~4Z(NIQF*95i&ob;<*FG%oJ3N)TAexS>+Zv*}V2Fr=2~vnEE>rW17Y>6zmUHSBw({lJxQGT=qQpdDf?I6Knur41TjfUETW2hi^OxHaM)8+(&s8^q zFHd34Ky9%=IqJbV9qMRj~wH6CyO2FoFGRFlHt-bFTCD`&6P^L3zi-JX#SSMEj#94-AsS z6VCOw@$N}p6tGAv$0pjqdSASLb%K?xeqjUD{Lsc%W|nUAx!}(`Co5+{Z;6rbMF+X@l;j>)o`a+gY!tK7Z4!-EtTTFmI39c)-%via+X z4^8~ztp|?YEiFVzy>gyQH%L!3HP5RITuz9xpwd%Ri^4zT!S;lfCN!=pyo_@iG)xyT z?H?XY_)0AM99J$;sJKWD*_wpWB7$`6a^ktHb(^Qo+Kac*TSt=rov8^%f{RS~?s@OADV2eE4fHFRwD7zRo|ehuUF5g6|{U6>d=QRt&L&9f5lZ>B!zJ!EW=-?dEQ~pw1|8L?sh;3tof)7t3O+8w78BpY1TWkq(;!Dh z9X)i^R8?Q|mj=Gc`^QaCkST!4EZ+p!+44=9BV?8y&muXO?#<{q`T*)IPkm+GnKv@G zK0%-Z4Eu?YYGf7`q2UEW`fd~N%Q~cNp*|+s!^G&V?y#fROL+P)d#l1o)Bx!XE4>|= zuwv)KVd)|4%=%ZvHVberQ>ZWu?P5n!k+q?4= zrJQ9jY2bq*^e;sXs?4iqchi?&1`7zDN>mCGlDe)iz!(eiLOF?uak(9{_4om!^<|8NJRi+d^p)Uwm+vvky5~GNozZpQ_zCd8b6yJJ zU|6N}^;y62I9mCfo*$>I#b8xsuhwTv*Y-R)4(jtVsDR0(}JUY1dRX zANPkjs^1fD4%4xM%*AS?>vx=Gn0JtkN>c%n#Zs z*ul4yonHkx?}weHTl9;ES~*Wo{F-BRYWb{P#e7Gn1gqVE5RI(s7JJ=y##oksOY42p zm#Vh}f}a0|6~J>Er~ojcc~)J9547S*2*!N8M3IfA9uRDle6jWyy@6DNx|UR<14QN!jm^aXjYXaTUNAqqd{GbOIehJPZw_M?p za@yH#d?I@f(aODOm^q!Zee1@H*T#I`-F?3O@So$;A{<{t?w8{~_(a)LJ@46Z^8SiC z%4{pFuhRnmv6t6v)b7ONf`rj4)U?IwG4Qt<2Wf4S{M)Y2BmNEaKtji>+2GV6sWKeM zgdhFHD0Jh5*9%-T>{Xp8?`zpqS)ajQJDbFTP#^1tH0WN5@>~vdrFyPCd3zzi_V%Lo zP)UyYn*UZJ;49!(lYb^aU7ts>J+v-t(AB9KxNu6`cTD0S%w_~Fn;&0Ij_ay_yn#HA zG9sCf@^?}Xgvk;8;pH&I1&C+Fv=_S_`9 z>M~0~vxSk2pTmyh(XZTrSmYOoMb@h>q5~G0_|e!0uLERemPzL2BM-lYjDvvzFRv41 zpGAs(T)%=8Lp+;3XZnZ#9_9lau$EoLq%5-8K;2yPgV!=D$E?lT@_72u1Zf(}X&tOe zqmIT$5#j>ul%P69zViAianF{2-hqo)!Di41+ebTe8p zWc_UCIooz;q4b-M=VS-L1>`f7NV4j38ceB-Af9WED%|h@z~4P##0ALeXzAoB9nu0A zu;_qNGQ8DoxmgCeXE%^?QBP}#s0+XFCgd^cv`b0nexoP*XA4`v76M%Q5oWAFo8B4t zvO*mm+Fk$BVZZ&-v$|Zn2S^nd#*M`$aQ8mf*{=h2i%&Xo>*5^MtL?uUGWBeUNa1M91|r34%G#&&66^%4i_);U=m{mOr_ zvY#lWi)fL+j3-~Q`bj$m-pm(nh(dNxq`+GfJ!K*2S~nf1Z+S$dIjThH<28)Bzc=Wz zfHZH5>nfvhfroI@@Cco)15lsGwcH_t*x%sIuE79`{eqnoPP9senLdJ5?jM{cV;5=w zFHp*4uGH!@wFDa|n&2R7W3PO67O)n@P zTKjm>u=3p#qFO$hV!2496)&UbD^!ZEJIGGo9B@2JbXmoggwq$iX4xy}7v@1n`LW}4 z4!He7)F+%se;gT32aI8Olt*S}eqH?0>*_f9i;@4S>FSw*u^p9B8)@ zF|yVWCPxEAEWL6~J=U>Fk>aKR#rs;IAfCOV;y6#17>>nT9EjMKOsY(<&-h-m<=cS` ziHr!B7m06A@ITJNoi|q`)~4Q@I<1tD^OmsuETMwyc>k~I03T+31%Q!Xj3twQa|V&P z&(p^<%t%^Ko(_x)&d%q@ZSWr)A18hVTQSZGsSw;O#r+!6X(p7rvkYPHrU-qLP4hOnVJG&;xawBD1kyXr(}Vrrp4@J zJpZ^88j2iw@fcg5jf&PS5XuVc-zd9*?EMfC>Yr(T$2<80I`5Ie=&<0DHMWl`2&0ap9RTG5f|NRUqJH*66IrT$PGC#;Sb1( zFaD)#@|OT~xC>7Fv)N413(5n9zpnKiJ$`ok`ji5H+KKazodB?xF)TAcd8Z7iyNAvTX#OXt zgi{bv?^Y@KJL=Jn)pd&j@%Vsv``0}@*TqvJ#EbkBUxB=uS01Pfh_86_tnFXmJoul> zgfxwjUIW;vF-Lm(<1G@AYj$G&X{P~%ozi>I@BKLo@YB!S=tDaIo1x``+zANnFs|85 z81Uo$6YIZ=`y)d*6HuBCP};z$hyH(4+7a0hP&x%r+Hv-o@BgYadQ2IhbQ7So4;sle zE=GP$>0gRc>n;I`-T@SSL}7R9e^WH7;YBWUVeO%MuWh;2H0kD%qP&2?`6N4>KcWY* zIvCiz*GQ$X?$6cv@^5v-$VW|K5{=;Tatkrf`N{EQkJHr;=rSr=r!+DvXn&U#a801{ zT-}@f8&oqy-cGs9Z8S`jiLwq>@{tYtgBe!OeS&}0(m!+6|KZ+s5AcCyd)3Gm)oiek zQ1nv_h0+Sj|0V|0yWJG#hZP+mso5&6TfEkXH~XCSx|#o{Yr-*sf|zfsQe>;{%+e3q z1WuI$UQiu=6mvA~S zxodGC-0%qSFRdqchB)7KuCH z($6ZtY5%0k{zf7LLTmtPWrfBanVsumXD|SOD!{+BPWWwM2-x-(17sy&%}i}Y@*iBo zF)yz~z$}{s4k;j-ihC)F)Ci4>0I>ny^rY9tYCZ-+{!x>z7G=zYx{-7TAj?Y;d15>< zJWKnZ?NA>smQPq)u5lqRFGj$ftM%~n%upgU*3Ib& z09n>jUamEYy8{9LFzE)e13({27011+VNa?VfA|`hI;4h;3=W3Arw|06{$c-+V22 zC}WvllA(|A0(!s;RVn(3j<+@mRw#AMGIRXgi2lVe9GM1cgvJKXg)ys!|RVXup-W?O;lfy3R z*P5BYAn7zfIlxIfoRE*o#TSvJCN??+huue)6$frj@Hq*8s;G(U9!@EMLCHHVFbg1ER%K zfZ$LYh;6k(uCnX%@nQ#%J<*0MW5`cMORgMCMMF*{a1Q~7MgaEm&dXiz+L7xP0s!h- zE1Tc-T^W*B&S7s%cLxEEz(IwK3@8pz=l@*8pBE5GVaI0SYYIa$Cay zZhG-r_+WHqxFS9wAfw7 zNdO~EKDn1!PBHp(W>T_m5bH~cu;ZI7g@4lm90YMlwQI&f8DfmE?It188e-yk2;2V7 z!+$jPaz*5PBPJOON$5`>i;8JP7zoizO8tkh{V8R~;zoWA8~t-wafHb`<$p24-(%}W0K>+oB<9fkw3l4ajh|#_32}sD zIuh$YMsP+>P+o)we$}v3$I;_~ssdMs*~)6)|zU^1e0Mj9`Pi*V&CDr1h7x(tBRQY zQWBXiVbp%W?~mBLbl9+ZI%d6DO;}~Hu7~dPB5C9mZM}yxm5uX7rn>WbChM2W3#*Uy zZPUI%)(zfp)T7&5Hz#h-P2Tq3ElCwx=CqMactCp&omoabLPGrIpKPzeBX5lgP_zYP zSi+&aoy8ZA)^twc%JS?qPRTYbf|q5!IfI7Ialym#r-up!Op zHjQvh5AAD{|E1Auce2X8#C82=_Eljer>$>m7GJ!r*}3wx-kdX@Q&4?_`)EOqfhTrj zArK294g0{pvE$emOacCizpFQ4e|? z>$}zeq5ckN#&w0PLJ5)Qt#AjJU#9ikF8lU0MM)>7Xnlsf#yUltqm>4e8E#j5r5XV> z2wr~FzFc>rQ2NmGXf`*Eq`lGr=MSN<0U!tq|2Ovh{Lh(Nz|k}UHJ@z1F3+DC&S zgHP_XhSe5{N^=Ti=);eqJuj#1!=s`Y-EUHa)LSe0@7gZraB@;H_miOhO!W_hb+G^( zp!IIazCN&SfEUe^`n_iDdC83&0_xp*p~_cc+TbW{Yy~+@kF(+1L5^x`HiHJ7;3nmO z{@k$K-=RxR31l{c9h)cDG7#EpsuW4sbgeoqqnS~AD$k-qBuk%Lfui=*v^f;hwphH= zPg+*pbXOsy(;w?(rEC558y+=_?qB6>0}3!84Y|Vg)&P+1Q~bVPd6-vllsgb^L(dz= zqvcKak|MDolkODLXR_Qj&hzoO5v8SW#@WAc$@>_Xq~D!?5QXyK$GZkhs{t5`HaDTg zTAQV|K(?%FI1R|&XR#b+mY$l|?37G>`}Z`s5qNv@Bn9=ljY8uZA_|8(BKX!9%DGq| zW5ewZ)0RH2!GadG*~g@MP;4I_CVJ-RrN2P0EATfj|E7Q@J5D zXmPzZsr%L!x0!sH!CuY+CN*|JCBJQ*IH{X?p^|cBdrbvPMP^2%|8JTQ0lX~2W5Ndd zALMC+cYsOAgBR)>EVy_vOg2K-pM^_ft~SwZAn}5?M)c!zit01@&js@ff1dl@a_B+3 zZsnMCuO))|%aX~yi6MGh!)W}PKw4LQH>J7VgSYv8*5!&%3vvV$i$w6ui(II?bpB2qOuy|*b4K0p?t~BNTc5>5u0nQF?Bze2{7&ILgx58ZkWWc} zNkz-IQ`XzO80_Aa%ICNK*$)PXdSZrr$};L!{h5SsP|?)>&B-K;#FzIvOvOItavy)ldU5ni~DE zgh}48{e3vW0Z7KB8VT1ob%h(Gyy;OXs(9wj+D+G2?yXwRh0X6Jj~E*8(GmSl^=v@z zDE2Wqou>lnjE&>?Z_w2JLhthcR;@v7!e6r$ZUBsQi~q)o;I%Li z#NzH90nx^fFX}7Sm}}@z0-*Tmu=}-*^Qn0jvDHc>07R{6bHu+VI)flbTO>q?ebaw) zkHB`?9prTe8ztKG@|6#C-c=n6rUD(1K;DZ`Bnm_@Mwa|0SPcZg!KiZ=1xKaAG7O%kyP=?(n()-(At~h`q8pdg^PHn-@?aEwdc9oB75W zco_J7k5l0`b==8g5!hA#LN26gD9aMI{xp!O#V#{@;7{mb1C%X6;?wqjEI5o}XG=Pn zPJ7sXTEfEr$p0Lb&)?NT!sKVCNcaK5ayA32?AP?Z#HIU=P2Wywv%38%)A6S<{+>Ml zXBwd1oFNL?jNV{+a#_Q8NCNSdPriR}_tXCUPfTAx_i->1fAPGA0KgM#xlKzEU9>+T zg@T2CJ~^Be?6E#AR0|w-Ze8LRxS*)Ln4!jAStSZ36R>}2mzDOYjC~;oW-76d#TJb5 zAR>5Ikq0>#J;B%<0)xl|3|)@Jd@r=>tYdGVMBd18O-lW z&6`bDyQ`aghAXcmMIg_=1xo}ERKP+!q-A({Km$QugUE%X=HVn~gz4F3wv9PChLP<{ z%C`FRyE*9a#Xk@ZnD^n5+6(VT$j!EPJqBzvem-!?b+k7Te^>IO-Ht6zCi$ ziY2LVusS-!PnR4bW}|E#YojMVy9l(JVH)G zZEacTQpMl-Vx#7nDRM|Q9J3J`G6D9%Su!BFdKmHIA#fgPg$SQRMb)&i_(Bg}8JwrB zNpBXd1X~v;G}!K4vYBRKVdlV!sm5Rz;%cjTP)hvAA-=x!a*pzS(-b}Q|wAb3QPA!Y08kT^yceFs+RWOI{ zW@nIL*mQ6_l24SLXXk(Cj^76FYZmv5_r;cKWjIUAk`m7Waz*|5r9?56*}(Aa;N&no zox+F!h>JnfE)TdV9#{IAW%SPRu_9knNcaL~ltuednnt`x)-92FrW8*XdYfa?%CuCDp8 z2R(|x%n{di5*Xd(`Frr$Rv%@g*k} zub0weIE}s`)N4w`6vCN6Hq3Ql@V?-yCcmRt>xH08&Y*Gu%_nhBMLN0mr4*Vk4D3*>SFEyrKgKx@vTABZF@yjX5KV-ti2WN$JJ)GJI z?kG0>hTw`h`t7u7?AS2xs|owB-<(vJTnlxur#Ps&G`6lTrCW{HF}}CEa^{nGNI9kn zpT`VrmOKX^o@dO)$n4_=N`%lHUFe1OH^7CBK-*hTVFlORiBQVMwTzE-hH6l{oR&J|(#^vfuJ07CT(ZN-&B} z2Y}S8SFPuhNl)$#Eb&+8?39Ugffo%pKW;U=l&CwdCF$!Wm`XfYGE)uC(N?kw8= z%CTqW?zTP5Li%(prKZkf>5%y_P_T>}#41Q53OoW4(*ut)sHHP{SOkh|g;iaR5IE1Z zh%~3qKYV`koS{dE6=%xYNay{|)_hT0j%ZZt8KaS0$>n6kX#lUR?%G*wD=F};qaSh$ zwp1)!VGG(1|wwb?ves6>VTzIel?SRH)%3j&pFg3HSN0<==1(3HPj+RG0ho* z_bvhGZ>1ddbemVtPIyIgAwqH5o**}-!koaYC>=mG#fOx(Yf`6Zow{z1x)k6lCuw!% z1vQy;bB495cfdNZw6I)Lv$YDU^FESvFr#}KVUplQu*1?l$*IKUD}3I9UuDPyP3{b* z9tYqhH>Z~rJ=?r|RQX`^v0HN84|iQ9aa-FGgH5bS>nwFCl95dfu84kFw?kr+NTkUy z&{9tpmK|y)4XgMhxBtdkF!-MvkB!Nq-eru*hW@f)XMHd)Qf{nZQb zivID#I-!wr%^{ZfbHh8)Vh?)$6)xc{`(E<*IqI%4eDCJw3$s!u zkkjo>5^Pz`R1o^ZX;|gFoN=GM@ws7R;IoD}jb}@$mHnmOp!X5Q_Eepw%kAfV;5rS` z;~f}qqBnPFs53{jB+yaqBb7e?$K34)cKyS6yfus8lA%~6E%i|MfE7wtmzB3ZVOPmC zjkGg=&H^~a@HE{;-qG8YajkIM6eo=E0S}Hjw_-?Fw5E9og6s}E-wQd%#@LAn_uhh| z4REgBn+@7+En=S^<4<$*5BsWFTiIE5G3r{{A)#Ad9y0BBUJR(A98;#eRPWheM$YkP zaWtt}d->Np%l7mSD70=Y6r6aNyMox@U}YJTw>18YOWTKcV_C2vK!vlLEab+S<1|dB zMeSTw{f=6?J7GO?^bvKd6s#T7j*@X;;`pGqKficg_dLK!v<|nckRIh#%pJ$m`UiO)^-)&sax>IK@ zqXMK{CtG^GzQ8Z3cPcRN-1+E;<|*ljR?q6;`y;Aa5Ht>9u4T|ktaOpMomkY4reBy$yk>Il>p#^*|*)Ox!PTp=SzdEW}4_u($%e(WQ zwqx=8bnO$*BfB{TM|4l1l6v^jfZAubNN5Dd-LXZ&T#OVm$@e5G)U z<+%(m&Icv4_=c8(I^#wKPd-vt<3>4E)ipk*1A%f60g&d}lPT})TvkqRB3B&T1u^WH zjFuA*>J**dHI43j;q$6`m5qxNcj$@ohtjkXjYASXcK##0B{iBh!ac_$__RP-Ze>Bv zz+lv5?M*Sg0#8;d60M8fL9&lrn4@3yg3Vt)oVpl!twu2FL|a_k)uxkdY6|i7yl~m4 zJa;6ksR&a(1)6hkvTLDA9@(1AI5b2o}?13$RKbhNU#b%dH*V&_Y8{pr%Z6Tv4Y zuNGeA^&=Z~>x4rNTc;npNxiDeBawT(w7mLO@EiB(>1}3Or1$ zf5eX;|KT&1Usb7u4*3>;Zmv!pR^pi|to0FRxX?Lo8F|;$x(4sUnLO)}JZ*JMeF#+1 z-rS?HYtIpG-ksJ_0r%WHF*f&D6Npbw=RU98j4kh;ZQE`P@g%CZeiE$fUV8-mmWc

BI54O~zRoBjaxEt(6hB^gH6Z0Z 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 literal 0 HcmV?d00001 diff --git a/docs/gitbook/.gitbook/assets/avalanche_api.png b/docs/gitbook/.gitbook/assets/avalanche_api.png new file mode 100644 index 0000000000000000000000000000000000000000..d27967d12d93027dca31ed1bd7d7725169a164c5 GIT binary patch literal 51341 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 X*YG!{!mZy!FMGkjRKNV(mHYn-MwHYA literal 0 HcmV?d00001 diff --git a/docs/gitbook/SUMMARY.md b/docs/gitbook/SUMMARY.md index 271b5e182..639a0ef57 100644 --- a/docs/gitbook/SUMMARY.md +++ b/docs/gitbook/SUMMARY.md @@ -9,14 +9,6 @@ * [How to Install](getting-started/how-to-install.md) * [Learn Avalanche in 5 Minutes](getting-started/learn-avalanche-in-5-minutes.md) -## 📝 Examples - -* [Models](examples/models.md) -* [Benchmarks](examples/benchmarks.md) -* [Training](examples/training.md) -* [Evaluation](examples/evaluation.md) -* [Loggers](examples/loggers.md) - ## 📙 From Zero to Hero Tutorial * [Introduction](from-zero-to-hero-tutorial/01\_introduction.md) @@ -37,6 +29,14 @@ * [Advanced Transformations](how-tos/avalanchedataset/advanced-transformations.md) * [Dataloaders, Buffers, and Replay](how-tos/dataloading\_buffers\_replay.md) +## 📝 Examples + +* [Models](examples/models.md) +* [Benchmarks](examples/benchmarks.md) +* [Training](examples/training.md) +* [Evaluation](examples/evaluation.md) +* [Loggers](examples/loggers.md) + ## 💻 Code Documentation * [Avalanche API](https://avalanche-api.continualai.org) diff --git a/docs/gitbook/contacts-and-links/join-us.md b/docs/gitbook/contacts-and-links/join-us.md index 4db43db48..ae612d26d 100644 --- a/docs/gitbook/contacts-and-links/join-us.md +++ b/docs/gitbook/contacts-and-links/join-us.md @@ -4,16 +4,15 @@ description: Happiness is only Real when Shared # Join Us! -> _Do you want to make Avalanche more suitable for your own research project? -> Or maybe you just want to learn more about it and sharpen your coding skills in this area?_ +> _Do you want to make Avalanche more suitable for your own research project?_\ +> _Or maybe you just want to learn more about it and sharpen your coding skills in this area?_ -No matter the reasons, we are always looking for new members __that can help _help us improve Avalanche and make it a better tool for everyone!_ +No matter the reasons, we are always looking for new members __ that can help _help us improve Avalanche and make it a better tool for everyone!_ -Building something great together 👪 is _fun_ and _fulfilling_ 🎈. Joining our team you will also join a family of _mentors_ and _friends_ that can let you collaborate, fave fun and ultimately achieve more in this area. +Building something great together 👪 is _fun_ and _fulfilling_ 🎈. Joining our team you will also join a family of _mentors_ and _friends_ that can let you collaborate, fave fun and ultimately achieve more in this area. No matter your research or coding expertise level you may have, we believe anyone has her own strengths that can help us build a wonderful tool, being _passion_ and _time_ the fundamental ingredients_._ So, don't hesistate to contact [our team](the-team.md) to learn more about how you can help. Do it now! 😊 -![](../.gitbook/assets/join-us-you-5cae9e.jpg) - +![](../../../.gitbook/assets/join-us-you-5cae9e.jpg) diff --git a/docs/gitbook/contacts-and-links/the-team.md b/docs/gitbook/contacts-and-links/the-team.md index 14e11ddc9..113a4b0a0 100644 --- a/docs/gitbook/contacts-and-links/the-team.md +++ b/docs/gitbook/contacts-and-links/the-team.md @@ -4,17 +4,17 @@ description: All the People that Made Avalanche Great # The People -![Avalanche: Coming soon to your computer screens! 😂](../.gitbook/assets/avalanche_maintaners.png) +![Avalanche: Coming soon to your computer screens! 😂](../../../.gitbook/assets/avalanche\_maintaners.png) ## 🗂️ Maintainers The Project is maintained mostly by [ContinualAI Lab](https://www.continualai.org/lab/) members, with the core mission of supporting the production, organization and dissemination of original research on CL with **technical research**, **open source projects** and **tools** that can make the life of a CL researcher easier. -* [**Antonio Carta**](http://pages.di.unipi.it/carta/) \(Lead Mantainer\) -* [**Lorenzo Pellegrini** ](https://www.unibo.it/sitoweb/l.pellegrini)\(Mantainer\) -* [**Andrea Cossu**](https://andreacossu.github.io/) **\(**Mantainer\) -* [**Gabriele Graffieti**](https://www.unibo.it/sitoweb/gabriele.graffieti/en) \(Mantainer\) -* [**Vincenzo Lomonaco**](https://www.vincenzolomonaco.com/) \(Project Manager\) +* [**Antonio Carta**](http://pages.di.unipi.it/carta/) (Lead Mantainer) +* [**Lorenzo Pellegrini** ](https://www.unibo.it/sitoweb/l.pellegrini)(Mantainer) +* [**Andrea Cossu**](https://andreacossu.github.io) **(**Mantainer) +* [**Gabriele Graffieti**](https://www.unibo.it/sitoweb/gabriele.graffieti/en) (Mantainer) +* [**Vincenzo Lomonaco**](https://www.vincenzolomonaco.com) (Project Manager) ## 🔨 Contributors @@ -26,21 +26,20 @@ _Tyler Hayes, Matthias De Lange, Marc Masana, Jary Pomponi, Gido van de Ven, Mar _Avalanche_ is a great tool also thanks to its many users. Here we list some research groups using _Avalanche_ for their continual learning research: -* [**ContinualAI Lab**](https://www.continualai.org/lab/) \(PI: Vincenzo Lomonaco\) -* [**Pervasive AI Lab**](http://pai.di.unipi.it/) \(PI: Davide Bacciu\) -* [**BioLab** ](http://biolab.csr.unibo.it/home.asp)\(PI: Davide Maltoni, University of Bologna\) -* [**Computational Intelligence & Machine Learning Group**](http://ciml.di.unipi.it/index.html) \(PI: Alessio Micheli, University of Pisa\) -* [**Italian Association for Machine Learning**](https://iaml.it/) \(President: Simone Scardapane, Sapienza University\) -* [**AIforPeople**](https://www.aiforpeople.org/) \(President: Marta Ziosi, University of Oxford\) -* [**Learning and Machine Perception Team**](http://www.cvc.uab.es/lamp/) \(PI: Joost van de Weijer\) -* [**Tinne Tuytelaars’ group**](https://homes.esat.kuleuven.be/~tuytelaa/) \(PI: Tinne Tuytelaars\) -* [**Machine and Neuromorphic Perception Laboratory**](http://klab.cis.rit.edu/) \(PI: Christopher Kanan\) -* [**LASTI Lab**](https://kalisteo.cea.fr/index.php/textual-and-visual-semantic/) \(PI: Adrian Popescu\) -* [**Visual Artificial Intelligence Laboratory**](https://cms.brookes.ac.uk/staff/FabioCuzzolin) \(PI: Fabio Cuzzolin\) -* [**Eugenio Culurciello’s group**](https://scholar.google.com/citations?user=SeGmqkIAAAAJ&hl=en) \(PI: Eugenio Culurciello\) +* [**ContinualAI Lab**](https://www.continualai.org/lab/) (PI: Vincenzo Lomonaco) +* [**Pervasive AI Lab**](http://pai.di.unipi.it) (PI: Davide Bacciu) +* [**BioLab** ](http://biolab.csr.unibo.it/home.asp)(PI: Davide Maltoni, University of Bologna) +* [**Computational Intelligence & Machine Learning Group**](http://ciml.di.unipi.it/index.html) (PI: Alessio Micheli, University of Pisa) +* [**Italian Association for Machine Learning**](https://iaml.it) (President: Simone Scardapane, Sapienza University) +* [**AIforPeople**](https://www.aiforpeople.org) (President: Marta Ziosi, University of Oxford) +* [**Learning and Machine Perception Team**](http://www.cvc.uab.es/lamp/) (PI: Joost van de Weijer) +* [**Tinne Tuytelaars’ group**](https://homes.esat.kuleuven.be/\~tuytelaa/) (PI: Tinne Tuytelaars) +* [**Machine and Neuromorphic Perception Laboratory**](http://klab.cis.rit.edu) (PI: Christopher Kanan) +* [**LASTI Lab**](https://kalisteo.cea.fr/index.php/textual-and-visual-semantic/) (PI: Adrian Popescu) +* [**Visual Artificial Intelligence Laboratory**](https://cms.brookes.ac.uk/staff/FabioCuzzolin) (PI: Fabio Cuzzolin) +* [**Eugenio Culurciello’s group**](https://scholar.google.com/citations?user=SeGmqkIAAAAJ\&hl=en) (PI: Eugenio Culurciello) * _..._[_and many more!_ ](https://www.continualai.org/research) ## 📫 Contacts -If you want to contact us don't hesitate to send an email to `vincenzo.lomonaco@continualai.org`, `contact@continualai.org`, or you can join us [on slack](https://join.slack.com/t/continualai/shared_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU) and chat with us all! 😃 - +If you want to contact us don't hesitate to send an email to `vincenzo.lomonaco@continualai.org`, `contact@continualai.org`, or you can join us [on slack](https://join.slack.com/t/continualai/shared\_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU) and chat with us all! 😃 diff --git a/docs/gitbook/examples/benchmarks.md b/docs/gitbook/examples/benchmarks.md index f56856798..3079f95d8 100644 --- a/docs/gitbook/examples/benchmarks.md +++ b/docs/gitbook/examples/benchmarks.md @@ -4,7 +4,7 @@ description: Benchmarks and DatasetCode Examples # Benchmarks -{% code title="\"All MNIST\" Example" %} +{% code title=""All MNIST" Example" %} ```python # Device config device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") @@ -58,4 +58,3 @@ You can run _this chapter_ and play with it on Google Colaboratory: {% hint style="danger" %} Notebook currently unavailable. {% endhint %} - diff --git a/docs/gitbook/examples/evaluation.md b/docs/gitbook/examples/evaluation.md index 5ae93fcdb..6c4028d3b 100644 --- a/docs/gitbook/examples/evaluation.md +++ b/docs/gitbook/examples/evaluation.md @@ -4,7 +4,7 @@ description: Protocols and Metrics Code Examples # Evaluation -{% code title="\"Evaluation Pluging\" Example" %} +{% code title=""Evaluation Pluging" Example" %} ```python # --- CONFIG device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") @@ -89,4 +89,3 @@ You can run _this chapter_ and play with it on Google Colaboratory: {% hint style="danger" %} Notebook currently unavailable. {% endhint %} - diff --git a/docs/gitbook/examples/loggers.md b/docs/gitbook/examples/loggers.md index 1f8bcbdf0..abb22f901 100644 --- a/docs/gitbook/examples/loggers.md +++ b/docs/gitbook/examples/loggers.md @@ -4,7 +4,7 @@ description: Examples for the Loggers module offered in Avalanche # Loggers -{% code title="\"Loggers\" Example" %} +{% code title=""Loggers" Example" %} ```python # --- CONFIG device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") @@ -89,4 +89,3 @@ You can run _this chapter_ and play with it on Google Colaboratory: {% hint style="danger" %} Notebook currently unavailable. {% endhint %} - diff --git a/docs/gitbook/examples/models.md b/docs/gitbook/examples/models.md index 11df6251b..eb8e8edac 100644 --- a/docs/gitbook/examples/models.md +++ b/docs/gitbook/examples/models.md @@ -4,7 +4,7 @@ description: Examples for the Models module offered in Avalanche # Models -{% code title="\"Available Models\" Example" %} +{% code title=""Available Models" Example" %} ```python from avalanche.models import SimpleCNN from avalanche.models import SimpleMLP @@ -20,4 +20,3 @@ You can run _this chapter_ and play with it on Google Colaboratory: {% hint style="danger" %} Notebook currently unavailable. {% endhint %} - diff --git a/docs/gitbook/examples/training.md b/docs/gitbook/examples/training.md index 0412010ee..4442ee509 100644 --- a/docs/gitbook/examples/training.md +++ b/docs/gitbook/examples/training.md @@ -4,7 +4,7 @@ description: Baselines and Strategies Code Examples # Training -{% code title="\"LWF\" Example" %} +{% code title=""LWF" Example" %} ```python model = SimpleMLP(hidden_size=args.hs) optimizer = torch.optim.SGD(model.parameters(), lr=args.lr) @@ -58,4 +58,3 @@ You can run _this chapter_ and play with it on Google Colaboratory: {% hint style="danger" %} Notebook currently unavailable. {% endhint %} - diff --git a/docs/gitbook/from-zero-to-hero-tutorial/01_introduction.md b/docs/gitbook/from-zero-to-hero-tutorial/01_introduction.md index a9aa19a8c..c01f60d90 100644 --- a/docs/gitbook/from-zero-to-hero-tutorial/01_introduction.md +++ b/docs/gitbook/from-zero-to-hero-tutorial/01_introduction.md @@ -1,21 +1,47 @@ --- -description: Understand the Avalanche Package Structure +description: Understand the Avalanche Structure --- # Introduction -Welcome to the "_Introduction_" tutorial of the "_From Zero to Hero_" series. We will start our journey by taking a quick look at the _Avalanche_ main modules to understand its **general architecture**. +![](../.gitbook/assets/avalanche\_api.png) -As hinted in the getting started introduction _Avalanche_ is organized in **five main modules**: +Welcome to the "_Introduction_" tutorial of the "_From Zero to Hero_" series. We will start our journey by taking a quick look at _Avalanche_ main modules and its **general architecture**. -* **`Benchmarks`**: This module maintains a uniform API for data handling: mostly generating a stream of data from one or more datasets. It contains all the major CL benchmarks \(similar to what has been done for [torchvision](https://pytorch.org/docs/stable/torchvision/index.html)\). -* **`Training`**: This module provides all the necessary utilities concerning model training. This includes simple and efficient ways of implement new _continual learning_ strategies as well as a set pre-implemented CL baselines and state-of-the-art algorithms you will be able to use for comparison! -* **`Evaluation`**: This module provides all the utilities and metrics that can help evaluate a CL algorithm with respect to all the factors we believe to be important for a continually learning system. It also includes advanced logging and plotting features, including native [Tensorboard](https://www.tensorflow.org/tensorboard) support. -* **`Models`**: In this module you'll find several model architectures and pre-trained models that can be used for your continual learning experiment \(similar to what has been done in [torchvision.models](https://pytorch.org/docs/stable/torchvision/index.html)\). Furthermore, we provide everything you need to implement architectural strategies, task-aware models, and dynamic model expansion. -* **`Logging`**: It includes advanced logging and plotting features, including native _stdout_, _file_ and [Tensorboard](https://www.tensorflow.org/tensorboard) support \(How cool it is to have a complete, interactive dashboard, tracking your experiment metrics in real-time with a single line of code?\) +_Avalanche_ is organized in **five main modules**: Benchmarks, Training, Evaluation, Models, Logging. + +#### Benchmarks + +This module provides a uniform API for data handling: mostly generating a stream of data from one or more datasets. It contains all the major CL benchmarks (similar to what has been done for [torchvision](https://pytorch.org/docs/stable/torchvision/index.html)). Its main components are: + +* **Benchmarks**: these are complete benchmarks, providing access to train/test streams. +* **Streams**: a sequential list of learning experiences. +* **Experience**: a bunch of data available at a specific point in time. +* **AvalancheDataset**: a dataset that provides support for train/eval transformations, concatenation and subsampling, and other operations needed to manipulate data in continual learning strategies. + +#### Training + +This module provides all the necessary utilities concerning model training. This includes simple and efficient ways of implement new _continual learning_ strategies as well as a set pre-implemented CL baselines and state-of-the-art algorithms you will be able to use for comparison! + +* **BaseStrategy** provides the default training and eval loops. +* **Plugins** extend the basic loops with additional functionality. + +#### **`Evaluation`** + +This module provides all the utilities and metrics that can help evaluate a CL algorithm with respect to all the factors we believe to be important for a continually learning system. It also includes advanced logging and plotting features, including native [Tensorboard](https://www.tensorflow.org/tensorboard) support. + +#### Models + +In this module you'll find model architectures, pre-trained models, and utilities to implement continual learning models. We provide everything you need to implement architectural strategies, task-aware models, and dynamic model expansion. + +#### Logging + +Metrics are automatically logger using native _stdout_, _files_ and [Tensorboard](https://www.tensorflow.org/tensorboard) support (How cool it is to have a complete, interactive dashboard, tracking your experiment metrics in real-time with a single line of code?) + +## File Structure {% code title="Avalanche Main Modules and Sub-Modules" %} -```text +``` Avalanche ├── Benchmarks │ ├── Classic @@ -33,7 +59,6 @@ Avalanche | └── Utils ├── Models └── Loggers - ``` {% endcode %} @@ -45,4 +70,4 @@ In the following tutorials we will assume you have already installed _Avalanche ## 🤝 Run it on Google Colab -You can run _this chapter_ and play with it on Google Colaboratory: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/from-zero-to-hero-tutorial/01_introduction.ipynb) +You can run _this chapter_ and play with it on Google Colaboratory: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ContinualAI/avalanche/blob/master/notebooks/from-zero-to-hero-tutorial/01\_introduction.ipynb) diff --git a/docs/gitbook/getting-started/alpha-version.md b/docs/gitbook/getting-started/alpha-version.md index dc2e416d1..87ebfe53e 100644 --- a/docs/gitbook/getting-started/alpha-version.md +++ b/docs/gitbook/getting-started/alpha-version.md @@ -1,133 +1,65 @@ --- -description: 'Supported Benchmarks, Strategies & Metrics' +description: 'Avalnche Features: Benchmarks, Strategies & Metrics' --- # Current Release -_Avalanche_ is a framework in constant development. Thanks to the support of the [ContinualAI](https://www.continualai.org/) community and its active members we plan to **extend its features** and **improve its usability** based on the demands of our research community! - -A the moment, _Avalanche_ is in **Alpha \(v0.0.1\)**, but we already support a number of _Benchmarks_, _Strategies_ and _Metrics_, that makes it, we believe, **the best tool out there for your continual learning research!** 💪 +_Avalanche_ is a framework in constant development. Thanks to the support of the [ContinualAI](https://www.continualai.org) community and its active members we plan to **extend its features** and **improve its usability** based on the demands of our research community!\ +\ +A the moment, _Avalanche_ is in **Beta (v0.1.0).** We support a large number of _Benchmarks_, _Strategies_ and _Metrics_, that makes it, we believe, **the best tool out there for your continual learning research!** 💪 + +You can find the full list of available features on the [API documentation](https://avalanche-api.continualai.org). {% hint style="info" %} -Check out below what we support in details, and please let us know if you think [we are missing out something important](../questions-and-issues/request-a-feature.md)! We deeply value [your feedback](../questions-and-issues/give-feedback.md)! +Do you think we are missing some important features? Please [let us know](../questions-and-issues/request-a-feature.md)! We deeply value [your feedback](../questions-and-issues/give-feedback.md)! {% endhint %} -## 🖼️ Supported Datasets - -In the Table below, we list all the Pytorch datasets used in _Continual Learning_ \(along with some references\) and indicating if we **support** them in _Avalanche_ or not. Some of them were already available in [_Torchvision_](https://pytorch.org/docs/stable/torchvision/index.html), while other have been integrated by us. - -| Name | Dataset Support | From Torch Vision | Automatic Download | References | -| :--- | :--- | :--- | :--- | :--- | -| **CORe50** | ✔️ | ✔️ | ✔️ | [\[1\]](http://proceedings.mlr.press/v78/lomonaco17a.html) | -| **MNIST** | ✔️ | ✔️ | ✔️ | n.a. | -| **CIFAR-10** | ✔️ | ✔️ | ✔️ | n.a. | -| **CIFAR-100** | ✔️ | ✔️ | ✔️ | n.a. | -| **FashionMNIST** | ✔️ | ✔️ | ✔️ | n.a. | -| **TinyImagenet** | ✔️ | ✔️ | ✔️ | n.a. | -| **MiniImagenet** | ✔️ | ❌ | ❌ | n.a. | -| **Imagenet** | ✔️ | ✔️ | ❌ | n.a. | -| **CUB200** | ✔️ | ❌ | ✔️ | n.a. | -| **CRIB** | ❌ | ❌ | ❌ | n.a. | -| **OpenLORIS** | ✔️ | ❌ | ✔️ | n.a. | -| **Stream-51** | ✔️ | ❌ | ✔️ | n.a. | -| **KMNIST** | ✔️ | ✔️ | ✔️ | n.a. | -| **EMNIST** | ✔️ | ✔️ | ✔️ | n.a. | -| **QMNIST** | ✔️ | ✔️ | ✔️ | n.a. | -| **FakeData** | ✔️ | ✔️ | ✔️ | n.a. | -| **CocoCaption** | ✔️ | ✔️ | ❌ | n.a. | -| **CocoDetection** | ✔️ | ❌ | ❌ | n.a. | -| **LSUN** | ✔️ | ❌ | ❌ | n.a. | -| **STL10** | ✔️ | ❌ | ✔️ | n.a. | -| **SVHN** | ✔️ | ❌ | ✔️ | n.a. | -| **PhotoTour** | ✔️ | ❌ | ✔️ | n.a. | -| **SBU** | ✔️ | ✔️ | ✔️ | n.a. | -| **Flickr8k** | ✔️ | ✔️ | ❌ | n.a. | -| **Flickr30k** | ✔️ | ✔️ | ❌ | n.a. | -| **VOCDetection** | ✔️ | ✔️ | ✔️ | n.a. | -| **VOCSegmentation** | ✔️ | ✔️ | ✔️ | n.a. | -| **Cityscapes** | ✔️ | ✔️ | ❌ | n.a. | -| **SBDataset** | ✔️ | ✔️ | ✔️ | n.a. | -| **USPS** | ✔️ | ✔️ | ✔️ | n.a. | -| **Kinetics400** | ✔️ | ✔️ | ❌ | n.a. | -| **HMDB51** | ✔️ | ✔️ | ❌ | n.a. | -| **UCF101** | ✔️ | ✔️ | ❌ | n.a. | -| **CelebA** | ✔️ | ✔️ | ✔️ | n.a. | -| **Caltech101** | ❌ | ❌ | ❌ | n.a. | -| **Caltech256** | ❌ | ❌ | ❌ | n.a. | - -## 📚 Supported Benchmarks - -In the Table below, we list all the major benchmarks used in _Continual Learning_ \(along with some references\) and indicating if we **support** them in _Avalanche_ or not. - -_""Benchmark Support"_ is checked if the actual _continual learning benchmark_ \(with the actual stream of data\) is also provided. - -| Name | Benchmark Support | References | -| :--- | :--- | :--- | -| **CORe50** | ✔️ | [\[1\]](http://proceedings.mlr.press/v78/lomonaco17a.html) | -| **RotatedMNIST** | ✔️ | n.a. | -| **PermutedMNIST** | ✔️ | n.a. | -| **SplitMNIST** | ✔️ | n.a. | -| **FashionMNIST** | ✔️ | n.a. | -| **CIFAR-10** | ✔️ | n.a. | -| **CIFAR-100** | ✔️ | n.a. | -| **CIFAR-110** | ✔️ | n.a. | -| **TinyImagenet** | ✔️ | n.a. | -| **CUB200** | ✔️ | n.a. | -| **SplitImagenet** | ✔️ | n.a. | -| **CRIB** | ❌ | n.a. | -| **OpenLORIS** | ✔️ | n.a. | -| **MiniImagenet** | ❌ | n.a. | -| **Stream-51** | ✔️ | n.a. | - -## 📈 Supported Strategies - -In the Table below, we list all the _Continual Learning_ algorithms \(also known as _strategies_\) we currently support in _Avalanche_. - -_"Strategy Support"_ is checked if the algorithm is already available in _Avalanche_, whereas _"Plugin Support"_ is checked if the **corresponding plugin** of the strategy \(that can be used in conjunction with other strategies\) is is also provided. - -| Name | Strategy Support | Plugin Support | References | -| :--- | :--- | :--- | :--- | -| **Naive \(a.k.a. "Finetuning"\)** | ✔️ | ❌ | n.a. | -| **Naive Multi-Head** | ✔️ | ✔️ | n.a. | -| **Joint Training \(a.k.a. "Multi-Task"\)** | ✔️ | ❌ | n.a. | -| **Cumulative** | ✔️ | ❌ | n.a. | -| **GDumb** | ✔️ | ✔️ | n.a. | -| **Experience Replay \(a.k.a. "Rehearsal"\)** | ✔️ | ✔️ | n.a. | -| **EWC** | ✔️ | ✔️ | n.a. | -| **LWF** | ✔️ | ✔️ | n.a. | -| **GEM** | ✔️ | ✔️ | n.a. | -| **AGEM** | ✔️ | ✔️ | n.a. | -| **CWR** | ✔️ | ✔️ | n.a. | -| **SI** | ✔️ | ✔️ | n.a. | -| **iCaRL** | ❌ | ❌ | n.a. | -| **AR1** | ✔️ | ❌ | n.a. | - -## 📊 Supported Metrics - -In the Table below, we list all the _Continual Learning_ **Metrics** we currently support in _Avalanche_. All the metrics by default can be **collected** during runtime, **logged on stdout** or on **log file**. - -With _"Tensorboard"_ is checked if the metrics can be also visualized in **Tensorboard** is already available in _Avalanche_, whereas _"Wandb"_ is checked if the metrics can be visualized in **Wandb**. - -| Name | Support | Tensorboard | Wandb | References | -| :--- | :--- | :--- | :--- | :--- | -| **Accuracy** | ✔️ | ✔️ | ❌ | n.a. | -| **Loss** | ✔️ | ✔️ | ❌ | n.a. | -| **ACC** | ❌ | ❌ | ❌ | [\(Lopez-Paz, 2017\)](https://arxiv.org/pdf/1706.08840.pdf) | -| **BWT** | ❌ | ❌ | ❌ | [\(Lopez-Paz, 2017\)](https://arxiv.org/pdf/1706.08840.pdf) | -| **FWT** | ❌ | ❌ | ❌ | [\(Lopez-Paz, 2017\)](https://arxiv.org/pdf/1706.08840.pdf) | -| **Catastrophic Forgetting** | ✔️ | ✔️ | ❌ | n.a. | -| **Remembering** | ❌ | ❌ | ❌ | n.a. | -| **A** | ❌ | ❌ | ❌ | [\(Díaz-Rodríguez, 2018\)](https://arxiv.org/pdf/1810.13166.pdf) | -| **MS** | ❌ | ❌ | ❌ | [\(Díaz-Rodríguez, 2018\)](https://arxiv.org/pdf/1810.13166.pdf) | -| **SSS** | ❌ | ❌ | ❌ | [\(Díaz-Rodríguez, 2018\)](https://arxiv.org/pdf/1810.13166.pdf) | -| **CE** | ❌ | ❌ | ❌ | [\(Díaz-Rodríguez, 2018\)](https://arxiv.org/pdf/1810.13166.pdf) | -| **Confusion Matrix** | ✔️ | ✔️ | ❌ | n.a. | -| **MAC** | ✔️ | ✔️ | ❌ | n.a. | -| **CPU Usage** | ✔️ | ✔️ | ❌ | n.a. | -| **Disk Usage** | ✔️ | ✔️ | ❌ | n.a. | -| **GPU Usage** | ✔️ | ✔️ | ❌ | n.a. | -| **RAM Usage** | ✔️ | ✔️ | ❌ | n.a. | -| **Running Time** | ✔️ | ✔️ | ❌ | n.a. | -| **CLScore** | ❌ | ❌ | ❌ | [\(Díaz-Rodríguez, 2018\)](https://arxiv.org/pdf/1810.13166.pdf) | -| **CLStability** | ❌ | ❌ | ❌ | [\(Díaz-Rodríguez, 2018\)](https://arxiv.org/pdf/1810.13166.pdf) | +## Benchmarks and Datasets + +You find a complete list of the features on the [benchmarks API documentation](https://avalanche-api.continualai.org/en/latest/benchmarks.html). + +### 🖼️ Datasets + +Avalanche supports all the most popular computer vision datasets used in _Continual Learning_. Some of them are available in [_Torchvision_](https://pytorch.org/docs/stable/torchvision/index.html), while other have been integrated by us. Most datasets are automatically downloaded by Avalanche. + +* **Toy datasets**: MNIST, Fashion MNIST, KMNIST, EMNIST, QMNIST. +* **CIFAR:** CIFAR10, CIFAR100. +* **ImageNet**: TinyImagenet, MiniImagenet, Imagenet. +* **Others**: EndlessCLDataset, CUB200, OpenLORIS, Stream-51, INATURALIST2018, Omniglot, ... + +### 📚 Benchmarks + +All the major continual learning benchmarks are available and ready to use. Benchmarks split the datasets and create the train and test streams: + +* **MNIST**: SplitMNIST, RotatedMNIST, PermutedMNIST, SplitFashionMNIST. +* **CIFAR10**: SplitCIFAR10, SplitCIFAR100, SplitCIFAR110. +* **CORe50**: all the CORe50 scenarios are supported. +* **Others**: SplitCUB200, CLStream51, OpenLORIS. + +## 📈 Continual Learning Strategies + +Avalanche provides _Continual Learning_ algorithms (_strategies_). We are continuously expanding the library with new algorithms. + +* **Baselines**: Naive, JointTraining, Cumulative. +* **Rehearsal**: Replay with reservoir sampling and balanced buffers, GSS greedy, CoPE. +* **Regularization**: EWC, LwF, GEM, AGEM, CWR\*, Synaptic Intelligence. +* **Architectural**: Progressive Neural Networks, multi-head, incremental classifier. +* **Others**: GDumb, iCaRL, AR1, Streaming LDA, LFL. + +## Models + +Avalanche uses and extends pytorch `nn.Module`s to define continual learning models: + +* support for `nn.Module`s and `torchvision` models. +* Dynamic output heads for class-incremental scenarios and multi heads for task-incremental scenarios. +* support for architectural strategies and dynamically expanding models such as progressive neural networks. + +## 📊 Metrics and Evaluation + +Avalanche provides continuous evaluation of CL strategies with a large set of **Metrics**. They are collected and logged automatically by the strategy during the training and evaluation loops. + +* accuracy, loss, confusion (averaged over streams or experiences). +* **CL-Metrics**: backward/forward transfer, forgetting. +* **Computational Resources**: CPU and RAM usage, MAC, execution times. +and [many more](https://avalanche-api.continualai.org/en/latest/evaluation.html#). diff --git a/docs/gitbook/getting-started/how-to-install.md b/docs/gitbook/getting-started/how-to-install.md index e976e5bad..0483c2495 100644 --- a/docs/gitbook/getting-started/how-to-install.md +++ b/docs/gitbook/getting-started/how-to-install.md @@ -9,14 +9,7 @@ _Avalanche_ has been designed for extreme **portability** and **usability**. Ind In order to install _Avalanche_ we have three main options: 1. [Installing it with Pip](how-to-install.md#installing-avalanche-with-pip) -2. [Installing it with Anaconda](how-to-install.md#install-avalanche-with-anaconda) -3. [Developer Mode Install](how-to-install.md#developer-mode-install) \(for contributing to _Avalanche_!\) - -## 🔂 Avalanche Dependencies - -The _Avalanche_ dependencies are the following: - -`python>=3.6,<=3.9.2`, `typing-extensions`, `psutil`, `torch`, `torchvision`, `tensorboard`, `scikit-learn`, `matplotlib`, `numpy`, `pytorchcv`, `quadprog`, `tqdm`, `gdown`, `pycocotools`. +2. [Developer Mode Install](how-to-install.md#developer-mode-install) (for contributing to _Avalanche_!) {% hint style="info" %} _Avalanche may work on lower Python versions as well but we don't officially support it, nor recommend it._ @@ -26,30 +19,17 @@ At the moment, we cannot provide a swift installation experience as some of the ## 📦 Installing Avalanche with Pip -Within an Anaconda environment or not you can install _Avalanche_ also with Pip with the following steps: - -1. Install the package with pip. -2. [Install Pytorch + TorchVision](https://pytorch.org/) \(follow the instructions on the website to use pip\) - -Step 1. can be done with the following line of code: +you can install Avalanche with pip: ```bash -pip install git+https://github.com/ContinualAI/avalanche.git +pip install avalanche-lib ``` -That's it. now we have _Avalanche_ up and running and we can start using it! +That's it. Now you can start using Avalanche! -## 🐍 Install Avalanche with Anaconda +1. -This is the **safest option** since it allows you to build an isolated environment for your Avalanche experiments. This means that you'll be able to _pin particular versions_ of your dependencies that may differ from the ones you want to maintain in the rest of your system. This will in turn increase reproducibility of any experiment you may produce. - -Assuming you have **Anaconda \(or Miniconda\) installed** on your system, you can follow these simple steps: - -1. Install the `avalanche-env` environment and activate it. -2. [Install Pytorch + TorchVision](https://pytorch.org/) \(follow the instructions on the website to use conda\) -3. Update the Conda Environment - -These steps can be accomplished with the following lines of code: +## Using Anaconda ```bash # choose your python version @@ -76,12 +56,12 @@ You can test your installation by running the `examples/test_install.py` script. ## 💻 Developer Mode Install -If you want to expand _Avalanche_ and help us improve it \(see the "[_From Zero to Hero_](../from-zero-to-hero-tutorial/03_benchmarks.md)" Tutorial\). In this case we suggest to create an environment in _**developer-mode**_ as follows \(just a couple of more dependencies will be installed\). +If you want to expand _Avalanche_ and help us improve it (see the "[_From Zero to Hero_](../from-zero-to-hero-tutorial/03\_benchmarks.md)" Tutorial). In this case we suggest to create an environment in _**developer-mode**_ as follows (just a couple of more dependencies will be installed). -Assuming you have **Anaconda \(or Miniconda\) installed** on your system, you can follow these simple steps: +Assuming you have **Anaconda (or Miniconda) installed** on your system, you can follow these simple steps: 1. Install the `avalanche-dev-env` environment and activate it. -2. [Install Pytorch + TorchVision](https://pytorch.org/) \(follow the instructions on the website to use conda\). +2. [Install Pytorch + TorchVision](https://pytorch.org) (follow the instructions on the website to use conda). 3. Update the Conda Environment. These three steps can be accomplished with the following lines of code: @@ -115,5 +95,6 @@ That's it. now we have _Avalanche_ up and running and we can start contribute to You can run _this chapter_ and play with it on Google Colaboratory: -{% embed url="https://colab.research.google.com/drive/1pSTUgftqqg2sFNlvM6ourNYLpt2lnCQf?usp=sharing" caption="Run the \"How to Install\" Chapter on Google Colab" %} - +{% embed url="https://colab.research.google.com/drive/1pSTUgftqqg2sFNlvM6ourNYLpt2lnCQf?usp=sharing" %} +Run the "How to Install" Chapter on Google Colab +{% endembed %} diff --git a/docs/gitbook/getting-started/learn-avalanche-in-5-minutes.md b/docs/gitbook/getting-started/learn-avalanche-in-5-minutes.md index 1320ce213..cb4a63d2d 100644 --- a/docs/gitbook/getting-started/learn-avalanche-in-5-minutes.md +++ b/docs/gitbook/getting-started/learn-avalanche-in-5-minutes.md @@ -6,6 +6,10 @@ description: A Short Guide for Researchers on the Run _Avalanche_ is mostly about making the life of a continual learning researcher easier. +![Avalanche modules.](<../.gitbook/assets/avalanche (1).png>) + +> #### +> > #### What are the **three pillars** of any respectful continual learning research project? * **`Benchmarks`**: Machine learning researchers need multiple benchmarks with efficient data handling utils to design and prototype new algorithms. Quantitative results on ever-changing benchmarks has been one of the driving forces of _Deep Learning_. diff --git a/docs/gitbook/getting-started/why-avalanche.md b/docs/gitbook/getting-started/why-avalanche.md index 40d7e1833..0caeb31e7 100644 --- a/docs/gitbook/getting-started/why-avalanche.md +++ b/docs/gitbook/getting-started/why-avalanche.md @@ -4,7 +4,7 @@ description: A Brief Introduction to Avalanche # Introduction -**Avalanche** was born within [ContinualAI](https://www.continualai.org/) with a clear goal in mind: +**Avalanche** was born within [ContinualAI](https://www.continualai.org) with a clear goal in mind: > ### _Pushing Continual Learning to the next level, providing a shared and collaborative library for fast prototyping, training and reproducible evaluation of continual learning algorithms._ @@ -12,22 +12,23 @@ As a powerful _avalanche_, a _Continual Learning_ agent _incrementally_ _improve We hope _Avalanche_ may trigger the same _**positive reinforcement loop**_ within our community, moving towards a more _**collaborative**_ **and inclusive** way of doing research and helping us tackle bigger problems, faster and better, but together! 👪 -{% embed url="https://www.youtube.com/watch?v=EyO1eM0-Hi8" caption="A complete Introduction to Avalanche: an End-to-End Library for Continual Learning." %} +{% embed url="https://www.youtube.com/watch?v=EyO1eM0-Hi8" %} +A complete Introduction to Avalanche: an End-to-End Library for Continual Learning. +{% endembed %} ## 💪The Avalanche Advantage Avalanche has several advantages: -* **Shared & Coherent Codebase**: Aren't you tired of re-inventing the wheel in continual learning? We are. Re-producing paper results has always been daunting in machine learning and it is even more so in continual learning. _Avalanche_ makes you stop re-write your \(and other people\) code all over again with a coherent and shared codebase that provides already all the utilities, benchmark, metrics and baselines you may need for your next great continual learning research project! -* **Errors Reduction**: The more code we write, the more bugs we introduce in our code. This is the rule, not the exception. _Avalanche_, let you focus on what really matters: defining your CL solution. _Benchmarks_ preparation to _training,_ _evaluation_ and _comparison_ with other methods will be already there for you. This in turn, massively reduce the amount of errors introduced and the time needed to debug your code. -* **Faster Prototyping**: As researchers or data scientists, we have dozens ideas every day and time is always too little to execute them. However, if we think about it, most of the time spent in bringing our ideas to life is consumed in installing software, preparing and cleaning our data, preparing the experiments code infrastructure and so on. _Avalanche_ lets you focus just on the original algorithmic proposal, taking care of most of the rest! -* **Improved Reproducibility & Portability**: One of the great features of _Avalanche_, is the possibility of reproducing experimental results easily and on any OS. Researchers can simply plug-in their algorithm into the codebase and see how it goes with respect of other researchers' methods. Their algorithm in turn, is used as a baseline for other methods, creating a virtuous circle. This is only possible thanks to the simple, yet powerful idea of providing shared _benchmarks_, _training_ and _evaluation_ in a single place. -* **Improved Modularity**: _Avalanche_ has been designed with modularity in mind. As you will learn more about Avalanche, you will realize we have sometimes forego simplicity in favor of modularity and reusability \(we hate code replication as you do 🤪\). However, we believe this will help us scale in the near future as we collaboratively bring this codebase into maturity. -* **Increased Efficiency & Scalability**: Full-stack researchers & data scientists know this, making your algorithm memory and computationally efficient is tough. _Avalanche_ is already optimized for you, so that you can run your ImageNet continual learning experiment on your 8GB laptop \(buy a cooling fan 💨\) or even try it on embedded devices of your latest product! +* **Shared & Coherent Codebase**: Aren't you tired of re-inventing the wheel in continual learning? We are. Re-producing paper results has always been daunting in machine learning and it is even more so in continual learning. _Avalanche_ makes you stop re-write your (and other people) code all over again with a coherent and shared codebase that provides already all the utilities, benchmark, metrics and baselines you may need for your next great continual learning research project! +* **Errors Reduction**: The more code we write, the more bugs we introduce in our code. This is the rule, not the exception. _Avalanche_, let you focus on what really matters: defining your CL solution. _Benchmarks_ preparation to _training,_ _evaluation_ and _comparison_ with other methods will be already there for you. This in turn, massively reduce the amount of errors introduced and the time needed to debug your code. +* **Faster Prototyping**: As researchers or data scientists, we have dozens ideas every day and time is always too little to execute them. However, if we think about it, most of the time spent in bringing our ideas to life is consumed in installing software, preparing and cleaning our data, preparing the experiments code infrastructure and so on. _Avalanche_ lets you focus just on the original algorithmic proposal, taking care of most of the rest! +* **Improved Reproducibility & Portability**: One of the great features of _Avalanche_, is the possibility of reproducing experimental results easily and on any OS. Researchers can simply plug-in their algorithm into the codebase and see how it goes with respect of other researchers' methods. Their algorithm in turn, is used as a baseline for other methods, creating a virtuous circle. This is only possible thanks to the simple, yet powerful idea of providing shared _benchmarks_, _training_ and _evaluation_ in a single place. +* **Improved Modularity**: _Avalanche_ has been designed with modularity in mind. As you will learn more about Avalanche, you will realize we have sometimes forego simplicity in favor of modularity and reusability (we hate code replication as you do 🤪). However, we believe this will help us scale in the near future as we collaboratively bring this codebase into maturity. +* **Increased Efficiency & Scalability**: Full-stack researchers & data scientists know this, making your algorithm memory and computationally efficient is tough. _Avalanche_ is already optimized for you, so that you can run your ImageNet continual learning experiment on your 8GB laptop (buy a cooling fan 💨) or even try it on embedded devices of your latest product! But most of all, _Avalanche_, can help us standardize our field and work better together, more collaboratively, towards our shared goal of making machines learn over time like humans do. _Avalanche_ the first experiment of a **End-to-end Library** for reproducible _continual learning_ research where you can find _benchmarks_, _algorithms, evaluation utilities_ and much more in the same place. Let's make it together 👫 a wonderful ride! 🎈 - diff --git a/docs/gitbook/how-to-contribute/guidelines.md b/docs/gitbook/how-to-contribute/guidelines.md index f6484559a..734a603fe 100644 --- a/docs/gitbook/how-to-contribute/guidelines.md +++ b/docs/gitbook/how-to-contribute/guidelines.md @@ -8,9 +8,14 @@ If you are here it means you are considering contributing to _Avalanche_. It is In order to contribute the this awesome framework we recommend to go through the "_From Zero to Hero_" _Avalanche_ Tutorial: +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + In this tutorial you'll learn Avalanche _in-depth_ and learn how to extend and contribute back to the community! In particular, be sure to read the "_Contribute to Avalanche_" chapter: -{% page-ref page="../from-zero-to-hero-tutorial/09\_contribute-to-avalanche.md" %} +{% content-ref url="../from-zero-to-hero-tutorial/09_contribute-to-avalanche.md" %} +[09\_contribute-to-avalanche.md](../from-zero-to-hero-tutorial/09\_contribute-to-avalanche.md) +{% endcontent-ref %} At the moment, we don't have a lot of rules for contributing or a strict _code of conduct_, please enjoy this freedom with a grain of salt! 😁 - diff --git a/docs/gitbook/how-tos/avalanchedataset/README.md b/docs/gitbook/how-tos/avalanchedataset/README.md index 0fa3820b4..83b69bd1a 100644 --- a/docs/gitbook/how-tos/avalanchedataset/README.md +++ b/docs/gitbook/how-tos/avalanchedataset/README.md @@ -4,12 +4,12 @@ description: Dealing with AvalancheDatasets # AvalancheDataset -The `AvalancheDataset` is an implementation of the PyTorch `Dataset` class that comes with many useful out-of-the-box functionalities. For most users, the *AvalancheDataset* can be used as a plain PyTorch Dataset that will return `x, y, t` elements. However, the AvalancheDataset is much more powerful than a simple PyTorch Dataset. +The `AvalancheDataset` is an implementation of the PyTorch `Dataset` class that comes with many useful out-of-the-box functionalities. For most users, the _AvalancheDataset_ can be used as a plain PyTorch Dataset that will return `x, y, t` elements. However, the AvalancheDataset is much more powerful than a simple PyTorch Dataset. -**A serie of _Mini How-Tos_** will guide you through the functionalities of the *AvalancheDataset* and its subclasses: +**A serie of **_**Mini How-Tos**_ will guide you through the functionalities of the _AvalancheDataset_ and its subclasses: -- [Preamble: PyTorch Datasets](https://avalanche.continualai.org/how-tos/avalanchedataset/preamble-pytorch-datasets) -- [Creating AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanchedataset/creating-avalanchedatasets) -- [Advanced Transformations](https://avalanche.continualai.org/how-tos/avalanchedataset/advanced-transformations) +* [Preamble: PyTorch Datasets](https://avalanche.continualai.org/how-tos/avalanchedataset/preamble-pytorch-datasets) +* [Creating AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanchedataset/creating-avalanchedatasets) +* [Advanced Transformations](https://avalanche.continualai.org/how-tos/avalanchedataset/advanced-transformations) -Brefore jumping to the actual *Mini How-To*s, **we recommend having a look at the basic notions of Dataset and DataLoader by reading the [Preamble page](https://avalanche.continualai.org/how-tos/avalanchedataset/preamble-pytorch-datasets)**. +Brefore jumping to the actual _Mini How-To_s, **we recommend having a look at the basic notions of Dataset and DataLoader by reading the** [**Preamble page**](https://avalanche.continualai.org/how-tos/avalanchedataset/preamble-pytorch-datasets). diff --git a/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md b/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md index c67d6a145..0f029b712 100644 --- a/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md +++ b/docs/gitbook/how-tos/avalanchedataset/advanced-transformations.md @@ -3,7 +3,9 @@ description: Dealing with transformations (groups, appending, replacing, freezin --- # Advanced Transformations -AvalancheDataset (and its subclasses like the Avalanche*Tensor/Subset/Concat*Dataset) allow for a finer control over transformations. While torchvision (and other) datasets allow for a minimal mechanism to apply transformations, with AvalancheDataset one can: + +AvalancheDataset (and its subclasses like the Avalanche_Tensor/Subset/Concat_Dataset) allow for a finer control over transformations. While torchvision (and other) datasets allow for a minimal mechanism to apply transformations, with AvalancheDataset one can: + 1. Have multiple **transformation "groups"** in the same dataset (like separated train and test transformations). 2. **Append, replace and remove transformations**, even by using nested Subset/Concat Datasets. 3. **Freeze transformations**, so that they can't be changed. @@ -14,7 +16,6 @@ It is warmly recommended to **run this page as a notebook** using Colab (info at Let's start by installing Avalanche: - ```python !pip install git+https://github.com/ContinualAI/avalanche.git @@ -25,12 +26,12 @@ Let's start by installing Avalanche: ``` ## Transformation groups + AvalancheDatasets can contain multiple **transformation groups**. This can be useful to keep train and test transformations in the same dataset and to have different set of transformations. This may come in handy in many situations (for instance, to apply ad-hoc transformations to replay data). As in torchvision datasets, AvalancheDataset supports the two kind of transformations: the `transform`, which is applied to X values, and the `target_transform`, which is applied to Y values. The latter is rarely used. This means that **a transformation group is a pair of transformations to be applied to the X and Y values** of each instance returned by the dataset. In both torchvision and Avalanche implementations, **a transformation must be a function (or other callable object)** that accepts one input (the X or Y value) and outputs its transformed version. This pair of functions is stored in the `transform` and `target_transform` fields of the dataset. A comprehensive guide on transformations can be found in the [torchvision documentation](https://pytorch.org/vision/stable/transforms.html). -In the following example, a MNIST dataset is created and then wrapped in an AvalancheDataset. When creating the AvalancheDataset, we can set *train* and *eval* transformations by passing a *transform\_groups* parameter. Train transformations usually include some form of random augmentation, while eval transformations usually include a sequence of deterministic transformations only. Here we define the sequence of train transformations as a random rotation followed by the ToTensor operation. The eval transformations only include the ToTensor operation. - +In the following example, a MNIST dataset is created and then wrapped in an AvalancheDataset. When creating the AvalancheDataset, we can set _train_ and _eval_ transformations by passing a _transform\_groups_ parameter. Train transformations usually include some form of random augmentation, while eval transformations usually include a sequence of deterministic transformations only. Here we define the sequence of train transformations as a random rotation followed by the ToTensor operation. The eval transformations only include the ToTensor operation. ```python from torchvision import transforms @@ -60,8 +61,7 @@ transform_groups = { avl_mnist_transform = AvalancheDataset(mnist_dataset, transform_groups=transform_groups) ``` -Of course, one can also just use the `transform` and `target_transform` constructor parameters to set the transformations for both the *train* and the *eval* groups. However, it is recommended to use the approach based on *transform\_groups* (shown in the code above) as it is much more flexible. - +Of course, one can also just use the `transform` and `target_transform` constructor parameters to set the transformations for both the _train_ and the _eval_ groups. However, it is recommended to use the approach based on _transform\_groups_ (shown in the code above) as it is much more flexible. ```python # Not recommended: use transform_groups instead @@ -70,16 +70,15 @@ avl_mnist_same_transforms = AvalancheDataset(mnist_dataset, transform=train_tra ### Using `.train()` and `.eval()` -**The default behaviour of the AvalancheDataset is to use transformations from the _train_ group.** However, one can easily obtain a version of the dataset where the *eval* group is used. Note: when obtaining the dataset of experiences from the test stream, those datasets will already be using the *eval* group of transformations so you don't need to switch to the eval group ;). +**The default behaviour of the AvalancheDataset is to use transformations from the **_**train**_** group.** However, one can easily obtain a version of the dataset where the _eval_ group is used. Note: when obtaining the dataset of experiences from the test stream, those datasets will already be using the _eval_ group of transformations so you don't need to switch to the eval group ;). -As noted before, transformations for the current group are loaded in the `transform` and `target_transform` fields. These fields can be changed directly, but this is *NOT* recommended, as this will not create a copy of the dataset and may probably affect other parts of the code in which the dataset is used. +As noted before, transformations for the current group are loaded in the `transform` and `target_transform` fields. These fields can be changed directly, but this is _NOT_ recommended, as this will not create a copy of the dataset and may probably affect other parts of the code in which the dataset is used. -The recommended way to switch between the *train* and *eval* groups is to use the `.train()` and `.eval()` methods to obtain a copy (view) of the dataset with the proper transformations enabled. This is another very handy feature of the AvalancheDataset: **methods that manipulate the AvalancheDataset fields (and transformations) always create a view of the dataset. The original dataset is never changed.** +The recommended way to switch between the _train_ and _eval_ groups is to use the `.train()` and `.eval()` methods to obtain a copy (view) of the dataset with the proper transformations enabled. This is another very handy feature of the AvalancheDataset: **methods that manipulate the AvalancheDataset fields (and transformations) always create a view of the dataset. The original dataset is never changed.** -In the following cell we use the *avl\_mnist\_transform* dataset created in the cells above. We first obtain a view of it in which *eval* transformations are enabled. Then, starting from this view, we obtain a version of it in which *train* transformations are enabled. We want to double-stress that `.train()` and `.eval()` never change the group of the dataset on which they are called: they always create a view. - -One can check that the correct transformation group is in use by looking at the content of the *transform/target_transform* fields. +In the following cell we use the _avl\_mnist\_transform_ dataset created in the cells above. We first obtain a view of it in which _eval_ transformations are enabled. Then, starting from this view, we obtain a version of it in which _train_ transformations are enabled. We want to double-stress that `.train()` and `.eval()` never change the group of the dataset on which they are called: they always create a view. +One can check that the correct transformation group is in use by looking at the content of the _transform/target\_transform_ fields. ```python # Obtain a view of the dataset in which eval transformations are enabled @@ -109,10 +108,10 @@ print('Back to train transformations:', avl_mnist_train.transform) ``` ### Custom transformation groups -In *AvalancheDataset*s the **_train_ and _eval_ transformation groups are always available**. However, *AvalancheDataset* also supports **custom transformation groups**. -The following example shows how to create an AvalancheDataset with an additional group named *replay*. We define the replay transformation as a random crop followed by the ToTensor operation. +In _AvalancheDataset_s the _**train**_** and **_**eval**_** transformation groups are always available**. However, _AvalancheDataset_ also supports **custom transformation groups**. +The following example shows how to create an AvalancheDataset with an additional group named _replay_. We define the replay transformation as a random crop followed by the ToTensor operation. ```python replay_transform = transforms.Compose([ @@ -131,15 +130,15 @@ transform_groups_with_replay = { AvalancheDataset(mnist_dataset, transform_groups=transform_groups_with_replay) ``` -However, once created the dataset will use the *train* group. There are two ways to **switch to our custom group**: -- Set the group when creating the dataset using the `initial_transform_group` constructor parameter -- Switch to the group using the `.with_transforms(group_name)` method +However, once created the dataset will use the _train_ group. There are two ways to **switch to our custom group**: + +* Set the group when creating the dataset using the `initial_transform_group` constructor parameter +* Switch to the group using the `.with_transforms(group_name)` method The `.with_transforms(group_name)` method behaves in the same way `.train()` and `.eval()` do by creating a view of the original dataset. The following example shows how to use both methods: - ```python # Method 1: create the dataset with "replay" as the default group avl_mnist_custom_transform_1 = AvalancheDataset( @@ -166,8 +165,7 @@ print(avl_mnist_custom_transform_2.transform) ## Appending transformations -In the standard torchvision datasets the only way to append (that is, add a new transformation step to the list of existing one) is to change the *transform* field directly by doing something like this: - +In the standard torchvision datasets the only way to append (that is, add a new transformation step to the list of existing one) is to change the _transform_ field directly by doing something like this: ```python # Append a transform by using torchvision datasets (>>> DON'T DO THIS! <<<) @@ -189,14 +187,14 @@ print(mnist_dataset_w_totensor.transform) ``` This solution has many huge drawbacks: -- The transformation field of the dataset is changed directly. This will affect other parts of the code that use that dataset instance. -- If the initial transform is `None`, then `Compose` will not complain, but the process will crash later (try it by yourself: replace the first element of Compose in cell above with `None`, then try obtaining a data point from the dataset). -- If you need to change transformations only temporarly to do some specific things in a limited part of the code, then you need to store the previous set of transformations in some variable in order to switch back to them later. -AvalancheDataset offers a very simple method to append transformations without incurring in those issues. The `.add_transforms(transform=None, target_transform=None)` method will append the given transform(s) **to the currently enabled transform group** and will return a new (a view actually) dataset with given transformations appended to the existing ones. The original dataset is not affected. One can also use `.add_transforms_to_group(group_name, transform, target_transform)` to change transformations for a different group. +* The transformation field of the dataset is changed directly. This will affect other parts of the code that use that dataset instance. +* If the initial transform is `None`, then `Compose` will not complain, but the process will crash later (try it by yourself: replace the first element of Compose in cell above with `None`, then try obtaining a data point from the dataset). +* If you need to change transformations only temporarly to do some specific things in a limited part of the code, then you need to store the previous set of transformations in some variable in order to switch back to them later. -The next cell shows how to use `.add_transforms(...)` to append the *to\_append\_transform* transform defined in the cell above. +AvalancheDataset offers a very simple method to append transformations without incurring in those issues. The `.add_transforms(transform=None, target_transform=None)` method will append the given transform(s) **to the currently enabled transform group** and will return a new (a view actually) dataset with given transformations appended to the existing ones. The original dataset is not affected. One can also use `.add_transforms_to_group(group_name, transform, target_transform)` to change transformations for a different group. +The next cell shows how to use `.add_transforms(...)` to append the _to\_append\_transform_ transform defined in the cell above. ```python # Create the dataset @@ -219,19 +217,18 @@ print('Original dataset:', avl_mnist.transform) Note that by using `.add_transforms(...)`: -- The original dataset is not changed, which means that other parts of the code that use that dataset instance are not affected. -- You don't need to worry about *None* transformations. -- In order to revert to the original transformations you don't need to keep a copy of them: the original dataset is not affected! +* The original dataset is not changed, which means that other parts of the code that use that dataset instance are not affected. +* You don't need to worry about _None_ transformations. +* In order to revert to the original transformations you don't need to keep a copy of them: the original dataset is not affected! ## Replacing transformations -The replacement operation follows the same idea (and benefits) of the append one. By using `.replace_transforms(transform, target_transform)` one can obtain a view of the original dataset in which the **transformaations for the current group** are replaced with the given ones. One may also change tranformations for other groups by passing the name of the group as the optional parameter `group`. As with any transform-related operation, the original dataset is not affected. +The replacement operation follows the same idea (and benefits) of the append one. By using `.replace_transforms(transform, target_transform)` one can obtain a view of the original dataset in which the **transformaations for the current group** are replaced with the given ones. One may also change tranformations for other groups by passing the name of the group as the optional parameter `group`. As with any transform-related operation, the original dataset is not affected. Note: one can use `.replace_transforms(...)` to remove previous transformations (by passing `None` as the new transform). The following cell shows how to use `.replace_transforms(...)` to replace the transformations of the current group: - ```python new_transform = transforms.RandomCrop(size=(28, 28), padding=4) @@ -254,8 +251,7 @@ One may wonder when this may come in handy... in fact, you will probably rarely Transformations for all transform groups can be frozen at once by using `.freeze_transforms()`. Transformations can be frozen for a single group by using `.freeze_group_transforms(group_name)`. As always, those methods return a view of the original dataset. -The cell below shows a simplified excerpt from the [PermutedMNIST benchmark implementation](https://github.com/ContinualAI/avalanche/blob/master/avalanche/benchmarks/classic/cmnist.py). First, a *PixelsPermutation* instance is created. That instance is a transformation that will permute the pixels of the input image. We then create the train end test sets. Once created, transformations for those datasets are frozen using `.freeze_transforms()`. - +The cell below shows a simplified excerpt from the [PermutedMNIST benchmark implementation](../../../../avalanche/benchmarks/classic/cmnist.py). First, a _PixelsPermutation_ instance is created. That instance is a transformation that will permute the pixels of the input image. We then create the train end test sets. Once created, transformations for those datasets are frozen using `.freeze_transforms()`. ```python from avalanche.benchmarks.classic.cmnist import PixelsPermutation @@ -299,7 +295,6 @@ In this way, that transform can't be removed. However, remember that one can alw The cell below shows that `replace_transforms` can't remove frozen transformations: - ```python # First, show that the image pixels are permuted print('Before replace_transforms:') @@ -314,11 +309,12 @@ display(with_removed_transforms[0][0].resize((192, 192), 0)) ``` ## Transformations wrap-up -This completes the *Mini How-To* for the functionalities of the *AvalancheDataset* related to **transformations**. + +This completes the _Mini How-To_ for the functionalities of the _AvalancheDataset_ related to **transformations**. Here you learned how to use **transformation groups** and how to **append/replace/freeze transformations** in a simple way. -Other *Mini How-To*s will guide you through the other functionalities offered by the *AvalancheDataset* class. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanchedataset). +Other _Mini How-To_s will guide you through the other functionalities offered by the _AvalancheDataset_ class. The list of _Mini How-To_s can be found [here](https://avalanche.continualai.org/how-tos/avalanchedataset). ## 🤝 Run it on Google Colab diff --git a/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md b/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md index 07ca936f4..6f9c95a88 100644 --- a/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md +++ b/docs/gitbook/how-tos/avalanchedataset/creating-avalanchedatasets.md @@ -4,19 +4,18 @@ description: Creation and manipulation of AvalancheDatasets and its subclasses. # Creating AvalancheDatasets -The *AvalancheDataset* is an implementation of the PyTorch Dataset class which comes with many out-of-the-box functionalities. The *AvalancheDataset* (an its few subclass) are extensively used through the whole Avalanche library as the reference way to manipulate datasets: +The _AvalancheDataset_ is an implementation of the PyTorch Dataset class which comes with many out-of-the-box functionalities. The _AvalancheDataset_ (an its few subclass) are extensively used through the whole Avalanche library as the reference way to manipulate datasets: -- The dataset carried by the `experience.dataset` field is always an *AvalancheDataset*. -- Benchmark creation functions accept *AvalancheDataset*s to create benchmarks where a finer control over task labels is required. -- Internally, benchmarks are created by manipulating *AvalancheDataset*s. +* The dataset carried by the `experience.dataset` field is always an _AvalancheDataset_. +* Benchmark creation functions accept _AvalancheDataset_s to create benchmarks where a finer control over task labels is required. +* Internally, benchmarks are created by manipulating _AvalancheDataset_s. -This first *Mini How-To* will guide through the main ways you can use to **instantiate an _AvalancheDataset_** while the **other Mini How-Tos ([complete list here](https://avalanche.continualai.org/how-tos/avalanchedataset)) will show how to use its functionalities**. +This first _Mini How-To_ will guide through the main ways you can use to **instantiate an **_**AvalancheDataset**_ while the **other Mini How-Tos (**[**complete list here**](https://avalanche.continualai.org/how-tos/avalanchedataset)**) will show how to use its functionalities**. It is warmly recommended to **run this page as a notebook** using Colab (info at the bottom of this page). Let's start by installing avalanche: - ```python !pip install git+https://github.com/ContinualAI/avalanche.git @@ -27,24 +26,26 @@ Let's start by installing avalanche: ``` ## AvalancheDataset vs PyTorch Dataset -This mini How-To will guide you through the main ways used to instantiate an *AvalancheDataset*. + +This mini How-To will guide you through the main ways used to instantiate an _AvalancheDataset_. First thing: the base class `AvalancheDataset` is a **wrapper for existing datasets**. Only two things must be considered when wrapping an existing dataset: -- Apart from the x and y values, the resulting AvalancheDataset will also return a third value: the task label (which defaults to 0). -- The wrapped dataset must contain a valid **targets** field. +* Apart from the x and y values, the resulting AvalancheDataset will also return a third value: the task label (which defaults to 0). +* The wrapped dataset must contain a valid **targets** field. + +The **targets field** is available is nearly all _torchvision_ datasets. It must be a list containing the label for each data point (usually the y value). In this way, Avalanche can use that field when instantiating benchmarks like the "Class/Task-Incremental\* and _Domain-Incremental_ ones. -The **targets field** is available is nearly all *torchvision* datasets. It must be a list containing the label for each data point (usually the y value). In this way, Avalanche can use that field when instantiating benchmarks like the "Class/Task-Incremental* and *Domain-Incremental* ones. +Avalanche exposes 4 classes of _AvalancheDataset_s which map exactly the 4 _Dataset_ classes offered by PyTorch: -Avalanche exposes 4 classes of *AvalancheDataset*s which map exactly the 4 *Dataset* classes offered by PyTorch: -- `AvalancheDataset`: the base class, which acts a wrapper to existing *Dataset* instances. -- `AvalancheTensorDataset`: equivalent to PyTorch `TesnsorDataset`. -- `AvalancheSubset`: equivalent to PyTorch `Subset`. -- `AvalancheConcatDataset`: equivalent to PyTorch `ConcatDataset`. +* `AvalancheDataset`: the base class, which acts a wrapper to existing _Dataset_ instances. +* `AvalancheTensorDataset`: equivalent to PyTorch `TesnsorDataset`. +* `AvalancheSubset`: equivalent to PyTorch `Subset`. +* `AvalancheConcatDataset`: equivalent to PyTorch `ConcatDataset`. ## 🛠️ Create an AvalancheDataset -Given a dataset (like MNIST), an *AvalancheDataset* can be instantiated as follows: +Given a dataset (like MNIST), an _AvalancheDataset_ can be instantiated as follows: ```python from avalanche.benchmarks.utils import AvalancheDataset @@ -59,7 +60,6 @@ mnist_avalanche_dataset = AvalancheDataset(mnist_dataset) Just like any other Dataset, a data point can be obtained using the `x, y = dataset[idx]` syntax. **When obtaining a data point from an AvalancheDataset, an additional third value (the task label) will be returned**: - ```python # Obtain the first instance from the original dataset x, y = mnist_dataset[0] @@ -72,8 +72,7 @@ print(f'x={x}, y={y}, t={t}') # Output: "x=, y=5, t=0" ``` -**Useful tip:** if you are not sure if you are dealing with a PyTorch *Dataset* or an *AvalancheDataset*, or if you want to ignore task labels, you can use this syntax: - +**Useful tip:** if you are not sure if you are dealing with a PyTorch _Dataset_ or an _AvalancheDataset_, or if you want to ignore task labels, you can use this syntax: ```python # You can use "x, y, *_" to manage both kinds of Datasets @@ -82,10 +81,10 @@ x, y, *_ = mnist_avalanche_dataset[0] # OK ``` ## The AvalancheTensorDataset -The PyTorch *TensorDataset* is one of the most useful Dataset classes as it can be used to quickly prototype the data loading part of your code. -A *TensorDataset* can be wrapped in an AvalancheDataset just like any Dataset, but this is not much convenient, as shown below: +The PyTorch _TensorDataset_ is one of the most useful Dataset classes as it can be used to quickly prototype the data loading part of your code. +A _TensorDataset_ can be wrapped in an AvalancheDataset just like any Dataset, but this is not much convenient, as shown below: ```python import torch @@ -112,7 +111,6 @@ print(f'x={x}, y={y}, t={t}') **Instead, it is recommended to use the AvalancheTensorDataset** class to get the same result. In this way, you can just skip one intermediate step. - ```python from avalanche.benchmarks.utils import AvalancheTensorDataset @@ -125,10 +123,9 @@ print(f'x={x}, y={y}, t={t}') # Output: "x=tensor([0.6329, 0.8495, 0.1853, 0.7254, 0.7893, 0.8079, 0.1106]), y=4, t=0" ``` -In both cases, **AvalancheDataset will automatically populate its _targets_ field by using the values from the second Tensor** (which usually contains the Y values). This behaviour can be customized by passing a custom `targets` constructor parameter (by either passing a list of targets or the index of the Tensor to use). - -The cell below shows the content of the target field of the dataset created in the cell above. Notice that the *targets* field has been filled with the content of the second Tensor (*y\_data*). +In both cases, **AvalancheDataset will automatically populate its **_**targets**_** field by using the values from the second Tensor** (which usually contains the Y values). This behaviour can be customized by passing a custom `targets` constructor parameter (by either passing a list of targets or the index of the Tensor to use). +The cell below shows the content of the target field of the dataset created in the cell above. Notice that the _targets_ field has been filled with the content of the second Tensor (_y\_data_). ```python # Check the targets field @@ -141,12 +138,12 @@ print('targets field=', avl_tensor_dataset.targets) ``` ## The AvalancheSubset and AvalancheConcatDataset classes -Avalanche offers the `AvalancheSubset` and `AvalancheConcatDataset` implementations that extend the functionalities of PyTorch *Subset* and *ConcatDataset*. -Regarding the subsetting operation, `AvalancheSubset` behaves in the same way the PyTorch `Subset` class does: both implementations accept a dataset and a list of indices as parameters. The resulting Subset is not a copy of the dataset, it's just a view. This is similar to creating a view of a NumPy array by passing a list of indexes using the `numpy_array[list_of_indices]` syntax. This can be used to both *create a smaller dataset* and to *change the order of data points* in the dataset. +Avalanche offers the `AvalancheSubset` and `AvalancheConcatDataset` implementations that extend the functionalities of PyTorch _Subset_ and _ConcatDataset_. -Here we create a toy dataset in which each X and Y values are *int*s. We then obtain a subset of it by creating an **AvalancheSubset**: +Regarding the subsetting operation, `AvalancheSubset` behaves in the same way the PyTorch `Subset` class does: both implementations accept a dataset and a list of indices as parameters. The resulting Subset is not a copy of the dataset, it's just a view. This is similar to creating a view of a NumPy array by passing a list of indexes using the `numpy_array[list_of_indices]` syntax. This can be used to both _create a smaller dataset_ and to _change the order of data points_ in the dataset. +Here we create a toy dataset in which each X and Y values are _int_s. We then obtain a subset of it by creating an **AvalancheSubset**: ```python from avalanche.benchmarks.utils import AvalancheSubset @@ -183,10 +180,9 @@ for x, y, t in avl_subset: # x=52, y=12, t=0 ``` -Concatenation is even simpler. Just like with PyTorch *ConcatDataset*, one can easily concatentate datasets with **AvalancheConcatDataset**. - -Both *AvalancheConcatDataset* and PyTorch *ConcatDataset* accept a list of datasets to concatenate. +Concatenation is even simpler. Just like with PyTorch _ConcatDataset_, one can easily concatentate datasets with **AvalancheConcatDataset**. +Both _AvalancheConcatDataset_ and PyTorch _ConcatDataset_ accept a list of datasets to concatenate. ```python from avalanche.benchmarks.utils import AvalancheConcatDataset @@ -222,9 +218,10 @@ for x, y, t in avl_concat: ``` ## Dataset Creation wrap-up -This *Mini How-To* showed you how to **create instances of AvalancheDataset (and its subclasses)**. -Other *Mini How-To*s will guide you through the functionalities offered by AvalancheDataset. The list of *Mini How-To*s can be found [here](https://avalanche.continualai.org/how-tos/avalanchedataset). +This _Mini How-To_ showed you how to **create instances of AvalancheDataset (and its subclasses)**. + +Other _Mini How-To_s will guide you through the functionalities offered by AvalancheDataset. The list of _Mini How-To_s can be found [here](https://avalanche.continualai.org/how-tos/avalanchedataset). ## 🤝 Run it on Google Colab diff --git a/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md b/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md index a23811b7e..9030f3854 100644 --- a/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md +++ b/docs/gitbook/how-tos/avalanchedataset/preamble-pytorch-datasets.md @@ -3,47 +3,56 @@ description: Few words about PyTorch Datasets --- # Preamble: PyTorch Datasets + This short preamble will briefly go through the basic notions of Dataset offered natively by PyTorch. A solid grasp of these notions are needed to understand: + 1. How PyTorch data loading works in general 2. How AvalancheDatasets differs from PyTorch Datasets ## 📚 Dataset: general definition In PyTorch, **a `Dataset` is a class** exposing two methods: -- `__len__()`, which returns the amount of instances in the dataset (as an `int`). -- `__getitem__(idx)`, which returns the data point at index `idx`. + +* `__len__()`, which returns the amount of instances in the dataset (as an `int`). +* `__getitem__(idx)`, which returns the data point at index `idx`. In other words, a Dataset instance is just an object for which, similarly to a list, one can simply: -- Obtain its length using the Python `len(dataset)` function. -- Obtain a single data point using the `x, y = dataset[idx]` syntax. + +* Obtain its length using the Python `len(dataset)` function. +* Obtain a single data point using the `x, y = dataset[idx]` syntax. The content of the dataset can be either loaded in memory when the dataset is instantiated (like the torchvision MNIST dataset does) or, for big datasets like ImageNet, the content is kept on disk, with the dataset keeping the list of files in an internal field. In this case, data is loaded from the storage on-the-fly when `__getitem__(idx)` is called. The way those things are managed is specific to each dataset implementation. ## PyTorch Datasets + The PyTorch library offers 4 Dataset implementations: -- `Dataset`: an interface defining the `__len__` and `__getitem__` methods. -- `TensorDataset`: instantiated by passing X and Y tensors. Each row of the X and Y tensors is interpreted as a data point. The `__getitem__(idx)` method will simply return the `idx`-th row of X and Y tensors. -- `ConcatDataset`: instantiated by passing a list of datasets. The resulting dataset is a concatenation of those datasets. -- `Subset`: instantiated by passing a dataset and a list of indices. The resulting dataset will only contain the data points described by that list of indices. -As explained in the mini *How-To*s, Avalanche offers a customized version for all these 4 datasets. +* `Dataset`: an interface defining the `__len__` and `__getitem__` methods. +* `TensorDataset`: instantiated by passing X and Y tensors. Each row of the X and Y tensors is interpreted as a data point. The `__getitem__(idx)` method will simply return the `idx`-th row of X and Y tensors. +* `ConcatDataset`: instantiated by passing a list of datasets. The resulting dataset is a concatenation of those datasets. +* `Subset`: instantiated by passing a dataset and a list of indices. The resulting dataset will only contain the data points described by that list of indices. + +As explained in the mini _How-To_s, Avalanche offers a customized version for all these 4 datasets. ## Transformations -Most datasets from the *torchvision* libraries (as well as datasets found "in the wild") allow for a `transformation` function to be passed to the dataset constructor. The support for transformations is not mandatory for a dataset, but it is quite common to support them. The transformation is used to process the X value of a data point before returning it. This is used to normalize values, apply augmentations, etcetera. -As explained in the mini *How-To*s, the `AvalancheDataset` class implements a very rich and powerful set of functionalities for managing transformations. +Most datasets from the _torchvision_ libraries (as well as datasets found "in the wild") allow for a `transformation` function to be passed to the dataset constructor. The support for transformations is not mandatory for a dataset, but it is quite common to support them. The transformation is used to process the X value of a data point before returning it. This is used to normalize values, apply augmentations, etcetera. + +As explained in the mini _How-To_s, the `AvalancheDataset` class implements a very rich and powerful set of functionalities for managing transformations. ## Quick note on the IterableDataset class + A variation of the standard `Dataset` exist in PyTorch: the [IterableDataset](https://pytorch.org/docs/stable/data.html#iterable-style-datasets). When using an `IterableDataset`, one can load the data points in a sequential way only (by using a tape-alike approach). The `dataset[idx]` syntax and `len(dataset)` function are not allowed. **Avalanche does NOT support `IterableDataset`s.** You shouldn't worry about this because, realistically, you will never encounter such datasets. ## DataLoader + The `Dataset` is a very simple object that only returns one data point given its index. In order to create minibatches and speed-up the data loading process, a `DataLoader` is required. The PyTorch `DataLoader` class is a very efficient mechanism that, given a `Dataset`, will return **minibatches** by optonally **shuffling** data brefore each epoch and by **loading data in parallel** by using multiple workers. ## Preamble wrap-up -To wrap-up, let's see how the native, *non-Avalanche*, PyTorch components work in practice. In the following code we create a `TensorDataset` and then we load it in minibatches using a `DataLoader`. +To wrap-up, let's see how the native, _non-Avalanche_, PyTorch components work in practice. In the following code we create a `TensorDataset` and then we load it in minibatches using a `DataLoader`. ```python import torch @@ -67,9 +76,10 @@ for x_minibatch, y_minibatch in my_dataloader: ``` ## Next steps -With these notions in mind, you can start start your journey on understanding the functionalities offered by the AvalancheDatasets by going through the *Mini How-To*s. -Please refer to the [list of the *Mini How-To*s regarding AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanchedataset) for a complete list. It is recommended to start with the **"Creating AvalancheDatasets"** *Mini How-To*. +With these notions in mind, you can start start your journey on understanding the functionalities offered by the AvalancheDatasets by going through the _Mini How-To_s. + +Please refer to the [list of the _Mini How-To_s regarding AvalancheDatasets](https://avalanche.continualai.org/how-tos/avalanchedataset) for a complete list. It is recommended to start with the **"Creating AvalancheDatasets"** _Mini How-To_. ## 🤝 Run it on Google Colab diff --git a/docs/gitbook/questions-and-issues/add-your-issue.md b/docs/gitbook/questions-and-issues/add-your-issue.md index f6ac1cd35..aea080e38 100644 --- a/docs/gitbook/questions-and-issues/add-your-issue.md +++ b/docs/gitbook/questions-and-issues/add-your-issue.md @@ -4,13 +4,12 @@ description: Help us Find Bug in Avalanche # Add Your Issue -If you encounter a problem in _Avalanche_, please do not give up on us and help us fix it as soon as possible**. This first of all means reporting it**. We are grateful to all the people who took the time _to report an issue_ or even fix it with a _Pull Request_. +If you encounter a problem in _Avalanche_, please do not give up on us and help us fix it as soon as possible**. This first of all means reporting it**. We are grateful to all the people who took the time _to report an issue_ or even fix it with a _Pull Request_. -![Open Issues for the Avalanche Project](../.gitbook/assets/issues.png) +![Open Issues for the Avalanche Project](../../../.gitbook/assets/issues.png) Check current _Avalanche_ issue or **submit a new one** here: {% embed url="https://github.com/ContinualAI/avalanche" %} Please try to use the _appropriate tags_ and explain your issue with a simple _code snippet_ to reproduce it. - diff --git a/docs/gitbook/questions-and-issues/ask-your-question.md b/docs/gitbook/questions-and-issues/ask-your-question.md index 0db6be777..db07e5f2e 100644 --- a/docs/gitbook/questions-and-issues/ask-your-question.md +++ b/docs/gitbook/questions-and-issues/ask-your-question.md @@ -1,5 +1,5 @@ --- -description: 'To get Answers of Life, Ask Questions' +description: To get Answers of Life, Ask Questions --- # Ask Your Question @@ -10,13 +10,13 @@ We know that learning a new tool may be tough at times. This is why we are here Don't be afraid to ask questions, there are no stupid questions and we will always answer to you. {% endhint %} -_However, in order to help you, we need you to help us first._ - -First of all, if the question is more of a _code issue_ **please use** the ****[**GitHub Issues**](https://github.com/ContinualAI/avalanche/issues) ****page. +_However, in order to help you, we need you to help us first._ \ +\ +First of all, if the question is more of a _code issue_ **please use** the **** [**GitHub Issues**](https://github.com/ContinualAI/avalanche/issues) **** page.\ For _general questions, ideas,_ [_feature-requests_](request-a-feature.md) _and discussions_ use [**GitHub Discussions**](https://github.com/ContinualAI/avalanche/discussions). -If instead, this is a _quick question about Avalanche or a request for support_, in this case you can ask us directly [on slack](https://join.slack.com/t/continualai/shared_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU) \(**\#avalanche** channel\). - +If instead, this is a _quick question about Avalanche or a request for support_, in this case you can ask us directly [on slack](https://join.slack.com/t/continualai/shared\_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU) (**#avalanche** channel).\ +\ In any case, please make sure to follow the steps below: 1. _Clarify your information needs._ @@ -26,5 +26,3 @@ In any case, please make sure to follow the steps below: Then we will try to answer as swiftly as possible! 🤗 - - diff --git a/docs/gitbook/questions-and-issues/give-feedback.md b/docs/gitbook/questions-and-issues/give-feedback.md index 24c0ccf93..40ca420ea 100644 --- a/docs/gitbook/questions-and-issues/give-feedback.md +++ b/docs/gitbook/questions-and-issues/give-feedback.md @@ -4,15 +4,17 @@ description: We are all ears! # Give Feedback -_Avalanche_ is a tool **from** the continual learning research community and **for** the continual learning research community. We try to keep the design of _Avalanche_ as _open_, _collaborative_ and _inclusive_ as possible. - -This is why **we are always keen to hear your feedback** about _Avalanche!_ Join directly [**on slack**](https://join.slack.com/t/continualai/shared_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU) \(**\#avalanche** channel\) for a quick feedback or write a post on [**GitHub Discussions**](https://github.com/ContinualAI/avalanche/discussions)! +_Avalanche_ is a tool **from** the continual learning research community and **for** the continual learning research community. We try to keep the design of _Avalanche_ as _open_, _collaborative_ and _inclusive_ as possible.\ +\ +This is why **we are always keen to hear your feedback** about _Avalanche!_ Join directly [**on slack**](https://join.slack.com/t/continualai/shared\_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU) (**#avalanche** channel) for a quick feedback or write a post on [**GitHub Discussions**](https://github.com/ContinualAI/avalanche/discussions)! -![General Feedback Section of the Avalanche GitHub "Discussions" Tab.](../.gitbook/assets/feedback.png) - -{% embed url="https://join.slack.com/t/continualai/shared\_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU" caption="Click Above to Join ContinualAI Slack" %} - -{% embed url="https://github.com/ContinualAI/avalanche/discussions" caption="Click Above to Join the Discussion!" %} +![General Feedback Section of the Avalanche GitHub "Discussions" Tab.](../../../.gitbook/assets/feedback.png) +{% embed url="https://join.slack.com/t/continualai/shared_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU" %} +Click Above to Join ContinualAI Slack +{% endembed %} +{% embed url="https://github.com/ContinualAI/avalanche/discussions" %} +Click Above to Join the Discussion! +{% endembed %} diff --git a/docs/gitbook/questions-and-issues/request-a-feature.md b/docs/gitbook/questions-and-issues/request-a-feature.md index 36a84777c..747102403 100644 --- a/docs/gitbook/questions-and-issues/request-a-feature.md +++ b/docs/gitbook/questions-and-issues/request-a-feature.md @@ -8,11 +8,10 @@ description: Help us Design Avalanche of the Future Do you think an **important feature is missing** in Avalanche? You are in the right place! {% endhint %} -We try to keep the design of _Avalanche_ as _open_, _collaborative_ and _inclusive_ as possible. This means discussing _Avalanche_ issues, development and future ideas openly through general [ContinualAI projects meetups](https://www.continualai.org/news/#meetup), its [slack channel](https://join.slack.com/t/continualai/shared_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU), [Github](https://github.com/vlomonaco) and [forum](https://continualai.discourse.group). +We try to keep the design of _Avalanche_ as _open_, _collaborative_ and _inclusive_ as possible. This means discussing _Avalanche_ issues, development and future ideas openly through general [ContinualAI projects meetups](https://www.continualai.org/news/#meetup), its [slack channel](https://join.slack.com/t/continualai/shared\_invite/enQtNjQxNDYwMzkxNzk0LTBhYjg2MjM0YTM2OWRkNDYzOGE0ZTIzNDQ0ZGMzNDE3ZGUxNTZmNmM1YzJiYzgwMTkyZDQxYTlkMTI3NzZkNjU), [Github](https://github.com/vlomonaco) and [forum](https://continualai.discourse.group). If you'd like to add a new feature to _Avalanche_ please let us know, so we can work on it, or team up with you to make it a happen! 😄 Features request can be opened on the appropriate [GitHub Discussion Feature-Request section](https://github.com/ContinualAI/avalanche/discussions/categories/feature-request). Vote your preferred features and we will try to implement the most voted first! -![Feature-request section of the Avalanche GitHub "Discussions" Tab. ](../.gitbook/assets/requerst.png) - +![Feature-request section of the Avalanche GitHub "Discussions" Tab. ](../../../.gitbook/assets/requerst.png) From ac83c45a6feb575d655a2ac512c17aaf30a2b0fb Mon Sep 17 00:00:00 2001 From: Andrea Cossu Date: Thu, 16 Dec 2021 15:52:34 +0100 Subject: [PATCH 40/60] Fixed bug in setuptools --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 24f88ea88..3850c0f6d 100644 --- a/setup.py +++ b/setup.py @@ -51,5 +51,6 @@ def get_version(rel_path): 'torch', 'torchvision', 'gdown', + 'setuptools<=59.5.0' ] ) From c82526ad1296d0b92141909210c65520a9036135 Mon Sep 17 00:00:00 2001 From: Andrea Cossu Date: Fri, 17 Dec 2021 17:18:33 +0100 Subject: [PATCH 41/60] Added beta release and pip install details --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c67a11ec8..642b5b9ed 100644 --- a/README.md +++ b/README.md @@ -83,10 +83,10 @@ Current Release Avalanche is a framework in constant development. Thanks to the support of the [ContinualAI]() community and its active members we are quickly extending its features and improve its usability based on the demands of our research community! -A the moment, Avalanche is in [**Alpha v0.0.1**](https://avalanche.continualai.org/getting-started/alpha-version), but we already support [several *Benchmarks*, *Strategies* and *Metrics*](https://avalanche.continualai.org/getting-started/alpha-version), that make it, we believe, the best tool out there for your continual learning research! 💪 +A the moment, Avalanche is in [**Beta (v0.1.0)**](https://github.com/ContinualAI/avalanche/releases/tag/v0.1.0). We support [several *Benchmarks*, *Strategies* and *Metrics*](https://avalanche.continualai.org/getting-started/alpha-version), that make it, we believe, the best tool out there for your continual learning research! 💪 -*Please note that, at the moment, we **do not** support stable releases and packaged versions of the library.* -*We do this intentionally as in this early phase we would like to stimulate contributions only from experienced CL researchers and coders.* +**You can install Avalanche by running `pip install avalanche-lib`.** +Look [here](https://avalanche.continualai.org/getting-started/how-to-install) for a more complete guide on the different ways available to install Avalanche. Getting Started ---------------- From eb68a711c60bf5c293aeb7b0d08a8663c602f516 Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Mon, 20 Dec 2021 11:14:30 +0000 Subject: [PATCH 42/60] GitBook: [#129] No subject --- docs/gitbook/.gitbook/assets/avalanche (3).png | Bin 0 -> 112023 bytes .../.gitbook/assets/avalanche_api (1).png | Bin 0 -> 51341 bytes .../learn-avalanche-in-5-minutes.md | 10 +++++----- docs/gitbook/getting-started/why-avalanche.md | 16 ++++++++-------- 4 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 docs/gitbook/.gitbook/assets/avalanche (3).png create mode 100644 docs/gitbook/.gitbook/assets/avalanche_api (1).png diff --git a/docs/gitbook/.gitbook/assets/avalanche (3).png b/docs/gitbook/.gitbook/assets/avalanche (3).png new file mode 100644 index 0000000000000000000000000000000000000000..141b3481b699611de01db23742f88dc082f6d237 GIT binary patch literal 112023 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

BI54O~zRoBjaxEt(6hB^gH6Z0Z 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 literal 0 HcmV?d00001 diff --git a/docs/gitbook/.gitbook/assets/avalanche_api (1).png b/docs/gitbook/.gitbook/assets/avalanche_api (1).png new file mode 100644 index 0000000000000000000000000000000000000000..d27967d12d93027dca31ed1bd7d7725169a164c5 GIT binary patch literal 51341 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 X*YG!{!mZy!FMGkjRKNV(mHYn-MwHYA literal 0 HcmV?d00001 diff --git a/docs/gitbook/getting-started/learn-avalanche-in-5-minutes.md b/docs/gitbook/getting-started/learn-avalanche-in-5-minutes.md index cb4a63d2d..09f4ad449 100644 --- a/docs/gitbook/getting-started/learn-avalanche-in-5-minutes.md +++ b/docs/gitbook/getting-started/learn-avalanche-in-5-minutes.md @@ -8,15 +8,15 @@ _Avalanche_ is mostly about making the life of a continual learning researcher e ![Avalanche modules.](<../.gitbook/assets/avalanche (1).png>) -> #### > -> #### What are the **three pillars** of any respectful continual learning research project? +> +> **What are the three pillars of any respectful continual learning research project?** * **`Benchmarks`**: Machine learning researchers need multiple benchmarks with efficient data handling utils to design and prototype new algorithms. Quantitative results on ever-changing benchmarks has been one of the driving forces of _Deep Learning_. * **`Training`**: Efficient implementation and training of continual learning algorithms; comparisons with other baselines and state-of-the-art methods become fundamental to asses the quality of an original algorithmic proposal. * **`Evaluation`**: _Training_ utils and _Benchmarks_ are not enough alone to push continual learning research forward. Comprehensive and sound _evaluation protocols_ and _metrics_ need to be employed as well. -> #### _With Avalanche, you can find all these three fundamental pieces together and much more, in a single and coherent, well-maintained codebase._ +> _**With Avalanche, you can find all these three fundamental pieces together and much more, in a single and coherent, well-maintained codebase.**_ Let's take a quick tour on how you can use Avalanche for your research projects with a **5-minutes guide**, for _researchers on the run_! @@ -31,7 +31,7 @@ _Avalanche_ is organized in **five main modules**: * **`Benchmarks`**: This module maintains a uniform API for data handling: mostly generating a stream of data from one or more datasets. It contains all the major CL benchmarks (similar to what has been done for [torchvision](https://pytorch.org/docs/stable/torchvision/index.html)). * **`Training`**: This module provides all the necessary utilities concerning model training. This includes simple and efficient ways of implement new _continual learning_ strategies as well as a set pre-implemented CL baselines and state-of-the-art algorithms you will be able to use for comparison! * **`Evaluation`**: This modules provides all the utilities and metrics that can help in evaluating a CL algorithm with respect to all the factors we believe to be important for a continually learning system. -* **`Models`**: In this module you'll be able to find several model architectures and pre-trained models that can be used for your continual learning experiment (similar to what has been done in [torchvision.models](https://pytorch.org/docs/stable/torchvision/index.html)). +* **`Models`**: In this module you'll be able to find several model architectures and pre-trained models that can be used for your continual learning experiment (similar to what has been done in [torchvision.models](https://pytorch.org/docs/stable/torchvision/index.html)). * **`Logging`**: It includes advanced logging and plotting features, including native _stdout_, _file_ and [Tensorboard](https://www.tensorflow.org/tensorboard) support (How cool it is to have a complete, interactive dashboard, tracking your experiment metrics in real-time with a single line of code?) In the graphic below, you can see how _Avalanche_ sub-modules are available and organized as well: @@ -257,7 +257,7 @@ Check out more details about what Avalanche can offer in this module following t The `evaluation` module is quite straightforward: it offers all the basic functionalities to evaluate and keep track of a continual learning experiment. -This is mostly done through the **Metrics** and the **Loggers.** The **Metrics** provide a set of classes which implements the main continual learning metrics like A_ccuracy_, F_orgetting_, M_emory Usage_, R_unning Times_, etc.\ +This is mostly done through the **Metrics** and the **Loggers.** The **Metrics** provide a set of classes which implements the main continual learning metrics like A\_ccuracy\_, F\_orgetting\_, M\_emory Usage\_, R\_unning Times\_, etc.\ Metrics should be created via the utility functions (e.g. `accuracy_metrics`, `timing_metrics`and others) specifying in the arguments when those metrics should be computed (after each minibatch, epoch, experience etc...).\ The **Loggers** specify a way to report the metrics (e.g. with Tensorboard, on console or others). Loggers are created by instantiating the respective class. diff --git a/docs/gitbook/getting-started/why-avalanche.md b/docs/gitbook/getting-started/why-avalanche.md index 0caeb31e7..769666da9 100644 --- a/docs/gitbook/getting-started/why-avalanche.md +++ b/docs/gitbook/getting-started/why-avalanche.md @@ -6,25 +6,25 @@ description: A Brief Introduction to Avalanche **Avalanche** was born within [ContinualAI](https://www.continualai.org) with a clear goal in mind: -> ### _Pushing Continual Learning to the next level, providing a shared and collaborative library for fast prototyping, training and reproducible evaluation of continual learning algorithms._ +> #### _Pushing Continual Learning to the next level, providing a shared and collaborative library for fast prototyping, training and reproducible evaluation of continual learning algorithms._ As a powerful _avalanche_, a _Continual Learning_ agent _incrementally_ _improves_ its knowledge and skills over time, building upon the previously acquired ones and learning how to interact with the external world. We hope _Avalanche_ may trigger the same _**positive reinforcement loop**_ within our community, moving towards a more _**collaborative**_ **and inclusive** way of doing research and helping us tackle bigger problems, faster and better, but together! 👪 -{% embed url="https://www.youtube.com/watch?v=EyO1eM0-Hi8" %} -A complete Introduction to Avalanche: an End-to-End Library for Continual Learning. +{% embed url="https://www.youtube.com/watch?v=y924wXP86Mo" %} +Avalanche 5 minutes introduction {% endembed %} ## 💪The Avalanche Advantage Avalanche has several advantages: -* **Shared & Coherent Codebase**: Aren't you tired of re-inventing the wheel in continual learning? We are. Re-producing paper results has always been daunting in machine learning and it is even more so in continual learning. _Avalanche_ makes you stop re-write your (and other people) code all over again with a coherent and shared codebase that provides already all the utilities, benchmark, metrics and baselines you may need for your next great continual learning research project! -* **Errors Reduction**: The more code we write, the more bugs we introduce in our code. This is the rule, not the exception. _Avalanche_, let you focus on what really matters: defining your CL solution. _Benchmarks_ preparation to _training,_ _evaluation_ and _comparison_ with other methods will be already there for you. This in turn, massively reduce the amount of errors introduced and the time needed to debug your code. -* **Faster Prototyping**: As researchers or data scientists, we have dozens ideas every day and time is always too little to execute them. However, if we think about it, most of the time spent in bringing our ideas to life is consumed in installing software, preparing and cleaning our data, preparing the experiments code infrastructure and so on. _Avalanche_ lets you focus just on the original algorithmic proposal, taking care of most of the rest! -* **Improved Reproducibility & Portability**: One of the great features of _Avalanche_, is the possibility of reproducing experimental results easily and on any OS. Researchers can simply plug-in their algorithm into the codebase and see how it goes with respect of other researchers' methods. Their algorithm in turn, is used as a baseline for other methods, creating a virtuous circle. This is only possible thanks to the simple, yet powerful idea of providing shared _benchmarks_, _training_ and _evaluation_ in a single place. -* **Improved Modularity**: _Avalanche_ has been designed with modularity in mind. As you will learn more about Avalanche, you will realize we have sometimes forego simplicity in favor of modularity and reusability (we hate code replication as you do 🤪). However, we believe this will help us scale in the near future as we collaboratively bring this codebase into maturity. +* **Shared & Coherent Codebase**: Aren't you tired of re-inventing the wheel in continual learning? We are. Re-producing paper results has always been daunting in machine learning and it is even more so in continual learning. _Avalanche_ makes you stop re-write your (and other people) code all over again with a coherent and shared codebase that provides already all the utilities, benchmark, metrics and baselines you may need for your next great continual learning research project! +* **Errors Reduction**: The more code we write, the more bugs we introduce in our code. This is the rule, not the exception. _Avalanche_, let you focus on what really matters: defining your CL solution. _Benchmarks_ preparation to _training,_ _evaluation_ and _comparison_ with other methods will be already there for you. This in turn, massively reduce the amount of errors introduced and the time needed to debug your code. +* **Faster Prototyping**: As researchers or data scientists, we have dozens ideas every day and time is always too little to execute them. However, if we think about it, most of the time spent in bringing our ideas to life is consumed in installing software, preparing and cleaning our data, preparing the experiments code infrastructure and so on. _Avalanche_ lets you focus just on the original algorithmic proposal, taking care of most of the rest! +* **Improved Reproducibility & Portability**: One of the great features of _Avalanche_, is the possibility of reproducing experimental results easily and on any OS. Researchers can simply plug-in their algorithm into the codebase and see how it goes with respect of other researchers' methods. Their algorithm in turn, is used as a baseline for other methods, creating a virtuous circle. This is only possible thanks to the simple, yet powerful idea of providing shared _benchmarks_, _training_ and _evaluation_ in a single place. +* **Improved Modularity**: _Avalanche_ has been designed with modularity in mind. As you will learn more about Avalanche, you will realize we have sometimes forego simplicity in favor of modularity and reusability (we hate code replication as you do 🤪). However, we believe this will help us scale in the near future as we collaboratively bring this codebase into maturity. * **Increased Efficiency & Scalability**: Full-stack researchers & data scientists know this, making your algorithm memory and computationally efficient is tough. _Avalanche_ is already optimized for you, so that you can run your ImageNet continual learning experiment on your 8GB laptop (buy a cooling fan 💨) or even try it on embedded devices of your latest product! But most of all, _Avalanche_, can help us standardize our field and work better together, more collaboratively, towards our shared goal of making machines learn over time like humans do. From 0cb205bdf7ba2da11ae54e6353c96d573b08c8fc Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Mon, 20 Dec 2021 11:22:40 +0000 Subject: [PATCH 43/60] GitBook: [#130] No subject --- docs/gitbook/getting-started/how-to-install.md | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/docs/gitbook/getting-started/how-to-install.md b/docs/gitbook/getting-started/how-to-install.md index 0483c2495..ff9114bc0 100644 --- a/docs/gitbook/getting-started/how-to-install.md +++ b/docs/gitbook/getting-started/how-to-install.md @@ -6,17 +6,6 @@ description: Installing Avalanche has Never Been so Simple _Avalanche_ has been designed for extreme **portability** and **usability**. Indeed, it can be run on every OS and native python environment. 💻🍎🐧 -In order to install _Avalanche_ we have three main options: - -1. [Installing it with Pip](how-to-install.md#installing-avalanche-with-pip) -2. [Developer Mode Install](how-to-install.md#developer-mode-install) (for contributing to _Avalanche_!) - -{% hint style="info" %} -_Avalanche may work on lower Python versions as well but we don't officially support it, nor recommend it._ -{% endhint %} - -At the moment, we cannot provide a swift installation experience as some of the dependencies cannot be installed automatically. However, in the sections below we detail how you can install Avalanche **in a matter of minutes** on any platform! - ## 📦 Installing Avalanche with Pip you can install Avalanche with pip: @@ -25,11 +14,11 @@ you can install Avalanche with pip: pip install avalanche-lib ``` -That's it. Now you can start using Avalanche! +That's it. Now you can start using Avalanche. -1. +## Installing the Master Branch Using Anaconda -## Using Anaconda +We suggest you to use the pip package, but if you need some recent features you may want to install directly from the master branch. In general, the master branch is well tested and safe to use. However, the API of new features may change more frequently or break backward compatibility. Reproducibility is also easier if you use the pip package. ```bash # choose your python version From c3e9f33e92a35ec16bebed9d2a5039e861ff4257 Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Mon, 20 Dec 2021 11:27:33 +0000 Subject: [PATCH 44/60] GitBook: [#131] No subject --- .../from-zero-to-hero-tutorial/01_introduction.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/gitbook/from-zero-to-hero-tutorial/01_introduction.md b/docs/gitbook/from-zero-to-hero-tutorial/01_introduction.md index c01f60d90..9e25055a9 100644 --- a/docs/gitbook/from-zero-to-hero-tutorial/01_introduction.md +++ b/docs/gitbook/from-zero-to-hero-tutorial/01_introduction.md @@ -24,11 +24,15 @@ This module provides a uniform API for data handling: mostly generating a stream This module provides all the necessary utilities concerning model training. This includes simple and efficient ways of implement new _continual learning_ strategies as well as a set pre-implemented CL baselines and state-of-the-art algorithms you will be able to use for comparison! * **BaseStrategy** provides the default training and eval loops. -* **Plugins** extend the basic loops with additional functionality. +* **Plugins** extend the basic loops with additional functionality. -#### **`Evaluation`** +#### **`Evaluation`** -This module provides all the utilities and metrics that can help evaluate a CL algorithm with respect to all the factors we believe to be important for a continually learning system. It also includes advanced logging and plotting features, including native [Tensorboard](https://www.tensorflow.org/tensorboard) support. +This module provides all the utilities and metrics that can help evaluate a CL algorithm with respect to all the factors we believe to be important for a continually learning system. + +* **EvaluationPlugin**: the plugin that connects metrics and the `BaseStrategy.` +* **MetricPlugins**: plugins that provide a bridge between metrics and the evaluation plugin. They emit `MetricValue`s that will be collected by the `EvaluationPlugin` and serialized by the loggers. +* **Metric**: basic logic to compute a metric. Provides the `update`, `result` and `reset` operations used to compute and retrieve the metric value. #### Models From 1ff70f7d787ecf571cf9c671e3e51016c81a5828 Mon Sep 17 00:00:00 2001 From: Lorenzo Pellegrini Date: Tue, 21 Dec 2021 17:30:25 +0100 Subject: [PATCH 45/60] Support for ReduceLROnPlateau. Fixes #858. Refactored scheduler tests. --- avalanche/training/plugins/lr_scheduling.py | 154 +++++- tests/training/test_plugins.py | 488 +++++++++++++++++--- 2 files changed, 559 insertions(+), 83 deletions(-) diff --git a/avalanche/training/plugins/lr_scheduling.py b/avalanche/training/plugins/lr_scheduling.py index cdce9c535..62acc57e9 100644 --- a/avalanche/training/plugins/lr_scheduling.py +++ b/avalanche/training/plugins/lr_scheduling.py @@ -1,4 +1,12 @@ +import warnings +from typing import TYPE_CHECKING + +from avalanche.evaluation.metrics import Mean from avalanche.training.plugins import StrategyPlugin +import inspect + +if TYPE_CHECKING: + from avalanche.training import BaseStrategy class LRSchedulerPlugin(StrategyPlugin): @@ -7,30 +15,73 @@ class LRSchedulerPlugin(StrategyPlugin): This plugin manages learning rate scheduling inside of a strategy using the PyTorch scheduler passed to the constructor. The step() method of the scheduler is called after each training epoch. + + Metric-based schedulers (like ReduceLROnPlateau) are supported as well. """ - def __init__(self, scheduler, reset_scheduler=True, reset_lr=True): + def __init__(self, scheduler, reset_scheduler=True, reset_lr=True, + metric=None): """ Creates a ``LRSchedulerPlugin`` instance. :param scheduler: a learning rate scheduler that can be updated through - a step() method and can be reset by setting last_epoch=0 + a step() method and can be reset by setting last_epoch=0. :param reset_scheduler: If True, the scheduler is reset at the end of - the experience. - Defaults to True. + the experience. Defaults to True. :param reset_lr: If True, the optimizer learning rate is reset to its - original value. - Default to True. + original value. Default to True. + :param metric: the metric to use. Must be set when using + metric-based scheduling (like ReduceLROnPlateau). Only "train_loss" + and "val_loss" are supported at the moment. Beware that, + when using "val_loss", the periodic evaluation flow must be enabled + in the strategy. By default, the `eval_every` parameter of the + base strategy is -1, which means that the validation set is never + evaluated. Set that value to 1 to obtain the correct results. + Also, when using `metric="val_loss"`, remember to pass a proper + validation stream to the strategy train method, otherwise the + periodic evaluation stream will use the training set to compute + the validation loss. """ + super().__init__() self.scheduler = scheduler self.reset_scheduler = reset_scheduler self.reset_lr = reset_lr + self.metric = metric + self.rolling_metric = Mean() + + # Used to detect and manage the periodic eval phase + self._was_training = False + self._eval_train_epoch = 0 + + arg_names = inspect.getfullargspec(self.scheduler.step)[0] + needs_metrics = 'metrics' in arg_names + + if needs_metrics and self.metric is None: + raise ValueError( + 'The step method of this scheduler requires a metric ' + '(usually the loss) to be passed. Please set a proper ' + 'metric parameter when creating this plugin.') + elif (not needs_metrics) and self.metric is not None: + warnings.warn('You are passing a metric value but the scheduler' + "doesn't seem to support metrics...") - def after_training_epoch(self, strategy, **kwargs): - self.scheduler.step() + if self.metric not in [None, 'train_loss', 'val_loss']: + raise ValueError( + 'Only scheduling based on "train_loss" and '"val_loss" '' + f'is supported at the moment (got {metric}.') - def after_training_exp(self, strategy, **kwargs): + LRSchedulerPlugin._patch_lr_on_plateau(self.scheduler) + + def after_training_epoch(self, strategy: 'BaseStrategy', **kwargs): + if self.metric == 'train_loss': + self.scheduler.step(metrics=self.rolling_metric.result()) + self.rolling_metric.reset() + elif self.metric != 'val_loss': + self.scheduler.step() + self.rolling_metric.reset() + + def after_training_exp(self, strategy: 'BaseStrategy', **kwargs): param_groups = strategy.optimizer.param_groups base_lrs = self.scheduler.base_lrs @@ -40,3 +91,88 @@ def after_training_exp(self, strategy, **kwargs): if self.reset_scheduler: self.scheduler.last_epoch = 0 + + # Manage the reset of the scheduler + # Mainly used to call _reset on ReduceLROnPlateau, but may come + # in handy for other schedulers in the future + reset_method = getattr(self.scheduler, 'reset', None) + if not callable(reset_method): + reset_method = getattr(self.scheduler, '_reset', None) + + if callable(reset_method): + # print('Calling reset method of scheduler') + reset_method() + + # Methods used to manage ReduceLROnPlateau (keep track of the periodic eval) + def before_training(self, strategy: 'BaseStrategy', **kwargs): + self._was_training = True + + def after_training(self, strategy: 'BaseStrategy', **kwargs): + self._was_training = False + + def after_eval(self, strategy: 'BaseStrategy', **kwargs): + + if self.metric == 'val_loss' and self._was_training: + + if strategy.clock.train_exp_epochs == 0: + # The base strategy may run an evaluation pass on the + # validation set before running the training loop. In that + # case, we should just discard the result. + # print('Ignoring pre-training validation') + pass + elif self._eval_train_epoch == strategy.clock.train_exp_epochs: + # The base strategy may run an evaluation pass on the + # validation set after the training loop. In that + # case, we should discard the result only if the validation pass + # has been duplicated. + + # In fact, the previous branch of the "if" could be omitted + # because this one can cover both the pre-training and + # duplicate post-training cases... + # print('Ignoring post-training duplicate validation ' + # f'{self._eval_train_epoch}') + pass + else: + # print('Stepping after validation', + # self.rolling_metric.result()) + self.scheduler.step(metrics=self.rolling_metric.result()) + self.rolling_metric.reset() + self._eval_train_epoch = strategy.clock.train_exp_epochs + + def after_training_iteration(self, strategy: 'BaseStrategy', **kwargs): + if self.metric != 'train_loss': + return + self.rolling_metric.update(strategy.loss, weight=len(strategy.mb_x)) + + def after_eval_iteration(self, strategy: 'BaseStrategy', **kwargs): + if self.metric != 'val_loss': + return + + # Check if switched to eval mid-training + # This only happens when running periodic validation + if self._was_training: + self.rolling_metric.update(strategy.loss, + weight=len(strategy.mb_x)) + + @staticmethod + def _patch_lr_on_plateau(scheduler): + # All PyTorch schedulers have the base_lrs field (needed to reset the + # initial LRs before each experience) with the only exception being + # ReduceLROnPlateau. This method will add that field to + # ReduceLROnPlateau. + + if hasattr(scheduler, 'base_lrs'): + return + + # Initialize epoch and base learning rates + for group in scheduler.optimizer.param_groups: + group.setdefault('initial_lr', group['lr']) + + scheduler.base_lrs = list( + map(lambda group_param: group_param['initial_lr'], + scheduler.optimizer.param_groups)) + + +__all__ = [ + 'LRSchedulerPlugin' +] diff --git a/tests/training/test_plugins.py b/tests/training/test_plugins.py index 9877ec00f..1fa974369 100644 --- a/tests/training/test_plugins.py +++ b/tests/training/test_plugins.py @@ -1,18 +1,24 @@ +import itertools import sys import torch +from torch import nn import unittest from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from torch.nn import CrossEntropyLoss from torch.optim import SGD -from torch.optim.lr_scheduler import MultiStepLR +from torch.optim.lr_scheduler import MultiStepLR, ReduceLROnPlateau from torch.utils.data import TensorDataset +from torch.utils.data.dataloader import DataLoader -from avalanche.benchmarks import nc_benchmark +from avalanche.benchmarks import nc_benchmark, GenericCLScenario, \ + benchmark_with_validation_stream +from avalanche.benchmarks.utils.data_loader import TaskBalancedDataLoader +from avalanche.evaluation.metrics import Mean from avalanche.logging import TextLogger -from avalanche.models import SimpleMLP +from avalanche.models import BaseModel from avalanche.training.plugins import StrategyPlugin from avalanche.training.plugins.lr_scheduling import LRSchedulerPlugin from avalanche.training.strategies import Naive @@ -95,10 +101,10 @@ class PluginTests(unittest.TestCase): def test_callback_reachability(self): # Check that all the callbacks are called during # training and test loops. - model = SimpleMLP(input_size=6, hidden_size=10) + model = _PlainMLP(input_size=6, hidden_size=10) optimizer = SGD(model.parameters(), lr=1e-3) criterion = CrossEntropyLoss() - benchmark = self.create_benchmark() + benchmark = PluginTests.create_benchmark() plug = MockPlugin() strategy = Naive(model, optimizer, criterion, @@ -110,19 +116,21 @@ def test_callback_reachability(self): strategy.eval([benchmark.test_stream[0]], num_workers=4) assert all(plug.activated) - def create_benchmark(self, task_labels=False): + @staticmethod + def create_benchmark(task_labels=False, seed=None): n_samples_per_class = 20 dataset = make_classification( n_samples=10 * n_samples_per_class, n_classes=10, - n_features=6, n_informative=6, n_redundant=0) + n_features=6, n_informative=6, n_redundant=0, + random_state=seed) X = torch.from_numpy(dataset[0]).float() y = torch.from_numpy(dataset[1]).long() train_X, test_X, train_y, test_y = train_test_split( - X, y, train_size=0.6, shuffle=True, stratify=y) + X, y, train_size=0.6, shuffle=True, stratify=y, random_state=seed) train_dataset = TensorDataset(train_X, train_y) test_dataset = TensorDataset(test_X, test_y) @@ -131,75 +139,407 @@ def create_benchmark(self, task_labels=False): fixed_class_order=list(range(10))) def test_scheduler_plugin(self): - self._test_scheduler_plugin(gamma=1 / 2., - milestones=[2, 3], - base_lr=4., - epochs=3, - reset_lr=True, - reset_scheduler=True, - expected=[[4., 2., 1.], - [4., 2., 1.]], - ) - - self._test_scheduler_plugin(gamma=1 / 2., - milestones=[2, 3], - base_lr=4., - epochs=3, - reset_lr=False, - reset_scheduler=True, - expected=[[4., 2., 1.], - [1., .5, .25]], - ) - - self._test_scheduler_plugin(gamma=1 / 2., - milestones=[2, 3], - base_lr=4., - epochs=3, - reset_lr=True, - reset_scheduler=False, - expected=[[4., 2., 1.], - [4., 4., 4.]], - ) - - self._test_scheduler_plugin(gamma=1 / 2., - milestones=[2, 3], - base_lr=4., - epochs=3, - reset_lr=False, - reset_scheduler=False, - expected=[[4., 2., 1.], - [1., 1., 1.]], - ) - - def _test_scheduler_plugin(self, gamma, milestones, base_lr, epochs, - reset_lr, reset_scheduler, expected): - - class TestPlugin(StrategyPlugin): - def __init__(self, expected_lrs): - super().__init__() - self.expected_lrs = expected_lrs - - def after_training_epoch(self, strategy, **kwargs): - exp_id = strategy.clock.train_exp_counter - curr_epoch = strategy.clock.train_exp_epochs - expected_lr = self.expected_lrs[exp_id][curr_epoch] - for group in strategy.optimizer.param_groups: - assert group['lr'] == expected_lr - - benchmark = self.create_benchmark() - model = SimpleMLP(input_size=6, hidden_size=10) - + PluginTests._test_scheduler_multi_step_lr_plugin( + gamma=1 / 2., + milestones=[2, 3], + base_lr=4., + epochs=3, + reset_lr=True, + reset_scheduler=True, + expected=[[4., 2., 1.], [4., 2., 1.]]) + + PluginTests._test_scheduler_multi_step_lr_plugin( + gamma=1 / 2., + milestones=[2, 3], + base_lr=4., + epochs=3, + reset_lr=False, + reset_scheduler=True, + expected=[[4., 2., 1.], [1., .5, .25]]) + + PluginTests._test_scheduler_multi_step_lr_plugin( + gamma=1 / 2., + milestones=[2, 3], + base_lr=4., + epochs=3, + reset_lr=True, + reset_scheduler=False, + expected=[[4., 2., 1.], [4., 4., 4.]]) + + PluginTests._test_scheduler_multi_step_lr_plugin( + gamma=1 / 2., + milestones=[2, 3], + base_lr=4., + epochs=3, + reset_lr=False, + reset_scheduler=False, + expected=[[4., 2., 1.], [1., 1., 1.]]) + + @staticmethod + def _test_scheduler_multi_step_lr_plugin( + gamma, milestones, base_lr, epochs, + reset_lr, reset_scheduler, expected): + + benchmark = PluginTests.create_benchmark() + model = _PlainMLP(input_size=6, hidden_size=10) optim = SGD(model.parameters(), lr=base_lr) - lrSchedulerPlugin = LRSchedulerPlugin( - MultiStepLR(optim, milestones=milestones, gamma=gamma), - reset_lr=reset_lr, reset_scheduler=reset_scheduler) + scheduler = MultiStepLR(optim, milestones=milestones, gamma=gamma) + + PluginTests._test_scheduler_plugin( + benchmark, model, optim, scheduler, + epochs, reset_lr, reset_scheduler, expected) + + def assert_model_equals(self, model1, model2): + dict1 = model1.state_dict() + dict2 = model2.state_dict() + + # compare keys + self.assertSetEqual(set(dict1.keys()), set(dict2.keys())) + + # compare params + for (k, v) in dict1.items(): + self.assertTrue(torch.equal(v, dict2[k])) + + def assert_benchmark_equals( + self, + bench1: GenericCLScenario, + bench2: GenericCLScenario): + self.assertSetEqual(set(bench1.streams.keys()), + set(bench2.streams.keys())) + + for stream_name in list(bench1.streams.keys()): + for exp1, exp2 in zip(bench1.streams[stream_name], + bench2.streams[stream_name]): + dataset1 = exp1.dataset + dataset2 = exp2.dataset + for t_idx in range(3): + dataset1_content = dataset1[:][t_idx] + dataset2_content = dataset2[:][t_idx] + self.assertTrue(torch.equal(dataset1_content, + dataset2_content)) + + def _verify_rop_tests_reproducibility( + self, init_strategy, n_epochs, criterion): + # This doesn't actually test the support for the specific scheduler + # (ReduceLROnPlateau), but it's only used to check if: + # - the same model+benchmark pair can be instantiated in a + # deterministic way. + # - the same results could be obtained in a standard training loop in a + # deterministic way. + models_rnd = [] + benchmarks_rnd = [] + for _ in range(2): + benchmark, model = init_strategy() + models_rnd.append(model) + benchmarks_rnd.append(benchmark) + + self.assert_model_equals(*models_rnd) + self.assert_benchmark_equals(*benchmarks_rnd) + + expected_lrs_rnd = [] + for _ in range(2): + benchmark, model = init_strategy() + + expected_lrs = [] + model.train() + for exp in benchmark.train_stream: + optimizer = SGD(model.parameters(), lr=0.001) + scheduler = ReduceLROnPlateau(optimizer) + expected_lrs.append([]) + train_loss = Mean() + for epoch in range(n_epochs): + train_loss.reset() + for x, y, t in TaskBalancedDataLoader( + exp.dataset, + oversample_small_groups=True, + num_workers=0, + batch_size=32, + shuffle=False, + pin_memory=False): + optimizer.zero_grad() + outputs = model(x) + loss = criterion(outputs, y) + train_loss.update(loss, weight=len(x)) + loss.backward() + optimizer.step() + scheduler.step(train_loss.result()) + for group in optimizer.param_groups: + expected_lrs[-1].append(group['lr']) + break + expected_lrs_rnd.append(expected_lrs) + self.assertEqual(expected_lrs_rnd[0], expected_lrs_rnd[1]) + + def test_scheduler_reduce_on_plateau_plugin(self): + # Regression test for issue #858 + n_epochs = 20 + criterion = CrossEntropyLoss() + + def _prepare_rng_critical_parts(seed=1234): + torch.random.manual_seed(seed) + return (PluginTests.create_benchmark(seed=seed), + _PlainMLP(input_size=6, hidden_size=10)) + + self._verify_rop_tests_reproducibility( + _prepare_rng_critical_parts, + n_epochs, + criterion) + + # Everything is in order, now we can test the plugin support for the + # ReduceLROnPlateau scheduler! + + for reset_lr, reset_scheduler in itertools.product( + (True, False), (True, False)): + with self.subTest(reset_lr=reset_lr, + reset_scheduler=reset_scheduler): + # First, obtain the reference (expected) lr timeline by running + # a plain PyTorch training loop with ReduceLROnPlateau. + benchmark, model = _prepare_rng_critical_parts() + model.train() + expected_lrs = [] + + optimizer = SGD(model.parameters(), lr=0.001) + scheduler = ReduceLROnPlateau(optimizer) + for exp in benchmark.train_stream: + if reset_lr: + for group in optimizer.param_groups: + group['lr'] = 0.001 + + if reset_scheduler: + scheduler = ReduceLROnPlateau(optimizer) + + expected_lrs.append([]) + train_loss = Mean() + for epoch in range(n_epochs): + train_loss.reset() + for x, y, t in TaskBalancedDataLoader( + exp.dataset, + oversample_small_groups=True, + num_workers=0, + batch_size=32, + shuffle=False, + pin_memory=False): + optimizer.zero_grad() + outputs = model(x) + loss = criterion(outputs, y) + train_loss.update(loss, weight=len(x)) + loss.backward() + optimizer.step() + scheduler.step(train_loss.result()) + for group in optimizer.param_groups: + expected_lrs[-1].append(group['lr']) + break + + # Now we have the correct timeline stored in expected_lrs. + # Let's test the plugin! + benchmark, model = _prepare_rng_critical_parts() + optimizer = SGD(model.parameters(), lr=0.001) + scheduler = ReduceLROnPlateau(optimizer) + + PluginTests._test_scheduler_plugin( + benchmark, model, optimizer, scheduler, + n_epochs, reset_lr, reset_scheduler, expected_lrs, + criterion=criterion, + metric='train_loss') + + # Other tests + benchmark, model = _prepare_rng_critical_parts() + optimizer = SGD(model.parameters(), lr=0.001) + scheduler = ReduceLROnPlateau(optimizer) + scheduler2 = MultiStepLR(optimizer, [1, 2, 3]) + + # The metric must be set + with self.assertRaises(Exception): + LRSchedulerPlugin( + scheduler, + metric=None) + + # Doesn't make sense to set the metric when using a non-metric + # based scheduler (should warn) + with self.assertWarns(Warning): + LRSchedulerPlugin( + scheduler2, + metric='train_loss') + + # Must raise an error on unsupported metric + with self.assertRaises(Exception): + LRSchedulerPlugin( + scheduler, + metric='cuteness') + + def test_scheduler_reduce_on_plateau_plugin_with_val_stream(self): + # Regression test for issue #858 (part 2) + n_epochs = 20 + criterion = CrossEntropyLoss() + + def _prepare_rng_critical_parts(seed=1234): + torch.random.manual_seed(seed) + initial_benchmark = PluginTests.create_benchmark(seed=seed) + val_benchmark = benchmark_with_validation_stream( + initial_benchmark, 0.3, shuffle=True) + return (val_benchmark, + _PlainMLP(input_size=6, hidden_size=10)) + + self._verify_rop_tests_reproducibility( + _prepare_rng_critical_parts, + n_epochs, + criterion) + + # Everything is in order, now we can test the plugin support for the + # ReduceLROnPlateau scheduler! + for reset_lr, reset_scheduler in itertools.product( + (True, False), (True, False)): + with self.subTest(reset_lr=reset_lr, + reset_scheduler=reset_scheduler): + # First, obtain the reference (expected) lr timeline by running + # a plain PyTorch training loop with ReduceLROnPlateau. + benchmark, model = _prepare_rng_critical_parts() + + expected_lrs = [] + + optimizer = SGD(model.parameters(), lr=0.001) + scheduler = ReduceLROnPlateau(optimizer) + for exp_idx, exp in enumerate(benchmark.train_stream): + expected_lrs.append([]) + model.train() + if reset_lr: + for group in optimizer.param_groups: + group['lr'] = 0.001 + + if reset_scheduler: + scheduler = ReduceLROnPlateau(optimizer) + + for epoch in range(n_epochs): + for x, y, t in TaskBalancedDataLoader( + exp.dataset, + oversample_small_groups=True, + num_workers=0, + batch_size=32, + shuffle=False, + pin_memory=False): + optimizer.zero_grad() + outputs = model(x) + loss = criterion(outputs, y) + loss.backward() + optimizer.step() + for group in optimizer.param_groups: + expected_lrs[-1].append(group['lr']) + break + + val_loss = Mean() + val_exp = benchmark.valid_stream[exp_idx] + + model.eval() + with torch.no_grad(): + for x, y, t in DataLoader( + val_exp.dataset, + num_workers=0, + batch_size=100, + pin_memory=False): + outputs = model(x) + loss = criterion(outputs, y) + val_loss.update(loss, weight=len(x)) + + scheduler.step(val_loss.result()) + + # Now we have the correct timeline stored in expected_lrs + # Let's test the plugin! + benchmark, model = _prepare_rng_critical_parts() + optimizer = SGD(model.parameters(), lr=0.001) + scheduler = ReduceLROnPlateau(optimizer) + + PluginTests._test_scheduler_plugin( + benchmark, model, optimizer, scheduler, + n_epochs, reset_lr, reset_scheduler, expected_lrs, + criterion=criterion, + metric='val_loss', + eval_on_valid_stream=True) + + @staticmethod + def _test_scheduler_plugin( + benchmark, model, optim, scheduler, epochs, + reset_lr, reset_scheduler, expected, criterion=None, + metric=None, eval_on_valid_stream=False): + lr_scheduler_plugin = LRSchedulerPlugin( + scheduler, + reset_lr=reset_lr, + reset_scheduler=reset_scheduler, + metric=metric) + + verifier_plugin = SchedulerPluginTestPlugin(expected) + + if criterion is None: + criterion = CrossEntropyLoss() + if eval_on_valid_stream: + cl_strategy = Naive( + model, optim, criterion, train_mb_size=32, + train_epochs=epochs, eval_mb_size=100, + plugins=[lr_scheduler_plugin, verifier_plugin], + eval_every=1, evaluator=None) + + cl_strategy.train(benchmark.train_stream[0], shuffle=False, + eval_streams=[benchmark.valid_stream[0]]) + cl_strategy.train(benchmark.train_stream[1], shuffle=False, + eval_streams=[benchmark.valid_stream[1]]) + else: + cl_strategy = Naive( + model, optim, criterion, train_mb_size=32, + train_epochs=epochs, eval_mb_size=100, + plugins=[lr_scheduler_plugin, verifier_plugin], + evaluator=None) + + cl_strategy.train(benchmark.train_stream[0], shuffle=False) + cl_strategy.train(benchmark.train_stream[1], shuffle=False) + + +class SchedulerPluginTestPlugin(StrategyPlugin): + def __init__(self, expected_lrs): + super().__init__() + self.expected_lrs = expected_lrs - cl_strategy = Naive(model, optim, CrossEntropyLoss(), train_mb_size=32, - train_epochs=epochs, eval_mb_size=100, - plugins=[lrSchedulerPlugin, TestPlugin(expected)]) + def after_training_epoch(self, strategy, **kwargs): + exp_id = strategy.clock.train_exp_counter + curr_epoch = strategy.clock.train_exp_epochs + expected_lr = self.expected_lrs[exp_id][curr_epoch] + for group in strategy.optimizer.param_groups: + assert group['lr'] == expected_lr,\ + f"LR mismatch: {group['lr']} vs {expected_lr}" + + +class _PlainMLP(nn.Module, BaseModel): + """ + An internal MLP implementation without Dropout. + + Needed to reproduce tests for the ReduceLROnPlateau scheduler + """ + def __init__(self, num_classes=10, input_size=28 * 28, + hidden_size=512, hidden_layers=1): + + super().__init__() - cl_strategy.train(benchmark.train_stream[0]) - cl_strategy.train(benchmark.train_stream[1]) + layers = nn.Sequential(*(nn.Linear(input_size, hidden_size), + nn.ReLU(inplace=True))) + for layer_idx in range(hidden_layers - 1): + layers.add_module( + f"fc{layer_idx + 1}", nn.Sequential( + *(nn.Linear(hidden_size, hidden_size), + nn.ReLU(inplace=True)))) + + self.features = nn.Sequential(*layers) + self.classifier = nn.Linear(hidden_size, num_classes) + self._input_size = input_size + + def forward(self, x): + x = x.contiguous() + x = x.view(x.size(0), self._input_size) + x = self.features(x) + x = self.classifier(x) + return x + + def get_features(self, x): + x = x.contiguous() + x = x.view(x.size(0), self._input_size) + x = self.features(x) + return x if __name__ == '__main__': From 9ceb11d649844e46efe6edd55f81c06b70a4020b Mon Sep 17 00:00:00 2001 From: AndreaCossu Date: Tue, 21 Dec 2021 16:45:31 +0000 Subject: [PATCH 46/60] Updated metrics files --- tests/target_metrics/mt.pickle | Bin 24845 -> 27515 bytes tests/target_metrics/sit.pickle | Bin 27911 -> 31953 bytes tests/target_metrics/tpp.pickle | Bin 40181 -> 42299 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/target_metrics/mt.pickle b/tests/target_metrics/mt.pickle index 08cfc7c72cef5ca9ce05a43f22bea28b5c48989f..16f33b39c0434ce7eeca64b37ad2a05e00c483a2 100644 GIT binary patch literal 27515 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%0Dew^=~d*;r$XU;wM zoO|a^yxDrw02w|OXdPBK`$k3gRd@vjDTd97c2dm>h)_g_2B=j|8kJh3P-|jU0W+O^ z1JvPix!eg@;H34}I`A^yg15zguksG~&xIe%4^}}L$NVs&>pJ+yF}Gg)Pw0XmZ;mOm zy;hLwWL_7z@lD3)X%=dZ$Rv~10hm30-#s|>-o@HQswLVLj-#T~>c$kmN_C_Fq~Vy7 z+41?d*9tl2Liys|NX9xqN%YEO#LPB7z3DwC^Zs=Klk2W`wp6aVw zx&AO8KLOvcnT;yG^^C3K?9me_beAQ?BXDJx-%+Gy=BHtPxrFjn5Q{<18oRGC$ZxZ- zKL(flT`tQIbDoRoJVLSTlZ+`jW%-*hxOC8DApj)4r%%9~@m@aSLr}$rsUm|^jno_1 zT`RwU8-a!FNjGDEECxk?np697)C7s1O}hA1 zKNPE7TwFM|+3H0RXU=@fv8|&HO|2U+zjoex1{E=vnhh#NSJeT^RY@r!At4;ws^iPA zI;4$WQw+QAfrAi$%- z^{)xE^&S*>Q!tf+&js*-B?YbspjH^;10@R&4$ACDxB>E1w)+Lp6?texk;e(ZbFu(N zl?cFpm;l1I31HS}0leoVfDfAsVB2*7s14G5pj7GWb%4Nt?rMY&bS6H~xqcs*m4l8; zwOv*NDNMd6J~-yaVRRf30cwIoA1Gb?$FUpt?Af!IW6eTTzhLm2TY%Rc4-si`yT?^6f&pXZI$BrE<0{jG|`#{M}VD*12i+a$@418d- zRtaf#Md11tGc#6JegojEc9EXvAA%Fh-CD6L{}6zOEypdGb{v5<&JRC60ifJ-+Z??d z0A~`mD)&nO9n9nx*4zRRxqI}u_r?R*$>ylu7=oXGG#@Bgd?xpm!9(Mb?8)@S!(9F*>UUg}H}b8A zxaji8;V7U&;y1_#N^VS@K9G7hZ5qgcJfQZp03^A>BLedW&J;Ar6-sUc_`4R=VYTm$ zBnC?N76Nd-A%MY|0vOXq0E$QfgyRtmu9`Djpe)T7z((p@Q?)=r4@88CJ&XoYqM`!IuOtBFG9%_b0MTW$w!=fU60yME< za}>|C`TC?^VX29V4GMKs#A(7J)Y=s`ida=(Tv$Y~LZgaQN5v{;Ict+(PSuLAso@0E zdfFA%iioIyU`0r5)J#R-T$oq|t|1*wWwLnFWfl=tn}`2yKjZVUc8>9;J~x`d{|jPd z4u004r|8%yO;k`+gkqK|7BwQq1Q`6Rpd=zpqlr){RFT190g*AL@B3K*Q8PDMrBH{> zQ>kN`Er>C5@G}RZIy5R)6JySs`Za@JVUe1c=DeAo8LA^Ha(YY)-YhN-?&;u%#(P2J z7+DZ*f`xZfTwsK10%?dvjEP@sVCwJMPwwI3=Gx!QxxcGx45>IMIxeOe-*vPm#%vr^ z=N}`hi`OK;u`ASo4vU;mqX$_WV#qI*96n3=p^q?7A6iND+6V+)h^YGUcQw2zrn>lD3z&-_BHpVFxu z^i1fx8FKwvu|D|+Re{+~DdKO+-(%|P6O|4}1$noL^}Bx;*6iPIdu#Dix;9ZeUJJ8< z&H3g_wD7;l67A9zt-u%}R6!cB1+c$XV`76J}^yk`A`e-}fqt65(2H!;iO%*KzN2m;#Q$F=)K9vH>PLAEOsH%eQfdZs ztv$bR-_INBRVnod)bwgTqME-om)TS+%@Kz7_?qQ4e;dnBJrb#3SFilUbPhA5-TUh0 z|0kG`TVFS480aK=A;SCIgr1uajg#D*<$YaU7~|BqaCn zkmiL1UIP=Nu}bw2dU>tTnGQIFFI@=0`$r69a#t4@XQ?JccnwU5s+OLqifd+Z zw7lk-#uidzoym8Xzme{DLUQayO`AOGlTUrrR4&4&l^pJqSn41{O_0_@&;JM$>g(dt z*F(ZUhCG(Q``m=6g~*m z(rH4HQMl`me{B!`J0}qWgXub4I=aUFOovsWQ?h`H!RJ49NpG51UlMhrr zS}#3UuXnyMU>*{tHFwv#58*!`(#d@{3G+<5n18}RCU=&*xl0_#D6fGDQPn@~OjQb# zMlf@fAwJ@#@l%tOI&VZx168Q6_{I_Uy?*-jGSYf5}Q@I$eoOr|XV&0J;*HPFEr+Kmzv` zS5RjM#0JAF1d-|xRqUS@KApah{49`Rv29RJOe-$EApi;QD89t@%=J@YpGf_y*X+*m zp>BP>&%$eFf3W`f9^2r(wf6>e*MBz`fb;nqe!f;vv};yHoJk7+zu9-G3PSJY{uy0%{6 zneBo>MS0-6_V}uE6D^X-yHb4(wgE@i{Do3Exx(rGsH=Q zUj3mPq%c-j+aV>qL14n0^38ZN-W=9#TktLU7htngE8dc~;#>1C^45GC-iB{Wwn}B= z^}3B5bC?2=l1nMY@P<7c@V=xB9~@IoK`}0YB9T&Zb(P1}Rs9VpW$+e1eve;d7AdvC z4SG|tNUXUmCaWX-Lp{n3D`k9KWF))`f;W#vM+Jp8rFy<5fi%Ztk|`LDfi}Sb+Y`PVk>K)6TJ9g{@ zV0Zt0OnGqk9lMV{g6{TUV6hxi?my~%>tJ3a$K3C@x&%}1WnW*s%HwB_xo26mV1_w7 zY*97WRbFE;PsuQ|eqe}DG_;2wrd;bbV+mLi$6P)BgE=lLDtv_AU*eb|ePVCSQ|Rpe z1}-YNG~B60MmbVVZ*gpG3VUI*+`1wAhF8V6??+t-z-Z)HHUgM8831gB$v&Avc=W9h z1&Z_s1xhyFx&f~+XIod0%k_8F1Z0-!pixkiHERX#h-?-b>o6r@6rt!{1(4OXnA{@k zB$S|yM_FeP0R+oBlPlbGv>gK2zXSkEe*bJN1ZgrQ?AoRXFlQFP#%VGfUzGD4u z@*DtjB6@4ubVbUrOS~dOFUN28+29o12f&V7@4A^K0!Vw@?(>OJ05(PDG%tcT^7Y%b zi<;m3jbk$lz{~m*;+p|6A$S(qDKSLF#e90Zmib3$y;mOndYUnPAt$ zIt-NiiV2u{pX}pd-dbQEkBg>!=1zdpa}W^VsyQpK+G7wf>L{T^d#1%>%KNwcbQsK; zcFP`vu>FZF2J5)D)?g55F`H1r|MfEg>!bM(anUSS*JKRV9xha35Kz7`9)O{LAv+bC z##WdOvQ4D(zemBsopW_ql-ZOL8&Z^lteT0WMKeZRw849!r&FHb*m1-w1ybf^89Ah$ zc@)H4Tzvt1iW!umn*I~+I7|R7nx+jV{$&c~nWRrg!yCs;B+N)PPKFU=6PS$QhSg_b zBuk$sv0xKOf_5#mwnWdPDL|((q2@*fNGsjZ_Sh!Shm@TWPvgOsBSyYPcwhJ`010fx zK+7O5>SgTsFV-dw#Rem%Q$t1vS|h8^OA60i30vH=Ld)8Ztz=P)dl~CLXl=}ugO+5= z=1;HBJ_Ck=YI8R1c@0n@yK5b?IkbN$DFFdgsr*S7YCx5S9Ru4jsb~{3?=nosuvnDfy2@iB%V^TA+8^D%t$G^n4=U?XS_*eK2 zd`G?$|0>^^x97X?uko+*UHLcoZhUtv1kyM#OU?cl)w3ic;-!p;_8hZ~=5eBWP+*9o^#ZC4vFX|Q=8Upa!==cQy)*z^`m@v)g71yYaH)XaE=u-5VeI=D6Lh394xV3}f`j43aS zAb|q=!t09)FvaY;mktB-DPi^qFjXrLVhU4b;|TYgk-V-eU(TD*kx`0eR6?N?erlcs>G1A;za>e?~P{=fjhc1gurm z$CW6+VJ$=2eU2%u(tE{YV6n-mA1-B$Q@ZvhzB_ZX#Y0O-;NhDu z$p@fY;Vi_M)T{wY$({xxAYEj?QGxveo4unOGGdPXW{Dj(d^Wo%Uf{9Dt^yK_8>~Rd zN+HbI_P~o1x(=hT?_e{>3lx1`vA|_^0=h^n_JK05Ln3na;-C@W>)1ryW0Wflzx@hwF|=` z1{nDD%Hwtu07MiOb}JeKKwEI)g-=%lIR8l(r|*6QaQXV=m&WV>aCiTs6Eh)8&U&2t z){S=X0QD{BjZ^OQ$NoiPYG%Abn7I~i_VCC!M)~V4{OFj#okHgi^xlh}9b|3oQb>{d z@||Q%N!S%hvX(&Ye_2dX790}5RJ{&U!tS0Vl#rpq9MFt4zVWyy@bG>DLKC+T9!14v zp(yVQoeba9qXN!w_t;}O_80`tMxRZn=3k_TRt-?ig7&34eB+rE1gW?SxU2h9%B10M zVmoIh(4w)Q{5lg){rFIx5%~&|Kyj2gI&B?`YgSV5CO>lro>Ur3DI;|^fd$$>oE8o3 zXqyLBa|}y)1|7bR=4Cl%0AWV9Vq|J&v|gT-jv1CAS&T}}jFtptWW&BiI%*2=CYqN( zH8(0iT9KHV8Lf|}rCn0!*m8_Z&4_&Pp*E75iGh|uTx3LQR=qY>9EuG_P9syZ8a3*F zXcwQ(Vwf&zkCR_*of4udTmU;bBnb< zFZRr@iW!qOleK&Yz6alv@5T4#`|ysu6EElc^3EWEE8mZIA#E8 z5@(h8}r#O4>FL;CuaFEJ_qU@>UhKQtMGHcL;#8H%h@&I_QxO@}Eh zyjK%oK46FvQ%pmPSOl1=J?{`u*+z(UE4wdG#zl7y73nZ2&$|z2U@lqe&0=u-I{M@g zP|Cc1BjA^-Yjv1%dD?@M<}Oag`%vJ0&~_`p(yK3dU;FWMVDvMHCDZQ zH$fg&U_SLek^=6Ybu@((Wy_GHc!AKZK}mW;mDcicNWj6h(AbuFRlN?wnx#HGCe0du zglu4kX2m1JfZ|ymc()7s*6dqW>;^qrO!W@8I`6a)z!-j6XW2;reBp}1$Cdyb-DclW z?E~O*>-(xjc95tK$$T?n&l9RQ7NnUZZ?)#*vjDao-q9}leSRwzR!Yk41&D+vls-V&Vn-(DdUz3V4E+CDXO;a1SntSb(o?kJ_l!D_CN7j zJfSGd37B}v3jw4iI$LqV=5Bgf3HRifFbV=}tXWK%Oewx^w~fb?akR*1=+bp~ zy>$fT8Gb?s?gnQ4yeadLPbMwjS_0o0T-L@T8DBn+f*9p@GqB&9K`FtPlTi2Om;hSj ze|jm&?x#?mNxgI=yZ5Ec<11N>fBWG`VMeC;4bqljI$s|~G8vWbH%KCs*TmZ*k)X!p_D{J%I=w+Uy#WR2 Y27fMZM%v*A2l#8nZ_5TO(Z)Ic4^GT*cmMzZ diff --git a/tests/target_metrics/sit.pickle b/tests/target_metrics/sit.pickle index 66ceac15dbbad2f08c6167c30b4b411765703583..55d57fbabf021c7592630e91e260493e46d08cf6 100644 GIT binary patch literal 31953 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 literal 27911 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 qf`(NLX$w_;iehhAgS}yIiGmdsl?Yf+ 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 z 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 literal 40181 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 From 572bcb511a2dee9f8b3d9dd38ac1f95c2f16851c Mon Sep 17 00:00:00 2001 From: AndreaCossu Date: Tue, 21 Dec 2021 17:45:38 +0000 Subject: [PATCH 47/60] Updated metrics --- tests/target_metrics/mt.pickle | Bin 27515 -> 24628 bytes tests/target_metrics/sit.pickle | Bin 31953 -> 27680 bytes tests/target_metrics/tpp.pickle | Bin 42299 -> 40160 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/target_metrics/mt.pickle b/tests/target_metrics/mt.pickle index 16f33b39c0434ce7eeca64b37ad2a05e00c483a2..162ae207dc3c9a46989cf28ada2ea747d770e848 100644 GIT binary patch literal 24628 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 Tbog%mz8dk9ul`dtF&+O8|I-sf literal 27515 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 literal 31953 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 diff --git a/tests/target_metrics/tpp.pickle b/tests/target_metrics/tpp.pickle index f7e3f8f4672b0d9ed868987063648852d3b0cddb..89df94540e49ecef0733331b89b25c048fb7d72c 100644 GIT binary patch literal 40160 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 literal 42299 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 z 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 From 7cb42ae6626dd421a8ee57d15e5664723409e920 Mon Sep 17 00:00:00 2001 From: Arjun Ashok Date: Wed, 22 Dec 2021 09:29:33 +0530 Subject: [PATCH 48/60] Change tensor to int type --- avalanche/benchmarks/utils/avalanche_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avalanche/benchmarks/utils/avalanche_dataset.py b/avalanche/benchmarks/utils/avalanche_dataset.py index d99fd94db..16c808a30 100644 --- a/avalanche/benchmarks/utils/avalanche_dataset.py +++ b/avalanche/benchmarks/utils/avalanche_dataset.py @@ -2010,7 +2010,7 @@ def concat_datasets_sequentially( test_set = test_dataset_list[dataset_idx] # Get the classes in the dataset - dataset_classes = set([el[1].item() for el in train_set]) + dataset_classes = set([el[1] for el in train_set]) # The class IDs for this dataset will be in range # [n_classes_in_previous_datasets, From 8606ec118439bb98adaa2cd275435617e598c049 Mon Sep 17 00:00:00 2001 From: Lorenzo Pellegrini Date: Fri, 24 Dec 2021 15:06:03 +0100 Subject: [PATCH 49/60] Prevents TensorboardLogger from blocking process exit. Fixes issue #864 --- avalanche/logging/tensorboard_logger.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/avalanche/logging/tensorboard_logger.py b/avalanche/logging/tensorboard_logger.py index 945553d4c..12f58c88f 100644 --- a/avalanche/logging/tensorboard_logger.py +++ b/avalanche/logging/tensorboard_logger.py @@ -21,8 +21,9 @@ from matplotlib.pyplot import Figure from torchvision.transforms.functional import to_tensor from avalanche.evaluation.metric_results import AlternativeValues, \ - MetricValue, TensorImage + TensorImage from avalanche.logging import StrategyLogger +import weakref class TensorboardLogger(StrategyLogger): @@ -61,8 +62,11 @@ def __init__(self, tb_log_dir: Union[str, Path] = "./tb_data", self.writer = SummaryWriter(tb_log_dir, filename_suffix=filename_suffix) - def __del__(self): - self.writer.close() + # Shuts down the writer gracefully on process exit + # or when this logger gets GCed + # Fixes issue #864. + # See: https://docs.python.org/3/library/weakref.html#comparing-finalizers-with-del-methods + weakref.finalize(self, SummaryWriter.close, self.writer) def log_single_metric(self, name, value, x_plot): if isinstance(value, AlternativeValues): From 5db9dd0cc930274c63f453f53d659038e9e91b61 Mon Sep 17 00:00:00 2001 From: Lorenzo Pellegrini Date: Fri, 24 Dec 2021 15:14:03 +0100 Subject: [PATCH 50/60] Fixes PEP8 issue. --- avalanche/logging/tensorboard_logger.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/avalanche/logging/tensorboard_logger.py b/avalanche/logging/tensorboard_logger.py index 12f58c88f..88ecaffbb 100644 --- a/avalanche/logging/tensorboard_logger.py +++ b/avalanche/logging/tensorboard_logger.py @@ -63,9 +63,9 @@ def __init__(self, tb_log_dir: Union[str, Path] = "./tb_data", filename_suffix=filename_suffix) # Shuts down the writer gracefully on process exit - # or when this logger gets GCed - # Fixes issue #864. - # See: https://docs.python.org/3/library/weakref.html#comparing-finalizers-with-del-methods + # or when this logger gets GCed. Fixes issue #864. + # For more info see: + # https://docs.python.org/3/library/weakref.html#comparing-finalizers-with-del-methods weakref.finalize(self, SummaryWriter.close, self.writer) def log_single_metric(self, name, value, x_plot): From c92174c313ef0c462ac3c60bf7531b5259842845 Mon Sep 17 00:00:00 2001 From: Zalak Bhalani <47480255+zalakbhalani@users.noreply.github.com> Date: Fri, 24 Dec 2021 21:49:02 +0530 Subject: [PATCH 51/60] GitBook: fixed a bug in documentation --- docs/gitbook/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/gitbook/README.md b/docs/gitbook/README.md index 84dfb5c04..5a53873e6 100644 --- a/docs/gitbook/README.md +++ b/docs/gitbook/README.md @@ -176,8 +176,8 @@ for task_id, train_dataset in enumerate(list_train_dataset): acc_results = [] for task_id, test_dataset in enumerate(list_test_dataset): - train_data_loader = DataLoader( - train_dataset, num_workers=num_workers, batch_size=train_mb_size) + test_data_loader = DataLoader( + test_dataset, num_workers=num_workers, batch_size=test_mb_size) correct = 0 for iteration, (test_mb_x, test_mb_y) in enumerate(test_data_loader): From 848400847ef088799cb31df369b325f40f9d1b77 Mon Sep 17 00:00:00 2001 From: Arjun Ashok Date: Wed, 29 Dec 2021 10:00:33 +0530 Subject: [PATCH 52/60] Get classes by `dataset.targets` --- avalanche/benchmarks/utils/avalanche_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avalanche/benchmarks/utils/avalanche_dataset.py b/avalanche/benchmarks/utils/avalanche_dataset.py index 16c808a30..51dd9b927 100644 --- a/avalanche/benchmarks/utils/avalanche_dataset.py +++ b/avalanche/benchmarks/utils/avalanche_dataset.py @@ -2010,7 +2010,7 @@ def concat_datasets_sequentially( test_set = test_dataset_list[dataset_idx] # Get the classes in the dataset - dataset_classes = set([el[1] for el in train_set]) + dataset_classes = set(map(int, dataset.targets)) # The class IDs for this dataset will be in range # [n_classes_in_previous_datasets, From 66337bb88c17130f7faee4d76aadd997125c95c1 Mon Sep 17 00:00:00 2001 From: Arjun Ashok Date: Wed, 29 Dec 2021 10:02:56 +0530 Subject: [PATCH 53/60] Correct error in var name --- avalanche/benchmarks/utils/avalanche_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avalanche/benchmarks/utils/avalanche_dataset.py b/avalanche/benchmarks/utils/avalanche_dataset.py index 51dd9b927..f2e9ef2a2 100644 --- a/avalanche/benchmarks/utils/avalanche_dataset.py +++ b/avalanche/benchmarks/utils/avalanche_dataset.py @@ -2010,7 +2010,7 @@ def concat_datasets_sequentially( test_set = test_dataset_list[dataset_idx] # Get the classes in the dataset - dataset_classes = set(map(int, dataset.targets)) + dataset_classes = set(map(int, train_set.targets)) # The class IDs for this dataset will be in range # [n_classes_in_previous_datasets, From 2b62cb8a8f62540c20e455810c9a6e7c7f2cb02e Mon Sep 17 00:00:00 2001 From: Arjun Ashok Date: Fri, 31 Dec 2021 13:15:36 +0530 Subject: [PATCH 54/60] Added tests for `concat_datasets_sequentially` --- tests/test_avalanche_dataset.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test_avalanche_dataset.py b/tests/test_avalanche_dataset.py index 81f4c736e..5b33cf465 100644 --- a/tests/test_avalanche_dataset.py +++ b/tests/test_avalanche_dataset.py @@ -23,7 +23,7 @@ create_generic_benchmark_from_tensor_lists from avalanche.benchmarks.utils import AvalancheDataset, \ AvalancheSubset, AvalancheConcatDataset, AvalancheDatasetType, \ - AvalancheTensorDataset + AvalancheTensorDataset, concat_datasets_sequentially from avalanche.benchmarks.utils.dataset_utils import ConstantSequence from avalanche.training.utils import load_all_dataset import random @@ -1326,6 +1326,33 @@ def test_avalanche_avalanche_subset_concat_stack_overflow(self): leaf[d_sz*dataset_hierarchy_depth:][2])) + def test_avalanche_concat_datasets_sequentially(self): + # create list of training datasets + train = [AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(0, 2, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(2, 4, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(4, 6, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(0, 2, (20,))))] + + # create list of test datasets + test = [AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(0, 2, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(2, 4, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(4, 6, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(0, 2, (20,))))] + + # concatenate datasets + final_train, final_test, classes = concat_datasets_sequentially(train, test) + + # merge all classes into a single list + classes_all = [] + for class_list in classes: + classes_all.extend(class_list) + + # get the target set of classes + target_classes = list(set(map(int, final_train.targets))) + + # test for correctness + self.assertEqual(classes_all, target_classes) + class TransformationSubsetTests(unittest.TestCase): def test_avalanche_subset_transform(self): dataset_mnist = MNIST(root=default_dataset_location('mnist'), From 438f70081c7dcabe51ac040db9e48b9a3e7fe4f4 Mon Sep 17 00:00:00 2001 From: Arjun Ashok Date: Fri, 31 Dec 2021 13:35:13 +0530 Subject: [PATCH 55/60] Fix PEP8 errors --- tests/test_avalanche_dataset.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/tests/test_avalanche_dataset.py b/tests/test_avalanche_dataset.py index 5b33cf465..0ba14a0fd 100644 --- a/tests/test_avalanche_dataset.py +++ b/tests/test_avalanche_dataset.py @@ -1325,22 +1325,29 @@ def test_avalanche_avalanche_subset_concat_stack_overflow(self): self.assertTrue(torch.equal(tensor_t, leaf[d_sz*dataset_hierarchy_depth:][2])) - def test_avalanche_concat_datasets_sequentially(self): # create list of training datasets - train = [AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(0, 2, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(2, 4, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(4, 6, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(0, 2, (20,))))] + train = [AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(0, 2, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(2, 4, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(4, 6, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(0, 2, (20,))))] # create list of test datasets - test = [AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(0, 2, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(2, 4, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(4, 6, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20,10), torch.randint(0, 2, (20,))))] + test = [AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(0, 2, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(2, 4, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(4, 6, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(0, 2, (20,))))] # concatenate datasets - final_train, final_test, classes = concat_datasets_sequentially(train, test) + final_train, _, classes = concat_datasets_sequentially(train, test) # merge all classes into a single list classes_all = [] @@ -1353,6 +1360,7 @@ def test_avalanche_concat_datasets_sequentially(self): # test for correctness self.assertEqual(classes_all, target_classes) + class TransformationSubsetTests(unittest.TestCase): def test_avalanche_subset_transform(self): dataset_mnist = MNIST(root=default_dataset_location('mnist'), From 2164acf62115549d62ea0b7eccb1cd8c0950de5d Mon Sep 17 00:00:00 2001 From: Andrea Cossu Date: Mon, 3 Jan 2022 10:44:09 +0100 Subject: [PATCH 56/60] Force added ctrl-benchmark dependency --- environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment.yml b/environment.yml index 71e01d05e..81d8143a5 100644 --- a/environment.yml +++ b/environment.yml @@ -26,4 +26,5 @@ dependencies: - pip: - pytorchcv - gdown + - ctrl-benchmark - gym From bf5776f77702375338ee98195b0e43442e86ca1b Mon Sep 17 00:00:00 2001 From: Andrea Cossu Date: Mon, 3 Jan 2022 11:13:34 +0100 Subject: [PATCH 57/60] Force add ctrl-benchmark as dependency --- environment-dev.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment-dev.yml b/environment-dev.yml index 5fc4786b3..59b09edf2 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -31,4 +31,5 @@ dependencies: - pip: - pytorchcv - gdown + - ctrl-benchmark - gym From 743d232213073ba76598e76c5f12acf963dc0601 Mon Sep 17 00:00:00 2001 From: Andrea Cossu Date: Mon, 3 Jan 2022 11:14:44 +0100 Subject: [PATCH 58/60] Added ctrl-benchmark as dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 3850c0f6d..a7b4f120c 100644 --- a/setup.py +++ b/setup.py @@ -51,6 +51,7 @@ def get_version(rel_path): 'torch', 'torchvision', 'gdown', + 'ctrl-benchmark', 'setuptools<=59.5.0' ] ) From 93fb791ec5d69e877327544f62d03c2b4a17b2ef Mon Sep 17 00:00:00 2001 From: AndreaCossu Date: Mon, 3 Jan 2022 14:09:16 +0100 Subject: [PATCH 59/60] Fixed pep --- tests/test_avalanche_dataset.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/test_avalanche_dataset.py b/tests/test_avalanche_dataset.py index 0ba14a0fd..f5e0f2da5 100644 --- a/tests/test_avalanche_dataset.py +++ b/tests/test_avalanche_dataset.py @@ -1327,24 +1327,24 @@ def test_avalanche_avalanche_subset_concat_stack_overflow(self): def test_avalanche_concat_datasets_sequentially(self): # create list of training datasets - train = [AvalancheDataset(TensorDataset(torch.randn(20, 10), - torch.randint(0, 2, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20, 10), - torch.randint(2, 4, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20, 10), - torch.randint(4, 6, (20,)))), - AvalancheDataset(TensorDataset(torch.randn(20, 10), + train = [AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(0, 2, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(2, 4, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), + torch.randint(4, 6, (20,)))), + AvalancheDataset(TensorDataset(torch.randn(20, 10), torch.randint(0, 2, (20,))))] # create list of test datasets test = [AvalancheDataset(TensorDataset(torch.randn(20, 10), - torch.randint(0, 2, (20,)))), + torch.randint(0, 2, (20,)))), AvalancheDataset(TensorDataset(torch.randn(20, 10), - torch.randint(2, 4, (20,)))), + torch.randint(2, 4, (20,)))), AvalancheDataset(TensorDataset(torch.randn(20, 10), - torch.randint(4, 6, (20,)))), + torch.randint(4, 6, (20,)))), AvalancheDataset(TensorDataset(torch.randn(20, 10), - torch.randint(0, 2, (20,))))] + torch.randint(0, 2, (20,))))] # concatenate datasets final_train, _, classes = concat_datasets_sequentially(train, test) From 19a5ffd50161ae365329816c40976533cb3c332d Mon Sep 17 00:00:00 2001 From: Antonio Carta Date: Fri, 7 Jan 2022 13:52:29 +0100 Subject: [PATCH 60/60] update tests and target metrics --- tests/target_metrics/mt.pickle | Bin 24628 -> 24628 bytes tests/target_metrics/sit.pickle | Bin 27680 -> 27687 bytes tests/target_metrics/tpp.pickle | Bin 40160 -> 63910 bytes tests/test_metrics.py | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/target_metrics/mt.pickle b/tests/target_metrics/mt.pickle index 162ae207dc3c9a46989cf28ada2ea747d770e848..53f02c071334f13eae7615ddd0c7ff0377b2cdb8 100644 GIT binary patch literal 24628 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 Xo8`?&JAC)xz+~~0uffxk6CC~r)^^bQ literal 24628 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 Tbog%mz8dk9ul`dtF&+O8|I-sf diff --git a/tests/target_metrics/sit.pickle b/tests/target_metrics/sit.pickle index 2167514a9db07315b142c256db4be33f7682a9d1..9c13709c3ad6deff64d78874320e142480305df7 100644 GIT binary patch literal 27687 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 diff --git a/tests/target_metrics/tpp.pickle b/tests/target_metrics/tpp.pickle index 89df94540e49ecef0733331b89b25c048fb7d72c..b02386526b277d9c50df07ce2f33b10d6794e31d 100644 GIT binary patch literal 63910 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 literal 40160 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 diff --git a/tests/test_metrics.py b/tests/test_metrics.py index fc9fd22c6..a560983d0 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -542,7 +542,7 @@ def setUpClass(cls) -> None: collect_all=True) # collect all metrics (set to True by default) cl_strategy = BaseStrategy( model, SGD(model.parameters(), lr=0.001, momentum=0.9), - CrossEntropyLoss(), train_mb_size=2, train_epochs=2, + CrossEntropyLoss(), train_mb_size=4, train_epochs=2, eval_mb_size=2, device=DEVICE, evaluator=eval_plugin, eval_every=1) for i, experience in enumerate(benchmark.train_stream):