Repository: Continvvm/continuum (language: python)

Issue:
CIFAR10 Default transformation can not be changed with args
```
from continuum.datasets import CIFAR10
transforms=Compose([transforms.ToTensor(),
            transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))]
dataset_train = CIFAR10(data_path=data_path, transform=transforms, train=True)
```
Does not change the default transformation


Discussion / hints:

Fix patch:
diff --git a/continuum/datasets/__init__.py b/continuum/datasets/__init__.py
index 92ede3c5..a88068bd 100644
--- a/continuum/datasets/__init__.py
+++ b/continuum/datasets/__init__.py
@@ -3,13 +3,14 @@
 from continuum.datasets.base import (
     ImageFolderDataset, InMemoryDataset, PyTorchDataset, _ContinuumDataset
 )
+from continuum.datasets.cifar100 import CIFAR100
 from continuum.datasets.core50 import (Core50, Core50v2_79, Core50v2_196, Core50v2_391)
 from continuum.datasets.fellowship import (CIFARFellowship, Fellowship, MNISTFellowship)
 from continuum.datasets.imagenet import ImageNet100, ImageNet1000, TinyImageNet200
 from continuum.datasets.synbols import Synbols
 from continuum.datasets.nlp import MultiNLI
 from continuum.datasets.pytorch import (
-    CIFAR10, CIFAR100, EMNIST, KMNIST, MNIST, QMNIST, FashionMNIST
+    CIFAR10, EMNIST, KMNIST, MNIST, QMNIST, FashionMNIST
 )
 from continuum.datasets.svhn import SVHN
 from continuum.datasets.cub200 import CUB200
diff --git a/continuum/datasets/cifar100.py b/continuum/datasets/cifar100.py
new file mode 100644
index 00000000..fbc44ae8
--- /dev/null
+++ b/continuum/datasets/cifar100.py
@@ -0,0 +1,94 @@
+from typing import Tuple
+
+import numpy as np
+from torchvision import datasets as torchdata
+from torchvision import transforms
+
+from continuum.datasets import PyTorchDataset
+
+# update labels
+cifar100_coarse_labels = np.array([4, 1, 14, 8, 0, 6, 7, 7, 18, 3,
+                                   3, 14, 9, 18, 7, 11, 3, 9, 7, 11,
+                                   6, 11, 5, 10, 7, 6, 13, 15, 3, 15,
+                                   0, 11, 1, 10, 12, 14, 16, 9, 11, 5,
+                                   5, 19, 8, 8, 15, 13, 14, 17, 18, 10,
+                                   16, 4, 17, 4, 2, 0, 17, 4, 18, 17,
+                                   10, 3, 2, 12, 12, 16, 12, 1, 9, 19,
+                                   2, 10, 0, 1, 16, 12, 9, 13, 15, 13,
+                                   16, 19, 2, 4, 6, 19, 5, 5, 8, 19,
+                                   18, 1, 2, 15, 6, 0, 17, 8, 14, 13])
+
+
+class CIFAR100(PyTorchDataset):
+    """Continuum use of the CIFAR100 dataset.
+    We adapt this dataset to create scenario from the combination of both of category labels and class labels.
+
+    References:
+        * "Learning Multiple Layers of Features from Tiny Images"
+          Alex Krizhevsky
+
+    :param data_path: The folder path containing the data.
+    :param download: An option useless in this case.
+    :param labels_type: labels type define if we use class labels or category labels for each data point.
+    :param task_labels: labels type define what type of labels we use if we want to create a task id vector.
+    """
+
+    def __init__(self, *args, labels_type: str = "class", task_labels: str = None, **kwargs):
+        super().__init__(*args, dataset_type=torchdata.cifar.CIFAR100, **kwargs)
+        if not labels_type in ["class", "category"]:
+            AssertionError("unknown labels_type parameter, choose among ['class', 'category']")
+        if not  task_labels in [None, "class", "category"]:
+            AssertionError("unknown task_labels parameter, choose among [None, 'class', 'category']")
+
+        self.labels_type = labels_type
+        self.task_labels_type = task_labels
+
+        if self.task_labels_type is None:
+            # the dataset does not provide a task id vector
+            self.t = None
+        elif self.task_labels_type == "class":
+            # the dataset provides a task id vector based on classes
+            self.t = self.dataset.targets
+        elif self.task_labels_type == "category":
+            # the dataset provides a task id vector based on categories
+            self.t = cifar100_coarse_labels[self.dataset.targets]
+
+        if self.labels_type == "category":
+            # here we replace class labels by category labels to annotate data.
+
+            # Classes labels from 0-19 (instead of 0 to 99)
+            # cf : "superclasses" or "coarse labels" https://www.cs.toronto.edu/~kriz/cifar.html
+
+            # code from https://github.com/ryanchankh/cifar100coarse/blob/master/cifar100coarse.py
+            self.dataset.targets = cifar100_coarse_labels[self.dataset.targets]
+
+            # update classes
+            self.dataset.classes = [['beaver', 'dolphin', 'otter', 'seal', 'whale'],
+                                    ['aquarium_fish', 'flatfish', 'ray', 'shark', 'trout'],
+                                    ['orchid', 'poppy', 'rose', 'sunflower', 'tulip'],
+                                    ['bottle', 'bowl', 'can', 'cup', 'plate'],
+                                    ['apple', 'mushroom', 'orange', 'pear', 'sweet_pepper'],
+                                    ['clock', 'keyboard', 'lamp', 'telephone', 'television'],
+                                    ['bed', 'chair', 'couch', 'table', 'wardrobe'],
+                                    ['bee', 'beetle', 'butterfly', 'caterpillar', 'cockroach'],
+                                    ['bear', 'leopard', 'lion', 'tiger', 'wolf'],
+                                    ['bridge', 'castle', 'house', 'road', 'skyscraper'],
+                                    ['cloud', 'forest', 'mountain', 'plain', 'sea'],
+                                    ['camel', 'cattle', 'chimpanzee', 'elephant', 'kangaroo'],
+                                    ['fox', 'porcupine', 'possum', 'raccoon', 'skunk'],
+                                    ['crab', 'lobster', 'snail', 'spider', 'worm'],
+                                    ['baby', 'boy', 'girl', 'man', 'woman'],
+                                    ['crocodile', 'dinosaur', 'lizard', 'snake', 'turtle'],
+                                    ['hamster', 'mouse', 'rabbit', 'shrew', 'squirrel'],
+                                    ['maple_tree', 'oak_tree', 'palm_tree', 'pine_tree', 'willow_tree'],
+                                    ['bicycle', 'bus', 'motorcycle', 'pickup_truck', 'train'],
+                                    ['lawn_mower', 'rocket', 'streetcar', 'tank', 'tractor']]
+
+    def get_data(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
+        x, y, _ = super().get_data()
+        return x, y, self.t
+
+    @property
+    def transformations(self):
+        return [transforms.ToTensor(),
+                transforms.Normalize((0.5071, 0.4867, 0.4408), (0.2675, 0.2565, 0.2761))]
diff --git a/continuum/datasets/fellowship.py b/continuum/datasets/fellowship.py
index 8a23c45b..75950f04 100644
--- a/continuum/datasets/fellowship.py
+++ b/continuum/datasets/fellowship.py
@@ -3,7 +3,8 @@
 import numpy as np
 
 from continuum.datasets.base import _ContinuumDataset
-from continuum.datasets.pytorch import (CIFAR10, CIFAR100, KMNIST, MNIST, FashionMNIST)
+from continuum.datasets.pytorch import (CIFAR10, KMNIST, MNIST, FashionMNIST)
+from continuum.datasets.cifar100 import CIFAR100
 
 
 class Fellowship(_ContinuumDataset):
diff --git a/continuum/datasets/pytorch.py b/continuum/datasets/pytorch.py
index 0484dada..a6c20708 100644
--- a/continuum/datasets/pytorch.py
+++ b/continuum/datasets/pytorch.py
@@ -11,23 +11,8 @@ def __init__(self, *args, **kwargs):
 
     @property
     def transformations(self):
-        return [
-            transforms.ToTensor(),
-            transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
-        ]
-
-
-class CIFAR100(PyTorchDataset):
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, dataset_type=torchdata.cifar.CIFAR100, **kwargs)
-
-    @property
-    def transformations(self):
-        return [
-            transforms.ToTensor(),
-            transforms.Normalize((0.5071, 0.4867, 0.4408), (0.2675, 0.2565, 0.2761))
-        ]
+        return [transforms.ToTensor(),
+                transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]
 
 
 class MNIST(PyTorchDataset):
diff --git a/docs/_tutorials/scenarios_suites/CIFAR100.rst b/docs/_tutorials/scenarios_suites/CIFAR100.rst
new file mode 100644
index 00000000..6c559348
--- /dev/null
+++ b/docs/_tutorials/scenarios_suites/CIFAR100.rst
@@ -0,0 +1,55 @@
+CORe50
+-----------------
+
+CIFAR100 is a famous dataset proposed in
+`"Learning Multiple Layers of Features from Tiny Images (pdf)" <https://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf>`__.
+This dataset is mainly used with its 100 class labels type. However, it exists also 20 super classes (coarse labels or category labels).
+In continuum, we propose to benefit from both to create various types of scenarios.
+
+Continuum Scenarios
+##########
+
+- Class Incremental
+""""""""
+
+We can create a simple class incremental setting with the default parameters, i.e. 100 classes.
+In this scenario coarse labels are not used.
+
+.. code-block:: python
+
+    from continuum.datasets import CIFAR100
+
+    dataset = CIFAR100("/your/path", train=True)
+    # 5 tasks with 20 classes each
+    scenario = ClassIncremental(dataset, nb_tasks=5)
+
+
+Or a `ClassIncremental` with coarse labels (category labels).
+In this scenario classical labels are not used.
+
+.. code-block:: python
+
+    from continuum.datasets import CIFAR100
+
+    dataset = CIFAR100("/your/path", train=True, labels_type="category")
+    # 5 tasks with 20 classes each
+    scenario = ClassIncremental(dataset, nb_tasks=5)
+
+
+
+
+- Classes and Instances Incremental
+""""""""
+
+In Class and Instances Incremental scenario, the labels are set by category of object but new tasks bring new object.
+Hence, new task either bring a new object from a known category or a new object from an unknown category.
+
+.. code-block:: python
+
+    from continuum.datasets import CIFAR100
+    # task_labels parameter makes possible to create a task id vector from either classes or categories.
+    dataset = CIFAR100("/your/path", train=True), task_labels="class", labels_type="category"
+    # 100 tasks with 1 object each among the 20 categories of coarse labels
+    # classes are object ids (20 classes then), new tasks might contains new label or known label
+    scenario = ContinualScenario(dataset)
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..3c4d6568
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+per-file-ignores = __init__.py:F401
\ No newline at end of file


Test patch:
diff --git a/tests/test_CIFAR100.py b/tests/test_CIFAR100.py
new file mode 100644
index 00000000..4c0a93c3
--- /dev/null
+++ b/tests/test_CIFAR100.py
@@ -0,0 +1,56 @@
+import os
+
+import pytest
+from torchvision.transforms import Resize, ToTensor
+
+from continuum.datasets import CIFAR100
+from continuum.scenarios import ClassIncremental, ContinualScenario
+
+DATA_PATH = os.environ.get("CONTINUUM_DATA_PATH")
+
+'''
+Basic test of CIFAR100 without parameters
+'''
+@pytest.mark.slow
+def test_scenario_CIFAR100_ClassIncremental():
+    dataset = CIFAR100(DATA_PATH, train=True)
+    scenario = ClassIncremental(dataset, increment=50)
+
+    assert scenario.nb_classes == 100
+    assert scenario.nb_tasks == 2
+
+'''
+Basic test of CIFAR100 without parameters
+'''
+@pytest.mark.slow
+def test_scenario_CIFAR100_CoarseLabels():
+    dataset = CIFAR100(DATA_PATH, train=True, labels_type="category")
+    scenario = ClassIncremental(dataset, increment=10)
+
+    assert scenario.nb_classes == 20
+    assert scenario.nb_tasks == 2
+
+'''
+Basic test of CIFAR100 without parameters
+'''
+@pytest.mark.slow
+def test_scenario_CIFAR100_Scenarios():
+    dataset = CIFAR100(DATA_PATH, train=True, labels_type="category", task_labels="category")
+    scenario = ContinualScenario(dataset)
+    assert scenario.nb_classes == 20
+    assert scenario.nb_tasks == 20
+
+    dataset = CIFAR100(DATA_PATH, train=True, labels_type="category", task_labels="class")
+    scenario = ContinualScenario(dataset)
+    assert scenario.nb_classes == 20
+    assert scenario.nb_tasks == 100
+
+    dataset = CIFAR100(DATA_PATH, train=True, labels_type="class", task_labels="class")
+    scenario = ContinualScenario(dataset)
+    assert scenario.nb_classes == 100
+    assert scenario.nb_tasks == 100
+
+    dataset = CIFAR100(DATA_PATH, train=True, labels_type="class", task_labels="category")
+    scenario = ContinualScenario(dataset)
+    assert scenario.nb_classes == 100
+    assert scenario.nb_tasks == 20
diff --git a/tests/test_CUB200.py b/tests/test_CUB200.py
index 7af38d58..8a5d947a 100644
--- a/tests/test_CUB200.py
+++ b/tests/test_CUB200.py
@@ -21,5 +21,5 @@ def test_scenario_CUB200_ClassIncremental():
     for task_id, task_set in enumerate(scenario):
         print(f"Task {task_id} : {task_set.nb_classes} classes")
         task_set.plot(path="Archives/Samples/CUB200/CI",
-                      title="CUB200_InstanceIncremental_{}.jpg".format(task_id),
+                      title="CUB200_ClassIncremental_{}.jpg".format(task_id),
                       nb_samples=100)